fix device getting stuck landscape or in portrait when entering and exiting full screen via the button

fix weird rotation behavior when locking and unlocking rotation via the player button
This commit is contained in:
Kai 2024-12-12 15:25:02 -06:00
parent b20b625820
commit c88d457021
No known key found for this signature in database
3 changed files with 145 additions and 14 deletions

View File

@ -415,7 +415,7 @@ class Settings : FragmentedStorageFileJson() {
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4) @FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4)
var simplifySources: Boolean = true; var simplifySources: Boolean = true;
@FormField(R.string.force_allow_full_screen_rotation, FieldForm.TOGGLE, R.string.force_allow_full_screen_rotation_description, 5) @FormField(R.string.force_enable_auto_rotate_in_full_screen, FieldForm.TOGGLE, R.string.force_enable_auto_rotate_in_full_screen_description, 5)
var forceAllowFullScreenRotation: Boolean = false var forceAllowFullScreenRotation: Boolean = false
@FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 6) @FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 6)

View File

@ -1,11 +1,12 @@
package com.futo.platformplayer.fragment.mainactivity.main package com.futo.platformplayer.fragment.mainactivity.main
import android.annotation.SuppressLint import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.OrientationEventListener
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets import android.view.WindowInsets
@ -28,10 +29,15 @@ import com.futo.platformplayer.models.PlatformVideoWithTime
import com.futo.platformplayer.models.UrlVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime
import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.math.min import kotlin.math.min
//region Fragment
@UnstableApi @UnstableApi
class VideoDetailFragment : MainFragment { class VideoDetailFragment() : MainFragment() {
override val isMainView: Boolean = false; override val isMainView: Boolean = false;
override val hasBottomBar: Boolean = true; override val hasBottomBar: Boolean = true;
override val isOverlay: Boolean = true; override val isOverlay: Boolean = true;
@ -76,8 +82,10 @@ class VideoDetailFragment : MainFragment {
private var _loadUrlOnCreate: UrlVideoWithTime? = null; private var _loadUrlOnCreate: UrlVideoWithTime? = null;
private var _leavingPiP = false; private var _leavingPiP = false;
//region Fragment private var _landscapeOrientationListener: LandscapeOrientationListener? = null
constructor() : super() private var _portraitOrientationListener: PortraitOrientationListener? = null
private var _lastSetOrientation: Int = Configuration.ORIENTATION_UNDEFINED
private var _ignoreNextNewOrientation = false
fun nextVideo() { fun nextVideo() {
_viewDetail?.nextVideo(true, true, true); _viewDetail?.nextVideo(true, true, true);
@ -101,6 +109,15 @@ class VideoDetailFragment : MainFragment {
val isSmallWindow = isSmallWindow() val isSmallWindow = isSmallWindow()
val temp = _lastSetOrientation
if (_ignoreNextNewOrientation) {
_ignoreNextNewOrientation = false
} else {
// the device has rotated so update our state tracking what the physical orientation of the device is
_lastSetOrientation = newConfig.orientation
}
if ( if (
isSmallWindow isSmallWindow
&& newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
@ -113,6 +130,7 @@ class VideoDetailFragment : MainFragment {
&& isFullscreen && isFullscreen
&& !Settings.instance.playback.fullscreenPortrait && !Settings.instance.playback.fullscreenPortrait
&& newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
&& temp == Configuration.ORIENTATION_LANDSCAPE
&& isLandscapeVideo && isLandscapeVideo
) { ) {
_viewDetail?.setFullscreen(false) _viewDetail?.setFullscreen(false)
@ -153,20 +171,48 @@ class VideoDetailFragment : MainFragment {
val isSmallWindow = isSmallWindow() val isSmallWindow = isSmallWindow()
val autoRotateEnabled = android.provider.Settings.System.getInt(
context?.contentResolver,
android.provider.Settings.System.ACCELEROMETER_ROTATION, 0
) == 1
// For small windows if the device isn't landscape right now and full screen portrait isn't allowed then we should force landscape // 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) { if (isSmallWindow && isFullscreen && !isFullScreenPortraitAllowed && _lastSetOrientation != Configuration.ORIENTATION_LANDSCAPE && !rotationLock && isLandscapeVideo) {
if (Settings.instance.playback.forceAllowFullScreenRotation) { if (Settings.instance.playback.forceAllowFullScreenRotation) {
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
} else { } else {
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
} }
// the next orientation change will not reflect the device because we are manually setting the orientation to landscape
_ignoreNextNewOrientation = true
if (autoRotateEnabled
) {
// start listening for the device to rotate to landscape
// at which point we'll be able to set requestedOrientation to back to UNSPECIFIED
_landscapeOrientationListener?.enableListener()
}
} }
// For small windows if the device isn't in a portrait orientation and we're in the maximized state then we should force portrait // For small windows if the device isn't in a portrait orientation and we're in the maximized state then we should force portrait
else if (isSmallWindow && !isMinimizingFromFullScreen && !isFullscreen && state == State.MAXIMIZED && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { else if (isSmallWindow && !isMinimizingFromFullScreen && !isFullscreen && state == State.MAXIMIZED && _lastSetOrientation == Configuration.ORIENTATION_LANDSCAPE) {
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
// the next orientation change will not reflect the device because we are manually setting the orientation to portrait
_ignoreNextNewOrientation = true
if (autoRotateEnabled
) {
// start listening for the device to rotate to portrait
// at which point we'll be able to set requestedOrientation to back to UNSPECIFIED
_portraitOrientationListener?.enableListener()
} else {
// the rotation state resets to portrait when changing requestedOrientation
_lastSetOrientation = Configuration.ORIENTATION_PORTRAIT
}
} else if (rotationLock) { } else if (rotationLock) {
_portraitOrientationListener?.disableListener()
_landscapeOrientationListener?.disableListener()
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
} else { } else {
_portraitOrientationListener?.disableListener()
_landscapeOrientationListener?.disableListener()
a.requestedOrientation = if (isReversePortraitAllowed) { a.requestedOrientation = if (isReversePortraitAllowed) {
ActivityInfo.SCREEN_ORIENTATION_FULL_USER ActivityInfo.SCREEN_ORIENTATION_FULL_USER
} else { } else {
@ -341,6 +387,26 @@ class VideoDetailFragment : MainFragment {
StatePlayer.instance.onRotationLockChanged.subscribe(this) { StatePlayer.instance.onRotationLockChanged.subscribe(this) {
updateOrientation() updateOrientation()
} }
_landscapeOrientationListener = LandscapeOrientationListener(requireContext())
{
CoroutineScope(Dispatchers.Main).launch {
// delay to make sure that the system auto rotate updates
delay(300)
_lastSetOrientation = Configuration.ORIENTATION_LANDSCAPE
updateOrientation()
}
}
_portraitOrientationListener = PortraitOrientationListener(requireContext())
{
CoroutineScope(Dispatchers.Main).launch {
// delay to make sure that the system auto rotate updates
delay(300)
_lastSetOrientation = Configuration.ORIENTATION_PORTRAIT
updateOrientation()
}
}
return _view!!; return _view!!;
} }
@ -442,6 +508,9 @@ class VideoDetailFragment : MainFragment {
SettingsActivity.settingsActivityClosed.remove(this) SettingsActivity.settingsActivityClosed.remove(this)
StatePlayer.instance.onRotationLockChanged.remove(this) StatePlayer.instance.onRotationLockChanged.remove(this)
_landscapeOrientationListener?.disableListener()
_portraitOrientationListener?.disableListener()
_viewDetail?.let { _viewDetail?.let {
_viewDetail = null; _viewDetail = null;
it.onDestroy(); it.onDestroy();
@ -535,3 +604,65 @@ class VideoDetailFragment : MainFragment {
//TODO: Determine if encapsulated would be readable enough //TODO: Determine if encapsulated would be readable enough
//endregion //endregion
} }
class LandscapeOrientationListener(
context: Context,
private val onLandscapeDetected: () -> Unit
) : OrientationEventListener(context) {
private var isListening = false
override fun onOrientationChanged(orientation: Int) {
if (!isListening) return
if (orientation in 60..120 || orientation in 240..300) {
onLandscapeDetected()
disableListener()
}
}
fun enableListener() {
if (!isListening) {
isListening = true
enable()
}
}
fun disableListener() {
if (isListening) {
isListening = false
disable()
}
}
}
class PortraitOrientationListener(
context: Context,
private val onPortraitDetected: () -> Unit
) : OrientationEventListener(context) {
private var isListening = false
override fun onOrientationChanged(orientation: Int) {
if (!isListening) return
if (orientation in 0..30 || orientation in 330..360 || orientation in 150..210) {
onPortraitDetected()
disableListener()
}
}
fun enableListener() {
if (!isListening) {
isListening = true
enable()
}
}
fun disableListener() {
if (isListening) {
isListening = false
disable()
}
}
}

View File

@ -287,8 +287,8 @@
<string name="planned_content_notifications_description">Schedules discovered planned content as notifications, resulting in more accurate notifications for this content.</string> <string name="planned_content_notifications_description">Schedules discovered planned content as notifications, resulting in more accurate notifications for this content.</string>
<string name="attempt_to_utilize_byte_ranges">Attempt to utilize byte ranges</string> <string name="attempt_to_utilize_byte_ranges">Attempt to utilize byte ranges</string>
<string name="auto_update">Auto Update</string> <string name="auto_update">Auto Update</string>
<string name="force_allow_full_screen_rotation">Force Allow Full Screen Rotation</string> <string name="force_enable_auto_rotate_in_full_screen">Force Enable Auto-Rotate In Full-Screen Mode</string>
<string name="force_allow_full_screen_rotation_description">Allow auto-rotation between the two landscape orientations even when you disable auto-rotate at the system level.</string> <string name="force_enable_auto_rotate_in_full_screen_description">Force enable auto-rotation between the two landscape orientations in full-screen mode, even when you disable auto-rotate at the system level.</string>
<string name="simplify_sources">Simplify sources</string> <string name="simplify_sources">Simplify sources</string>
<string name="simplify_sources_description">Deduplicate sources by resolution so that only more relevant sources are visible.</string> <string name="simplify_sources_description">Deduplicate sources by resolution so that only more relevant sources are visible.</string>
<string name="automatic_backup">Automatic Backup</string> <string name="automatic_backup">Automatic Backup</string>