mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-20 03:24:50 +00:00
improved vertical video detection and auto full screen for vertical videos
This commit is contained in:
parent
1564433e02
commit
cc0c400b28
2 changed files with 126 additions and 52 deletions
|
@ -96,7 +96,7 @@ class VideoDetailFragment : MainFragment {
|
|||
|
||||
detectWindowSize()
|
||||
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: true
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false
|
||||
|
||||
if (
|
||||
isSmallWindow
|
||||
|
@ -117,13 +117,29 @@ class VideoDetailFragment : MainFragment {
|
|||
}
|
||||
|
||||
private fun onStateChanged(state: State) {
|
||||
if (isSmallWindow && state == State.MAXIMIZED && !isFullscreen && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (
|
||||
isSmallWindow
|
||||
&& state == State.MAXIMIZED
|
||||
&& !isFullscreen
|
||||
&& resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
) {
|
||||
_viewDetail?.setFullscreen(true)
|
||||
}
|
||||
|
||||
updateOrientation()
|
||||
}
|
||||
|
||||
private fun onVideoChanged(videoWidth : Int, videoHeight: Int) {
|
||||
if (
|
||||
isSmallWindow
|
||||
&& state == State.MAXIMIZED
|
||||
&& !isFullscreen
|
||||
&& videoHeight > videoWidth
|
||||
) {
|
||||
_viewDetail?.setFullscreen(true)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
fun updateOrientation() {
|
||||
val a = activity ?: return
|
||||
|
@ -133,7 +149,7 @@ class VideoDetailFragment : MainFragment {
|
|||
val isReversePortraitAllowed = Settings.instance.playback.reversePortrait
|
||||
val rotationLock = StatePlayer.instance.rotationLock
|
||||
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: true
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false
|
||||
|
||||
// For small windows if the device isn't landscape right now and full screen portrait isn't allowed then we should force landscape
|
||||
if (isSmallWindow && isFullscreen && !isFullScreenPortraitAllowed && resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT && !rotationLock && isLandscapeVideo) {
|
||||
|
@ -247,6 +263,7 @@ class VideoDetailFragment : MainFragment {
|
|||
_viewDetail = _view!!.findViewById<VideoDetailView>(R.id.fragview_videodetail).also {
|
||||
it.applyFragment(this);
|
||||
it.onFullscreenChanged.subscribe(::onFullscreenChanged);
|
||||
it.onVideoChanged.subscribe(::onVideoChanged)
|
||||
it.onMinimize.subscribe {
|
||||
isMinimizing = true
|
||||
_view!!.transitionToStart();
|
||||
|
|
|
@ -80,6 +80,7 @@ import com.futo.platformplayer.casting.CastConnectionState
|
|||
import com.futo.platformplayer.casting.StateCasting
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.Event2
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.downloads.VideoLocal
|
||||
import com.futo.platformplayer.dp
|
||||
|
@ -300,6 +301,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
val onFullscreenChanged = Event1<Boolean>();
|
||||
val onEnterPictureInPicture = Event0();
|
||||
val onPlayChanged = Event1<Boolean>();
|
||||
val onVideoChanged = Event2<Int, Int>()
|
||||
|
||||
var allowBackground : Boolean = false
|
||||
private set;
|
||||
|
@ -1205,6 +1207,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
switchContentView(_container_content_main);
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) {
|
||||
Logger.i(TAG, "setVideoDetails (${videoDetail.name})")
|
||||
|
@ -1213,7 +1216,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
_autoplayVideo = null
|
||||
Logger.i(TAG, "Autoplay video cleared (setVideoDetails)")
|
||||
|
||||
if(newVideo && this.video?.url == videoDetail.url)
|
||||
if (newVideo && this.video?.url == videoDetail.url)
|
||||
return;
|
||||
|
||||
if (newVideo) {
|
||||
|
@ -1222,8 +1225,13 @@ class VideoDetailView : ConstraintLayout {
|
|||
_lastSubtitleSource = null;
|
||||
}
|
||||
|
||||
if(videoDetail.datetime != null && videoDetail.datetime!! > OffsetDateTime.now())
|
||||
UIDialogs.toast(context, context.getString(R.string.planned_in) + " ${videoDetail.datetime?.toHumanNowDiffString(true)}")
|
||||
if (videoDetail.datetime != null && videoDetail.datetime!! > OffsetDateTime.now())
|
||||
UIDialogs.toast(
|
||||
context,
|
||||
context.getString(R.string.planned_in) + " ${
|
||||
videoDetail.datetime?.toHumanNowDiffString(true)
|
||||
}"
|
||||
)
|
||||
|
||||
if (!videoDetail.isLive) {
|
||||
_player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed());
|
||||
|
@ -1232,26 +1240,25 @@ class VideoDetailView : ConstraintLayout {
|
|||
val videoLocal: VideoLocal?;
|
||||
val video: IPlatformVideoDetails?;
|
||||
|
||||
if(videoDetail is VideoLocal) {
|
||||
if (videoDetail is VideoLocal) {
|
||||
videoLocal = videoDetail;
|
||||
video = videoDetail;
|
||||
this.video = video;
|
||||
val videoTask = StatePlatform.instance.getContentDetails(videoDetail.url);
|
||||
videoTask.invokeOnCompletion { ex ->
|
||||
if(ex != null) {
|
||||
if (ex != null) {
|
||||
Logger.e(TAG, "Failed to fetch live video for offline video", ex);
|
||||
return@invokeOnCompletion;
|
||||
}
|
||||
val result = videoTask.getCompleted();
|
||||
if(this.video == videoDetail && result is IPlatformVideoDetails) {
|
||||
if (this.video == videoDetail && result is IPlatformVideoDetails) {
|
||||
this.video = result;
|
||||
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||
updateQualitySourcesOverlay(result, videoLocal);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else { //TODO: Update cached video if it exists with video
|
||||
} else { //TODO: Update cached video if it exists with video
|
||||
videoLocal = StateDownloads.instance.getCachedVideo(videoDetail.id);
|
||||
video = videoDetail;
|
||||
}
|
||||
|
@ -1259,7 +1266,9 @@ class VideoDetailView : ConstraintLayout {
|
|||
this.video = video;
|
||||
cleanupPlaybackTracker();
|
||||
|
||||
if(video is JSVideoDetails) {
|
||||
onVideoChanged.emit(video.video.videoSources[0].width, video.video.videoSources[0].height)
|
||||
|
||||
if (video is JSVideoDetails) {
|
||||
val me = this;
|
||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
|
@ -1267,8 +1276,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
val chapters = null ?: StatePlatform.instance.getContentChapters(video.url);
|
||||
_player.setChapters(chapters);
|
||||
_cast.setChapters(chapters);
|
||||
}
|
||||
catch(ex: Throwable) {
|
||||
} catch (ex: Throwable) {
|
||||
Logger.e(TAG, "Failed to get chapters", ex);
|
||||
_player.setChapters(null);
|
||||
_cast.setChapters(null);
|
||||
|
@ -1278,7 +1286,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
}*/
|
||||
}
|
||||
try {
|
||||
if(!StateApp.instance.privateMode) {
|
||||
if (!StateApp.instance.privateMode) {
|
||||
val stopwatch = com.futo.platformplayer.debug.Stopwatch()
|
||||
var tracker = video.getPlaybackTracker()
|
||||
Logger.i(TAG, "video.getPlaybackTracker took ${stopwatch.elapsedMs}ms")
|
||||
|
@ -1294,17 +1302,22 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
if (me.video == video)
|
||||
me._playbackTracker = tracker;
|
||||
}
|
||||
else if(me.video == video)
|
||||
} else if (me.video == video)
|
||||
me._playbackTracker = null;
|
||||
}
|
||||
catch(ex: Throwable) {
|
||||
} catch (ex: Throwable) {
|
||||
Logger.e(TAG, "Playback tracker failed", ex);
|
||||
if(me.video?.isLive == true) withContext(Dispatchers.Main) {
|
||||
UIDialogs.toast(context, context.getString(R.string.failed_to_get_playback_tracker));
|
||||
if (me.video?.isLive == true) withContext(Dispatchers.Main) {
|
||||
UIDialogs.toast(
|
||||
context,
|
||||
context.getString(R.string.failed_to_get_playback_tracker)
|
||||
);
|
||||
};
|
||||
else withContext(Dispatchers.Main) {
|
||||
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_get_playback_tracker), ex);
|
||||
UIDialogs.showGeneralErrorDialog(
|
||||
context,
|
||||
context.getString(R.string.failed_to_get_playback_tracker),
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1321,8 +1334,11 @@ class VideoDetailView : ConstraintLayout {
|
|||
if (Settings.instance.comments.recommendationsDefault && !Settings.instance.comments.hideRecommendations) {
|
||||
setTabIndex(2, true)
|
||||
} else {
|
||||
when(Settings.instance.comments.defaultCommentSection) {
|
||||
0 -> if(Settings.instance.other.polycentricEnabled) setTabIndex(0, true) else setTabIndex(1, true);
|
||||
when (Settings.instance.comments.defaultCommentSection) {
|
||||
0 -> if (Settings.instance.other.polycentricEnabled) setTabIndex(
|
||||
0,
|
||||
true
|
||||
) else setTabIndex(1, true);
|
||||
1 -> setTabIndex(1, true);
|
||||
2 -> setTabIndex(StateMeta.instance.getLastCommentSection(), true)
|
||||
}
|
||||
|
@ -1332,9 +1348,16 @@ class VideoDetailView : ConstraintLayout {
|
|||
//UI
|
||||
_title.text = video.name;
|
||||
_channelName.text = video.author.name;
|
||||
if(video.author.subscribers != null) {
|
||||
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else "";
|
||||
(_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_5 * -1).toInt(), 0, 0);
|
||||
if (video.author.subscribers != null) {
|
||||
_channelMeta.text = if ((video.author.subscribers
|
||||
?: 0) > 0
|
||||
) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else "";
|
||||
(_channelName.layoutParams as MarginLayoutParams).setMargins(
|
||||
0,
|
||||
(DP_5 * -1).toInt(),
|
||||
0,
|
||||
0
|
||||
);
|
||||
} else {
|
||||
_channelMeta.text = "";
|
||||
(_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_2).toInt(), 0, 0);
|
||||
|
@ -1342,7 +1365,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
|
||||
video.author.let {
|
||||
if(it is PlatformAuthorMembershipLink && !it.membershipUrl.isNullOrEmpty())
|
||||
if (it is PlatformAuthorMembershipLink && !it.membershipUrl.isNullOrEmpty())
|
||||
_monetization.setPlatformMembership(video.id.pluginId, it.membershipUrl);
|
||||
else
|
||||
_monetization.setPlatformMembership(null, null);
|
||||
|
@ -1356,7 +1379,8 @@ class VideoDetailView : ConstraintLayout {
|
|||
_creatorThumbnail.setThumbnail(video.author.thumbnail, false);
|
||||
|
||||
|
||||
val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(video.author.url, true);
|
||||
val cachedPolycentricProfile =
|
||||
PolycentricCache.instance.getCachedProfile(video.author.url, true);
|
||||
if (cachedPolycentricProfile != null) {
|
||||
setPolycentricProfile(cachedPolycentricProfile, animate = false);
|
||||
} else {
|
||||
|
@ -1365,13 +1389,19 @@ class VideoDetailView : ConstraintLayout {
|
|||
}
|
||||
|
||||
_platform.setPlatformFromClientID(video.id.pluginId);
|
||||
val subTitleSegments : ArrayList<String> = ArrayList();
|
||||
if(video.viewCount > 0)
|
||||
subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) context.getString(R.string.watching_now) else context.getString(R.string.views)}");
|
||||
if(video.datetime != null) {
|
||||
val subTitleSegments: ArrayList<String> = ArrayList();
|
||||
if (video.viewCount > 0)
|
||||
subTitleSegments.add(
|
||||
"${video.viewCount.toHumanNumber()} ${
|
||||
if (video.isLive) context.getString(
|
||||
R.string.watching_now
|
||||
) else context.getString(R.string.views)
|
||||
}"
|
||||
);
|
||||
if (video.datetime != null) {
|
||||
val diff = video.datetime?.getNowDiffSeconds() ?: 0;
|
||||
val ago = video.datetime?.toHumanNowDiffString(true)
|
||||
if(diff >= 0)
|
||||
if (diff >= 0)
|
||||
subTitleSegments.add("${ago} ago");
|
||||
else
|
||||
subTitleSegments.add("available in ${ago}");
|
||||
|
@ -1384,20 +1414,27 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val queryReferencesResponse = ApiMethods.getQueryReferences(PolycentricCache.SERVER, ref, null,null,
|
||||
val queryReferencesResponse = ApiMethods.getQueryReferences(
|
||||
PolycentricCache.SERVER, ref, null, null,
|
||||
arrayListOf(
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder().setFromType(ContentType.OPINION.value).setValue(
|
||||
ByteString.copyFrom(Opinion.like.data)).build(),
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder().setFromType(ContentType.OPINION.value).setValue(
|
||||
ByteString.copyFrom(Opinion.dislike.data)).build()
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder()
|
||||
.setFromType(ContentType.OPINION.value).setValue(
|
||||
ByteString.copyFrom(Opinion.like.data)
|
||||
).build(),
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder()
|
||||
.setFromType(ContentType.OPINION.value).setValue(
|
||||
ByteString.copyFrom(Opinion.dislike.data)
|
||||
).build()
|
||||
),
|
||||
extraByteReferences = listOfNotNull(extraBytesRef)
|
||||
);
|
||||
|
||||
val likes = queryReferencesResponse.countsList[0];
|
||||
val dislikes = queryReferencesResponse.countsList[1];
|
||||
val hasLiked = StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/;
|
||||
val hasDisliked = StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/;
|
||||
val hasLiked =
|
||||
StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/;
|
||||
val hasDisliked =
|
||||
StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/;
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_rating.visibility = View.VISIBLE;
|
||||
|
@ -1421,7 +1458,11 @@ class VideoDetailView : ConstraintLayout {
|
|||
}
|
||||
}
|
||||
|
||||
StatePolycentric.instance.updateLikeMap(ref, args.hasLiked, args.hasDisliked)
|
||||
StatePolycentric.instance.updateLikeMap(
|
||||
ref,
|
||||
args.hasLiked,
|
||||
args.hasDisliked
|
||||
)
|
||||
};
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
@ -1443,6 +1484,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
_textDislikes.visibility = View.VISIBLE;
|
||||
_textDislikes.text = r.dislikes.toHumanNumber();
|
||||
}
|
||||
|
||||
is RatingLikes -> {
|
||||
val r = video.rating as RatingLikes;
|
||||
_layoutRating.visibility = View.VISIBLE;
|
||||
|
@ -1454,6 +1496,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
_imageDislikeIcon.visibility = View.GONE;
|
||||
_textDislikes.visibility = View.GONE;
|
||||
}
|
||||
|
||||
else -> {
|
||||
_layoutRating.visibility = View.GONE;
|
||||
}
|
||||
|
@ -1465,6 +1508,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
setLoading(false);
|
||||
|
||||
|
||||
//Set Mediasource
|
||||
|
||||
val toResume = _videoResumePositionMilliseconds;
|
||||
|
@ -1481,9 +1525,22 @@ class VideoDetailView : ConstraintLayout {
|
|||
val historyItem = getHistoryIndex(videoDetail) ?: return@launch;
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_historicalPosition = StateHistory.instance.updateHistoryPosition(video, historyItem,false, (toResume.toFloat() / 1000.0f).toLong(), null, true);
|
||||
Logger.i(TAG, "Historical position: $_historicalPosition, last position: $lastPositionMilliseconds");
|
||||
if (_historicalPosition > 60 && video.duration - _historicalPosition > 5 && Math.abs(_historicalPosition - lastPositionMilliseconds / 1000) > 5.0) {
|
||||
_historicalPosition = StateHistory.instance.updateHistoryPosition(
|
||||
video,
|
||||
historyItem,
|
||||
false,
|
||||
(toResume.toFloat() / 1000.0f).toLong(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
Logger.i(
|
||||
TAG,
|
||||
"Historical position: $_historicalPosition, last position: $lastPositionMilliseconds"
|
||||
);
|
||||
if (_historicalPosition > 60 && video.duration - _historicalPosition > 5 && Math.abs(
|
||||
_historicalPosition - lastPositionMilliseconds / 1000
|
||||
) > 5.0
|
||||
) {
|
||||
_layoutResume.visibility = View.VISIBLE;
|
||||
_textResume.text = "Resume at ${_historicalPosition.toHumanTime(false)}";
|
||||
|
||||
|
@ -1509,10 +1566,10 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
_liveChat?.stop();
|
||||
_liveChat = null;
|
||||
if(video.isLive && video.live != null) {
|
||||
if (video.isLive && video.live != null) {
|
||||
loadLiveChat(video);
|
||||
}
|
||||
if(video.isLive && video.live == null && !video.video.videoSources.any())
|
||||
if (video.isLive && video.live == null && !video.video.videoSources.any())
|
||||
startLiveTry(video);
|
||||
|
||||
|
||||
|
@ -2241,14 +2298,14 @@ class VideoDetailView : ConstraintLayout {
|
|||
}
|
||||
}
|
||||
|
||||
fun isLandscapeVideo(): Boolean {
|
||||
fun isLandscapeVideo(): Boolean? {
|
||||
var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width
|
||||
var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height
|
||||
|
||||
return if (videoSourceHeight != null && videoSourceWidth != null) {
|
||||
videoSourceWidth > videoSourceHeight
|
||||
} else {
|
||||
true
|
||||
return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){
|
||||
null
|
||||
} else{
|
||||
videoSourceWidth >= videoSourceHeight
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue