improved vertical video detection and auto full screen for vertical videos

This commit is contained in:
Kai 2024-10-22 13:01:02 -05:00
parent 1564433e02
commit cc0c400b28
No known key found for this signature in database
2 changed files with 126 additions and 52 deletions

View file

@ -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();

View file

@ -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
}
}