From 834de928c27e267d29138e78954ef734cd740865 Mon Sep 17 00:00:00 2001 From: Koen Date: Mon, 5 Aug 2024 13:30:45 +0200 Subject: [PATCH 1/6] Rotation fixes. --- app/src/main/AndroidManifest.xml | 34 +-- .../java/com/futo/platformplayer/Settings.kt | 11 +- .../platformplayer/activities/MainActivity.kt | 50 ---- .../activities/SettingsActivity.kt | 8 + .../mainactivity/main/MainFragment.kt | 5 - .../mainactivity/main/VideoDetailFragment.kt | 216 ++++++++---------- .../listeners/AutoRotateChangeListener.kt | 42 ++++ .../listeners/OrientationManager.kt | 56 ----- .../futo/platformplayer/states/StatePlayer.kt | 8 +- .../views/video/FutoVideoPlayer.kt | 4 +- app/src/stable/assets/sources/spotify | 2 +- app/src/stable/assets/sources/youtube | 2 +- app/src/unstable/assets/sources/spotify | 2 +- app/src/unstable/assets/sources/youtube | 2 +- 14 files changed, 183 insertions(+), 259 deletions(-) create mode 100644 app/src/main/java/com/futo/platformplayer/listeners/AutoRotateChangeListener.kt delete mode 100644 app/src/main/java/com/futo/platformplayer/listeners/OrientationManager.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index be7cd437..0a19366e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,7 +50,7 @@ android:name=".activities.MainActivity" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="true" - android:screenOrientation="portrait" + android:screenOrientation="sensorPortrait" android:theme="@style/Theme.FutoVideo.NoActionBar" android:launchMode="singleTask" android:resizeableActivity="true" @@ -152,27 +152,27 @@ @@ -186,44 +186,44 @@ \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index 159b5810..6d37c951 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -23,6 +23,7 @@ import com.futo.platformplayer.states.StateBackup import com.futo.platformplayer.states.StateCache import com.futo.platformplayer.states.StateMeta import com.futo.platformplayer.states.StatePayment +import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StateUpdate import com.futo.platformplayer.stores.FragmentedStorage @@ -389,15 +390,7 @@ class Settings : FragmentedStorageFileJson() { @DropdownFieldOptionsId(R.array.system_enabled_disabled_array) var autoRotate: Int = 2; - fun isAutoRotate() = autoRotate == 1 || (autoRotate == 2 && StateApp.instance.getCurrentSystemAutoRotate()); - - @FormField(R.string.auto_rotate_dead_zone, FieldForm.DROPDOWN, R.string.this_prevents_the_device_from_rotating_within_the_given_amount_of_degrees, 6) - @DropdownFieldOptionsId(R.array.auto_rotate_dead_zone) - var autoRotateDeadZone: Int = 0; - - fun getAutoRotateDeadZoneDegrees(): Int { - return autoRotateDeadZone * 5; - } + fun isAutoRotate() = (autoRotate == 1 && !StatePlayer.instance.rotationLock) || (autoRotate == 2 && StateApp.instance.getCurrentSystemAutoRotate() && !StatePlayer.instance.rotationLock); @FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 7) @DropdownFieldOptionsId(R.array.player_background_behavior) diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index f13d0cc0..826d8eb2 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -39,7 +39,6 @@ import com.futo.platformplayer.fragment.mainactivity.topbar.GeneralTopBarFragmen import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment -import com.futo.platformplayer.listeners.OrientationManager import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.ImportCache import com.futo.platformplayer.states.* @@ -131,9 +130,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { val onNavigated = Event1(); - private lateinit var _orientationManager: OrientationManager; - var orientation: OrientationManager.Orientation = OrientationManager.Orientation.PORTRAIT - private set; private var _isVisible = true; private var _wasStopped = false; @@ -413,26 +409,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { .commitNow(); defaultTab.action(_fragBotBarMenu); - - _orientationManager = OrientationManager(this); - _orientationManager.onOrientationChanged.subscribe { - orientation = it; - Logger.i(TAG, "Orientation changed (Found ${it})"); - fragCurrent.onOrientationChanged(it); - if(_fragVideoDetail.state == VideoDetailFragment.State.MAXIMIZED) - _fragVideoDetail.onOrientationChanged(it); - else if(Settings.instance.other.bypassRotationPrevention) - { - requestedOrientation = when(orientation) { - OrientationManager.Orientation.PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - OrientationManager.Orientation.LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - OrientationManager.Orientation.REVERSED_PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT - OrientationManager.Orientation.REVERSED_LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE - } - } - }; - _orientationManager.enable(); - StateSubscriptions.instance; fragCurrent.onShown(null, false); @@ -529,17 +505,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { override fun onResume() { super.onResume(); Logger.v(TAG, "onResume") - - val curOrientation = _orientationManager.orientation; - - if(_fragVideoDetail.state == VideoDetailFragment.State.MAXIMIZED && _fragVideoDetail.lastOrientation != curOrientation) { - Logger.i(TAG, "Orientation mismatch (Found ${curOrientation})"); - orientation = curOrientation; - fragCurrent.onOrientationChanged(curOrientation); - if(_fragVideoDetail.state == VideoDetailFragment.State.MAXIMIZED) - _fragVideoDetail.onOrientationChanged(curOrientation); - } - _isVisible = true; } @@ -940,18 +905,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { override fun onRestart() { super.onRestart(); Logger.i(TAG, "onRestart"); - - //Force Portrait on restart - Logger.i(TAG, "Restarted with state ${_fragVideoDetail.state}"); - if(_fragVideoDetail.state != VideoDetailFragment.State.MAXIMIZED) { - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - WindowCompat.setDecorFitsSystemWindows(window, true) - WindowInsetsControllerCompat(window, rootView).let { controller -> - controller.show(WindowInsetsCompat.Type.statusBars()); - controller.show(WindowInsetsCompat.Type.systemBars()) - } - _fragVideoDetail.onOrientationChanged(OrientationManager.Orientation.PORTRAIT); - } } override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { @@ -966,9 +919,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { override fun onDestroy() { super.onDestroy(); Logger.v(TAG, "onDestroy") - - _orientationManager.disable(); - StateApp.instance.mainAppDestroyed(this); } diff --git a/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt index 268bd2c3..f7513f6e 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt @@ -18,6 +18,7 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import com.futo.platformplayer.* +import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.views.LoaderView @@ -184,12 +185,19 @@ class SettingsActivity : AppCompatActivity(), IWithResultLauncher { resultLauncher.launch(intent); } + override fun onDestroy() { + super.onDestroy() + settingsActivityClosed.emit() + } + companion object { //TODO: Temporary for solving Settings issues @SuppressLint("StaticFieldLeak") private var _lastActivity: SettingsActivity? = null; + val settingsActivityClosed = Event0() + fun getActivity(): SettingsActivity? { val act = _lastActivity; if(act != null && !act._isFinished) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/MainFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/MainFragment.kt index e9e1b0b6..967f607f 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/MainFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/MainFragment.kt @@ -8,7 +8,6 @@ import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.fragment.mainactivity.MainActivityFragment import com.futo.platformplayer.fragment.mainactivity.topbar.TopFragment -import com.futo.platformplayer.listeners.OrientationManager abstract class MainFragment : MainActivityFragment() { open val isMainView: Boolean = false; @@ -46,10 +45,6 @@ abstract class MainFragment : MainActivityFragment() { } - open fun onOrientationChanged(orientation: OrientationManager.Orientation) { - - } - open fun onBackPressed(): Boolean { return false; } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index 3c8a60da..18cd87d6 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -3,24 +3,25 @@ package com.futo.platformplayer.fragment.mainactivity.main import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.Bundle +import android.os.Handler +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowInsetsController import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.WindowInsetsControllerCompat import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs -import com.futo.platformplayer.activities.MainActivity +import com.futo.platformplayer.activities.SettingsActivity import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails -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.listeners.OrientationManager +import com.futo.platformplayer.listeners.AutoRotateChangeListener import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.PlatformVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime @@ -37,6 +38,7 @@ class VideoDetailFragment : MainFragment { private var _viewDetail : VideoDetailView? = null; private var _view : SingleViewTouchableMotionLayout? = null; + private lateinit var _autoRotateChangeListener: AutoRotateChangeListener var isFullscreen : Boolean = false; val onFullscreenChanged = Event1(); @@ -45,14 +47,22 @@ class VideoDetailFragment : MainFragment { var isInPictureInPicture : Boolean = false private set; - var state: State = State.CLOSED; + private var _state: State = State.CLOSED + + var state: State + get() = _state + set(value) { + _state = value + onStateChanged(value) + } + val currentUrl get() = _viewDetail?.currentUrl; val onMinimize = Event0(); val onTransitioning = Event1(); val onMaximized = Event0(); - var lastOrientation : OrientationManager.Orientation = OrientationManager.Orientation.PORTRAIT + var lastOrientation : Int = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT private set; private var _isInitialMaximize = true; @@ -74,6 +84,44 @@ class VideoDetailFragment : MainFragment { _viewDetail?.prevVideo(true); } + private fun onStateChanged(state: VideoDetailFragment.State) { + updateOrientation() + } + + private fun updateOrientation() { + val isMaximized = state == State.MAXIMIZED; + val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait; + val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate(); + + if (isFullscreen && isMaximized) { + if (isFullScreenPortraitAllowed) { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + } else if (isAutoRotateAllowed) { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + } else { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } + } else { + if (isMaximized && isAutoRotateAllowed) { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + } else { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + } + } + + Log.i(TAG, "updateOrientation (isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && !isFullscreen && Settings.instance.playback.isAutoRotate()) { + _viewDetail?.setFullscreen(true) + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && isFullscreen && Settings.instance.playback.isAutoRotate() && !Settings.instance.playback.fullscreenPortrait) { + _viewDetail?.setFullscreen(false) + } + } + override fun onShownWithView(parameter: Any?, isBack: Boolean) { super.onShownWithView(parameter, isBack); Logger.i(TAG, "onShownWithView parameter=$parameter") @@ -99,49 +147,6 @@ class VideoDetailFragment : MainFragment { } } - override fun onOrientationChanged(orientation: OrientationManager.Orientation) { - super.onOrientationChanged(orientation); - - if(!_isActive || state != State.MAXIMIZED) - return; - - var newOrientation = orientation; - val d = StateCasting.instance.activeDevice; - if (d != null && d.connectionState == CastConnectionState.CONNECTED) { - newOrientation = OrientationManager.Orientation.PORTRAIT; - } else if(StatePlayer.instance.rotationLock) { - return; - } - - if(Settings.instance.other.bypassRotationPrevention && orientation == OrientationManager.Orientation.PORTRAIT) - changeOrientation(OrientationManager.Orientation.PORTRAIT); - - if(lastOrientation == newOrientation) - return; - - activity?.let { - if (isFullscreen) { - if (Settings.instance.playback.fullscreenPortrait) { - changeOrientation(newOrientation); - } else { - if(newOrientation == OrientationManager.Orientation.REVERSED_LANDSCAPE && it.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - changeOrientation(OrientationManager.Orientation.REVERSED_LANDSCAPE); - else if(newOrientation == OrientationManager.Orientation.LANDSCAPE && it.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) - changeOrientation(OrientationManager.Orientation.LANDSCAPE); - else if(Settings.instance.playback.isAutoRotate() && (newOrientation == OrientationManager.Orientation.PORTRAIT || newOrientation == OrientationManager.Orientation.REVERSED_PORTRAIT)) { - _viewDetail?.setFullscreen(false); - } - } - } - else { - if(Settings.instance.playback.isAutoRotate() && (lastOrientation == OrientationManager.Orientation.PORTRAIT || lastOrientation == OrientationManager.Orientation.REVERSED_PORTRAIT)) { - lastOrientation = newOrientation; - _viewDetail?.setFullscreen(true); - } - } - } - lastOrientation = newOrientation; - } override fun onBackPressed(): Boolean { Logger.i(TAG, "onBackPressed") @@ -155,6 +160,7 @@ class VideoDetailFragment : MainFragment { closeVideoDetails(); return true; } + override fun onHide() { super.onHide(); } @@ -266,7 +272,6 @@ class VideoDetailFragment : MainFragment { override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) { } }); - context _view?.let { if (it.progress >= 0.5 && it.progress < 1.0) maximizeVideoDetail(); @@ -274,9 +279,21 @@ class VideoDetailFragment : MainFragment { minimizeVideoDetail(); } - _loadUrlOnCreate?.let { _viewDetail?.setVideo(it.url, it.timeSeconds, it.playWhenReady) }; + _autoRotateChangeListener = AutoRotateChangeListener(requireContext(), Handler()) { _ -> + updateOrientation() + } + _loadUrlOnCreate?.let { _viewDetail?.setVideo(it.url, it.timeSeconds, it.playWhenReady) }; maximizeVideoDetail(); + + SettingsActivity.settingsActivityClosed.subscribe(this) { + updateOrientation() + } + + StatePlayer.instance.onRotationLockChanged.subscribe(this) { + updateOrientation() + } + return _view!!; } @@ -334,11 +351,6 @@ class VideoDetailFragment : MainFragment { } } - val realOrientation = if(activity is MainActivity) (activity as MainActivity).orientation else lastOrientation; - Logger.i(TAG, "Real orientation on boot ${realOrientation}, lastOrientation: ${lastOrientation}"); - if(realOrientation != lastOrientation) - onOrientationChanged(realOrientation); - StateCasting.instance.onResume(); } override fun onPause() { @@ -380,6 +392,11 @@ class VideoDetailFragment : MainFragment { override fun onDestroyMainView() { super.onDestroyMainView(); Logger.v(TAG, "onDestroyMainView"); + _autoRotateChangeListener?.unregister() + + SettingsActivity.settingsActivityClosed.remove(this) + StatePlayer.instance.onRotationLockChanged.remove(this) + _viewDetail?.let { _viewDetail = null; it.onDestroy(); @@ -387,13 +404,6 @@ class VideoDetailFragment : MainFragment { _view = null; } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState); - StateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, _ -> - onOrientationChanged(lastOrientation); - }; - } - override fun onDestroy() { super.onDestroy() @@ -409,63 +419,37 @@ class VideoDetailFragment : MainFragment { onMaximized.clear(); } - private fun onFullscreenChanged(fullscreen : Boolean) { - activity?.let { - if (fullscreen) { - if (Settings.instance.playback.fullscreenPortrait) { - changeOrientation(lastOrientation); - } else { - var orient = lastOrientation; - if(orient == OrientationManager.Orientation.PORTRAIT || orient == OrientationManager.Orientation.REVERSED_PORTRAIT) - orient = OrientationManager.Orientation.LANDSCAPE; - changeOrientation(orient); - } - } - else - changeOrientation(OrientationManager.Orientation.PORTRAIT); + + private fun hideSystemUI() { + WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false) + activity?.window?.insetsController?.let { controller -> + controller.hide(WindowInsets.Type.statusBars()) + controller.hide(WindowInsets.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE } + } + + private fun showSystemUI() { + WindowCompat.setDecorFitsSystemWindows(requireActivity().window, true) + activity?.window?.insetsController?.let { controller -> + controller.show(WindowInsets.Type.statusBars()) + controller.show(WindowInsets.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT + } + } + + private fun onFullscreenChanged(fullscreen : Boolean) { isFullscreen = fullscreen; onFullscreenChanged.emit(isFullscreen); - _view?.allowMotion = !fullscreen; - } - private fun changeOrientation(orientation: OrientationManager.Orientation) { - Logger.i(TAG, "Orientation Change:" + orientation.name); - activity?.let { - when (orientation) { - OrientationManager.Orientation.LANDSCAPE -> { - it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - _view?.allowMotion = false; - WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false) - WindowInsetsControllerCompat(it.window, _viewDetail!!).let { controller -> - controller.hide(WindowInsetsCompat.Type.statusBars()); - controller.hide(WindowInsetsCompat.Type.systemBars()); - controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } - } - OrientationManager.Orientation.REVERSED_LANDSCAPE -> { - it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - _view?.allowMotion = false; - - WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false) - WindowInsetsControllerCompat(it.window, _viewDetail!!).let { controller -> - controller.hide(WindowInsetsCompat.Type.statusBars()); - controller.hide(WindowInsetsCompat.Type.systemBars()); - controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } - } - else -> { - it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - _view?.allowMotion = true; - - WindowCompat.setDecorFitsSystemWindows(it.window, true) - WindowInsetsControllerCompat(it.window, _viewDetail!!).let { controller -> - controller.show(WindowInsetsCompat.Type.statusBars()); - controller.show(WindowInsetsCompat.Type.systemBars()) - } - } - } + if (isFullscreen) { + hideSystemUI() + } else { + showSystemUI() } + + updateOrientation(); + _view?.allowMotion = !fullscreen; } companion object { diff --git a/app/src/main/java/com/futo/platformplayer/listeners/AutoRotateChangeListener.kt b/app/src/main/java/com/futo/platformplayer/listeners/AutoRotateChangeListener.kt new file mode 100644 index 00000000..12efb3aa --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/listeners/AutoRotateChangeListener.kt @@ -0,0 +1,42 @@ +package com.futo.platformplayer.listeners + +import android.content.Context +import android.database.ContentObserver +import android.os.Handler +import android.provider.Settings + +class AutoRotateObserver(handler: Handler, private val onChangeCallback: () -> Unit) : ContentObserver(handler) { + override fun onChange(selfChange: Boolean) { + super.onChange(selfChange) + onChangeCallback() + } +} + +class AutoRotateChangeListener(context: Context, handler: Handler, private val onAutoRotateChanged: (Boolean) -> Unit) { + + private val contentResolver = context.contentResolver + private val autoRotateObserver = AutoRotateObserver(handler) { + val isAutoRotateEnabled = isAutoRotateEnabled() + onAutoRotateChanged(isAutoRotateEnabled) + } + + init { + contentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), + false, + autoRotateObserver + ) + } + + fun unregister() { + contentResolver.unregisterContentObserver(autoRotateObserver) + } + + private fun isAutoRotateEnabled(): Boolean { + return Settings.System.getInt( + contentResolver, + Settings.System.ACCELEROMETER_ROTATION, + 0 + ) == 1 + } +} diff --git a/app/src/main/java/com/futo/platformplayer/listeners/OrientationManager.kt b/app/src/main/java/com/futo/platformplayer/listeners/OrientationManager.kt deleted file mode 100644 index bf6bf96d..00000000 --- a/app/src/main/java/com/futo/platformplayer/listeners/OrientationManager.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.futo.platformplayer.listeners - -import android.content.Context -import android.view.OrientationEventListener -import com.futo.platformplayer.Settings -import com.futo.platformplayer.constructs.Event1 -import com.futo.platformplayer.logging.Logger - -class OrientationManager : OrientationEventListener { - - val onOrientationChanged = Event1(); - - var orientation : Orientation = Orientation.PORTRAIT; - - constructor(context: Context) : super(context) { } - - //TODO: Something weird is going on here - //TODO: Old implementation felt pretty good for me, but now with 0 deadzone still feels bad, even though code should be identical? - override fun onOrientationChanged(orientationAnglep: Int) { - if (orientationAnglep == -1) return - - val deadZone = Settings.instance.playback.getAutoRotateDeadZoneDegrees() - val isInDeadZone = when (orientation) { - Orientation.PORTRAIT -> orientationAnglep in 0 until (60 - deadZone) || orientationAnglep in (300 + deadZone) .. 360 - Orientation.REVERSED_LANDSCAPE -> orientationAnglep in (60 + deadZone) until (140 - deadZone) - Orientation.REVERSED_PORTRAIT -> orientationAnglep in (140 + deadZone) until (220 - deadZone) - Orientation.LANDSCAPE -> orientationAnglep in (220 + deadZone) until (300 - deadZone) - } - - if (isInDeadZone) { - return; - } - - val newOrientation = when (orientationAnglep) { - in 60 until 140 -> Orientation.REVERSED_LANDSCAPE - in 140 until 220 -> Orientation.REVERSED_PORTRAIT - in 220 until 300 -> Orientation.LANDSCAPE - else -> Orientation.PORTRAIT - } - - Logger.i("OrientationManager", "Orientation=$newOrientation orientationAnglep=$orientationAnglep"); - - if (newOrientation != orientation) { - orientation = newOrientation - onOrientationChanged.emit(newOrientation) - } - } - - //TODO: Perhaps just use ActivityInfo orientations instead.. - enum class Orientation { - PORTRAIT, - LANDSCAPE, - REVERSED_PORTRAIT, - REVERSED_LANDSCAPE - } -} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt index 9226292d..bcdd122d 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt @@ -38,7 +38,13 @@ class StatePlayer { private var _thumbnailExoPlayer : PlayerManager? = null; //Video Status - var rotationLock : Boolean = false; + var rotationLock: Boolean = false + get() = field + set(value) { + field = value + onRotationLockChanged.emit(value) + } + val onRotationLockChanged = Event1() var loopVideo : Boolean = false; val isPlaying: Boolean get() = _exoplayer?.player?.playWhenReady ?: false; diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt index b7a44fbe..f4c1cf15 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt @@ -567,6 +567,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase { @OptIn(UnstableApi::class) fun setFullScreen(fullScreen: Boolean) { + updateRotateLock() + if (isFullScreen == fullScreen) { return; } @@ -759,7 +761,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { } fun updateRotateLock() { - if(!Settings.instance.playback.isAutoRotate()) { + if(Settings.instance.playback.autoRotate == 0) { _control_rotate_lock.visibility = View.GONE; _control_rotate_lock_fullscreen.visibility = View.GONE; } diff --git a/app/src/stable/assets/sources/spotify b/app/src/stable/assets/sources/spotify index c7000814..4d554e93 160000 --- a/app/src/stable/assets/sources/spotify +++ b/app/src/stable/assets/sources/spotify @@ -1 +1 @@ -Subproject commit c700081466038ee4782610feaa05cd4d34d024d8 +Subproject commit 4d554e93882d29542cb05d2956f4b2484d7df27f diff --git a/app/src/stable/assets/sources/youtube b/app/src/stable/assets/sources/youtube index 546d8623..24f9e445 160000 --- a/app/src/stable/assets/sources/youtube +++ b/app/src/stable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit 546d862342b10398d0737f0f2163691b611af8f2 +Subproject commit 24f9e4456faf97fbbb866e1df2df9d94628ffcc6 diff --git a/app/src/unstable/assets/sources/spotify b/app/src/unstable/assets/sources/spotify index c7000814..4d554e93 160000 --- a/app/src/unstable/assets/sources/spotify +++ b/app/src/unstable/assets/sources/spotify @@ -1 +1 @@ -Subproject commit c700081466038ee4782610feaa05cd4d34d024d8 +Subproject commit 4d554e93882d29542cb05d2956f4b2484d7df27f diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube index 546d8623..24f9e445 160000 --- a/app/src/unstable/assets/sources/youtube +++ b/app/src/unstable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit 546d862342b10398d0737f0f2163691b611af8f2 +Subproject commit 24f9e4456faf97fbbb866e1df2df9d94628ffcc6 From cf2639df3d047845138acce506a0e79744db51a3 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 6 Aug 2024 12:19:58 +0200 Subject: [PATCH 2/6] Build fixes. --- dep/futopay | 2 +- dep/polycentricandroid | 2 +- settings.gradle | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dep/futopay b/dep/futopay index 42689176..354e6a4b 160000 --- a/dep/futopay +++ b/dep/futopay @@ -1 +1 @@ -Subproject commit 4268917697b975e92dc74e45b4018042ef35c509 +Subproject commit 354e6a4b86b56e8503ce791083bb2dbd10074a51 diff --git a/dep/polycentricandroid b/dep/polycentricandroid index cedbb52d..0b9d90b8 160000 --- a/dep/polycentricandroid +++ b/dep/polycentricandroid @@ -1 +1 @@ -Subproject commit cedbb52d33a87bbff7b3e713347700138b715b69 +Subproject commit 0b9d90b853ce416f32bf6c3f8870a45e79ed1d5f diff --git a/settings.gradle b/settings.gradle index fae609e0..858e21cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,6 +3,7 @@ pluginManagement { gradlePluginPortal() google() mavenCentral() + jcenter() } } dependencyResolutionManagement { @@ -10,6 +11,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + jcenter() maven { url 'https://jitpack.io' } } } From 1167c314eefee0c1b3575a2151b3b15ef5d4ef39 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 20 Aug 2024 14:25:07 +0200 Subject: [PATCH 3/6] Intermediate commit point --- .../AdvancedOrientationListener.kt | 114 ++++++++++++++++++ .../mainactivity/main/VideoDetailFragment.kt | 62 ++++++---- 2 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt diff --git a/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt b/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt new file mode 100644 index 00000000..acc0baca --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt @@ -0,0 +1,114 @@ +import android.app.Activity +import android.content.Context +import android.content.pm.ActivityInfo +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.logging.Logger +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + + +class AdvancedOrientationListener(private val activity: Activity, private val lifecycleScope: CoroutineScope) { + private val sensorManager: SensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager + private val accelerometer: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) + private val magnetometer: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) + + private var lastOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + private var lastStableOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + private var lastOrientationChangeTime = 0L + private val debounceTime = 200L + private val stabilityThresholdTime = 800L + private var deviceAspectRatio: Float = 1.0f + + private val gravity = FloatArray(3) + private val geomagnetic = FloatArray(3) + private val rotationMatrix = FloatArray(9) + private val orientationAngles = FloatArray(3) + + val onOrientationChanged = Event1() + + private val sensorListener = object : SensorEventListener { + override fun onSensorChanged(event: SensorEvent) { + when (event.sensor.type) { + Sensor.TYPE_ACCELEROMETER -> { + System.arraycopy(event.values, 0, gravity, 0, gravity.size) + } + Sensor.TYPE_MAGNETIC_FIELD -> { + System.arraycopy(event.values, 0, geomagnetic, 0, geomagnetic.size) + } + } + + if (gravity.isNotEmpty() && geomagnetic.isNotEmpty()) { + val success = SensorManager.getRotationMatrix(rotationMatrix, null, gravity, geomagnetic) + if (success) { + SensorManager.getOrientation(rotationMatrix, orientationAngles) + + val azimuth = Math.toDegrees(orientationAngles[0].toDouble()).toFloat() + val pitch = Math.toDegrees(orientationAngles[1].toDouble()).toFloat() + val roll = Math.toDegrees(orientationAngles[2].toDouble()).toFloat() + + val newOrientation = when { + roll in -155f .. -15f && isWithinThreshold(pitch, 0f, 30.0) -> { + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } + roll in 15f .. 155f && isWithinThreshold(pitch, 0f, 30.0) -> { + ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE + } + isWithinThreshold(pitch, -90f, 30.0 * deviceAspectRatio) && roll in -15f .. 15f -> { + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + isWithinThreshold(pitch, 90f, 30.0 * deviceAspectRatio) && roll in -15f .. 15f -> { + ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT + } + else -> lastOrientation + } + + //Logger.i("AdvancedOrientationListener", "newOrientation = ${newOrientation}, roll = ${roll}, pitch = ${pitch}, azimuth = ${azimuth}") + + if (newOrientation != lastStableOrientation) { + val currentTime = System.currentTimeMillis() + if (currentTime - lastOrientationChangeTime > debounceTime) { + lastOrientationChangeTime = currentTime + lastStableOrientation = newOrientation + + lifecycleScope.launch(Dispatchers.Main) { + delay(stabilityThresholdTime) + if (newOrientation == lastStableOrientation) { + lastOrientation = newOrientation + onOrientationChanged.emit(newOrientation) + } + } + } + } + } + } + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} + } + + private fun isWithinThreshold(value: Float, target: Float, threshold: Double): Boolean { + return Math.abs(value - target) <= threshold + } + + init { + sensorManager.registerListener(sensorListener, accelerometer, SensorManager.SENSOR_DELAY_GAME) + sensorManager.registerListener(sensorListener, magnetometer, SensorManager.SENSOR_DELAY_GAME) + + val metrics = activity.resources.displayMetrics + deviceAspectRatio = (metrics.heightPixels.toFloat() / metrics.widthPixels.toFloat()) + if (deviceAspectRatio == 0.0f) + deviceAspectRatio = 1.0f + + lastOrientation = activity.resources.configuration.orientation + } + + fun stopListening() { + sensorManager.unregisterListener(sensorListener) + } +} diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index 18cd87d6..60e1b240 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -1,17 +1,22 @@ package com.futo.platformplayer.fragment.mainactivity.main +import AdvancedOrientationListener import android.content.pm.ActivityInfo import android.content.res.Configuration +import android.graphics.drawable.GradientDrawable.Orientation import android.os.Bundle import android.os.Handler import android.util.Log import android.view.LayoutInflater +import android.view.OrientationEventListener +import android.view.OrientationListener import android.view.View import android.view.ViewGroup import android.view.WindowInsets import android.view.WindowInsetsController import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.view.WindowCompat +import androidx.lifecycle.lifecycleScope import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs @@ -27,6 +32,10 @@ import com.futo.platformplayer.models.PlatformVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + class VideoDetailFragment : MainFragment { override val isMainView : Boolean = false; @@ -39,6 +48,8 @@ class VideoDetailFragment : MainFragment { private var _viewDetail : VideoDetailView? = null; private var _view : SingleViewTouchableMotionLayout? = null; private lateinit var _autoRotateChangeListener: AutoRotateChangeListener + private lateinit var _orientationListener: AdvancedOrientationListener + private var _currentOrientation = 0 var isFullscreen : Boolean = false; val onFullscreenChanged = Event1(); @@ -89,37 +100,37 @@ class VideoDetailFragment : MainFragment { } private fun updateOrientation() { + val a = activity ?: return val isMaximized = state == State.MAXIMIZED; val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait; val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate(); + val currentOrientation = _currentOrientation; + var desiredOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT - if (isFullscreen && isMaximized) { - if (isFullScreenPortraitAllowed) { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } else if (isAutoRotateAllowed) { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + if (isMaximized) { + if (isFullscreen) { + val isCurrentlyPortrait = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + if (isAutoRotateAllowed && isCurrentlyPortrait) { + _viewDetail?.setFullscreen(false) + return + } else { + desiredOrientation = if (isFullScreenPortraitAllowed) ActivityInfo.SCREEN_ORIENTATION_SENSOR else ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } } else { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - } - } else { - if (isMaximized && isAutoRotateAllowed) { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } else { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + val isCurrentlyLandscape = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + if (isAutoRotateAllowed && isCurrentlyLandscape) { + _viewDetail?.setFullscreen(true) + return + } } } - Log.i(TAG, "updateOrientation (isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && !isFullscreen && Settings.instance.playback.isAutoRotate()) { - _viewDetail?.setFullscreen(true) - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && isFullscreen && Settings.instance.playback.isAutoRotate() && !Settings.instance.playback.fullscreenPortrait) { - _viewDetail?.setFullscreen(false) + if (a.requestedOrientation != desiredOrientation) { + Log.i(TAG, "onConfigurationChanged setting requestedOrientation (desiredOrientation = ${desiredOrientation}, requestedOrientation = ${activity?.requestedOrientation})"); + a.requestedOrientation = desiredOrientation } + + Log.i(TAG, "updateOrientation (isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); } override fun onShownWithView(parameter: Any?, isBack: Boolean) { @@ -294,6 +305,12 @@ class VideoDetailFragment : MainFragment { updateOrientation() } + _orientationListener = AdvancedOrientationListener(requireActivity(), lifecycleScope) + _orientationListener.onOrientationChanged.subscribe { + _currentOrientation = it + Logger.i(TAG, "Current orientation changed (_currentOrientation = ${_currentOrientation})") + updateOrientation() + } return _view!!; } @@ -393,6 +410,7 @@ class VideoDetailFragment : MainFragment { super.onDestroyMainView(); Logger.v(TAG, "onDestroyMainView"); _autoRotateChangeListener?.unregister() + _orientationListener.stopListening() SettingsActivity.settingsActivityClosed.remove(this) StatePlayer.instance.onRotationLockChanged.remove(this) From 1dc50a697c69c9ef1107d693859244ff1171d937 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 20 Aug 2024 15:44:57 +0200 Subject: [PATCH 4/6] Hybrid orientation approach. --- .../SimpleOrientationListener.kt | 55 ++++++++++++++ .../mainactivity/main/VideoDetailFragment.kt | 75 ++++++++++++------- 2 files changed, 102 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt diff --git a/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt b/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt new file mode 100644 index 00000000..24c86095 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt @@ -0,0 +1,55 @@ +package com.futo.platformplayer + +import android.app.Activity +import android.content.pm.ActivityInfo +import android.hardware.SensorManager +import android.view.OrientationEventListener +import com.futo.platformplayer.constructs.Event1 +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +class SimpleOrientationListener( + private val activity: Activity, + private val lifecycleScope: CoroutineScope +) { + private var lastOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + private var lastStableOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + private val stabilityThresholdTime = 500L + + val onOrientationChanged = Event1() + + private val orientationListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_UI) { + override fun onOrientationChanged(orientation: Int) { + val newOrientation = when { + orientation in 45..134 -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE + orientation in 135..224 -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT + orientation in 225..314 -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + orientation in 315..360 || orientation in 0..44 -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + else -> lastOrientation + } + + if (newOrientation != lastStableOrientation) { + lastStableOrientation = newOrientation + + lifecycleScope.launch(Dispatchers.Main) { + delay(stabilityThresholdTime) + if (newOrientation == lastStableOrientation) { + lastOrientation = newOrientation + onOrientationChanged.emit(newOrientation) + } + } + } + } + } + + init { + orientationListener.enable() + lastOrientation = activity.resources.configuration.orientation + } + + fun stopListening() { + orientationListener.disable() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index 60e1b240..ed56e6e4 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -1,15 +1,11 @@ package com.futo.platformplayer.fragment.mainactivity.main -import AdvancedOrientationListener import android.content.pm.ActivityInfo import android.content.res.Configuration -import android.graphics.drawable.GradientDrawable.Orientation import android.os.Bundle import android.os.Handler import android.util.Log import android.view.LayoutInflater -import android.view.OrientationEventListener -import android.view.OrientationListener import android.view.View import android.view.ViewGroup import android.view.WindowInsets @@ -19,6 +15,7 @@ import androidx.core.view.WindowCompat import androidx.lifecycle.lifecycleScope import com.futo.platformplayer.R import com.futo.platformplayer.Settings +import com.futo.platformplayer.SimpleOrientationListener import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.activities.SettingsActivity import com.futo.platformplayer.api.media.models.video.IPlatformVideo @@ -32,9 +29,6 @@ import com.futo.platformplayer.models.PlatformVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch class VideoDetailFragment : MainFragment { @@ -48,8 +42,11 @@ class VideoDetailFragment : MainFragment { private var _viewDetail : VideoDetailView? = null; private var _view : SingleViewTouchableMotionLayout? = null; private lateinit var _autoRotateChangeListener: AutoRotateChangeListener - private lateinit var _orientationListener: AdvancedOrientationListener + private lateinit var _orientationListener: SimpleOrientationListener private var _currentOrientation = 0 + private var _temporaryForceLandscape = false + private var _temporaryForcePortrait = false + private var _wasFullScreen = false var isFullscreen : Boolean = false; val onFullscreenChanged = Event1(); @@ -100,37 +97,59 @@ class VideoDetailFragment : MainFragment { } private fun updateOrientation() { - val a = activity ?: return val isMaximized = state == State.MAXIMIZED; val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait; val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate(); - val currentOrientation = _currentOrientation; - var desiredOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + val currentOrientation = _currentOrientation + val isFs = isFullscreen + val wasFs = _wasFullScreen + _wasFullScreen = isFs - if (isMaximized) { - if (isFullscreen) { - val isCurrentlyPortrait = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - if (isAutoRotateAllowed && isCurrentlyPortrait) { - _viewDetail?.setFullscreen(false) - return + if (isFs && isMaximized) { + if (isFullScreenPortraitAllowed) { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + } else if (isAutoRotateAllowed) { + _temporaryForcePortrait = false + if (!wasFs && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { + _temporaryForceLandscape = true + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } else if (_temporaryForceLandscape && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) { + _temporaryForceLandscape = false + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR } else { - desiredOrientation = if (isFullScreenPortraitAllowed) ActivityInfo.SCREEN_ORIENTATION_SENSOR else ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR } } else { - val isCurrentlyLandscape = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - if (isAutoRotateAllowed && isCurrentlyLandscape) { - _viewDetail?.setFullscreen(true) - return + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } + } else { + if (isMaximized && isAutoRotateAllowed) { + _temporaryForceLandscape = false + if (wasFs && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) { + _temporaryForcePortrait = true + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + } else if (_temporaryForcePortrait && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { + _temporaryForcePortrait = false + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + } else { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR } + } else { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT } } - if (a.requestedOrientation != desiredOrientation) { - Log.i(TAG, "onConfigurationChanged setting requestedOrientation (desiredOrientation = ${desiredOrientation}, requestedOrientation = ${activity?.requestedOrientation})"); - a.requestedOrientation = desiredOrientation - } + Log.i(TAG, "updateOrientation (isFs = ${isFs}, wasFs = ${wasFs}, _temporaryLandscape = ${_temporaryForceLandscape}, currentOrientation = ${currentOrientation}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); + } - Log.i(TAG, "updateOrientation (isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && !isFullscreen && Settings.instance.playback.isAutoRotate()) { + _viewDetail?.setFullscreen(true) + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && isFullscreen && Settings.instance.playback.isAutoRotate() && !Settings.instance.playback.fullscreenPortrait) { + _viewDetail?.setFullscreen(false) + } } override fun onShownWithView(parameter: Any?, isBack: Boolean) { @@ -305,7 +324,7 @@ class VideoDetailFragment : MainFragment { updateOrientation() } - _orientationListener = AdvancedOrientationListener(requireActivity(), lifecycleScope) + _orientationListener = SimpleOrientationListener(requireActivity(), lifecycleScope) _orientationListener.onOrientationChanged.subscribe { _currentOrientation = it Logger.i(TAG, "Current orientation changed (_currentOrientation = ${_currentOrientation})") From ca8dc0f0f5bd9b66630b64ca7bfd73a95749fb98 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 20 Aug 2024 16:24:47 +0200 Subject: [PATCH 5/6] Auto rotate fixes. --- .../mainactivity/main/VideoDetailFragment.kt | 64 +++++-------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index ed56e6e4..e1f311fb 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -43,10 +43,7 @@ class VideoDetailFragment : MainFragment { private var _view : SingleViewTouchableMotionLayout? = null; private lateinit var _autoRotateChangeListener: AutoRotateChangeListener private lateinit var _orientationListener: SimpleOrientationListener - private var _currentOrientation = 0 - private var _temporaryForceLandscape = false - private var _temporaryForcePortrait = false - private var _wasFullScreen = false + private var _currentOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED var isFullscreen : Boolean = false; val onFullscreenChanged = Event1(); @@ -70,9 +67,6 @@ class VideoDetailFragment : MainFragment { val onTransitioning = Event1(); val onMaximized = Event0(); - var lastOrientation : Int = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - private set; - private var _isInitialMaximize = true; private val _maximizeProgress get() = _view?.progress ?: 0.0f; @@ -97,59 +91,22 @@ class VideoDetailFragment : MainFragment { } private fun updateOrientation() { - val isMaximized = state == State.MAXIMIZED; + val isMaximized = state == State.MAXIMIZED val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait; - val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate(); val currentOrientation = _currentOrientation val isFs = isFullscreen - val wasFs = _wasFullScreen - _wasFullScreen = isFs if (isFs && isMaximized) { if (isFullScreenPortraitAllowed) { activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } else if (isAutoRotateAllowed) { - _temporaryForcePortrait = false - if (!wasFs && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { - _temporaryForceLandscape = true - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - } else if (_temporaryForceLandscape && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) { - _temporaryForceLandscape = false - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } else { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } } else { activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE } } else { - if (isMaximized && isAutoRotateAllowed) { - _temporaryForceLandscape = false - if (wasFs && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) { - _temporaryForcePortrait = true - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT - } else if (_temporaryForcePortrait && (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { - _temporaryForcePortrait = false - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } else { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR - } - } else { - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT - } + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT } - Log.i(TAG, "updateOrientation (isFs = ${isFs}, wasFs = ${wasFs}, _temporaryLandscape = ${_temporaryForceLandscape}, currentOrientation = ${currentOrientation}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && !isFullscreen && Settings.instance.playback.isAutoRotate()) { - _viewDetail?.setFullscreen(true) - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && isFullscreen && Settings.instance.playback.isAutoRotate() && !Settings.instance.playback.fullscreenPortrait) { - _viewDetail?.setFullscreen(false) - } + Log.i(TAG, "updateOrientation (isFs = ${isFs}, currentOrientation = ${currentOrientation}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}) resulted in requested orientation ${activity?.requestedOrientation}"); } override fun onShownWithView(parameter: Any?, isBack: Boolean) { @@ -328,6 +285,19 @@ class VideoDetailFragment : MainFragment { _orientationListener.onOrientationChanged.subscribe { _currentOrientation = it Logger.i(TAG, "Current orientation changed (_currentOrientation = ${_currentOrientation})") + + if (Settings.instance.playback.isAutoRotate()) { + if (!isFullscreen && (it == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || it == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) { + _viewDetail?.setFullscreen(true) + return@subscribe + } + + if (isFullscreen && !Settings.instance.playback.fullscreenPortrait && (it == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || it == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { + _viewDetail?.setFullscreen(false) + return@subscribe + } + } + updateOrientation() } return _view!!; From ec19ea44ad6f95fd445e1d343240bf8451ac1f9b Mon Sep 17 00:00:00 2001 From: Koen J Date: Wed, 21 Aug 2024 16:05:37 +0200 Subject: [PATCH 6/6] Implemented fix for media session vanishing after 10 minutes. --- app/build.gradle | 2 ++ app/src/main/AndroidManifest.xml | 1 + .../java/com/futo/platformplayer/Settings.kt | 35 +++++++++++++++---- .../services/MediaPlaybackService.kt | 6 ++-- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ecde6342..a3ddf53e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -147,6 +147,8 @@ android { } dependencies { + implementation 'com.google.dagger:dagger:2.48' + annotationProcessor 'com.google.dagger:dagger-compiler:2.48' //Core implementation 'androidx.core:core-ktx:1.12.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0a19366e..05675554 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index 6d37c951..ff7a203a 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -2,8 +2,11 @@ package com.futo.platformplayer import android.content.ActivityNotFoundException import android.content.Context +import android.content.Context.POWER_SERVICE import android.content.Intent import android.net.Uri +import android.os.Build +import android.os.PowerManager import android.webkit.CookieManager import androidx.lifecycle.lifecycleScope import com.futo.platformplayer.activities.MainActivity @@ -35,6 +38,7 @@ import com.futo.platformplayer.views.fields.FormField import com.futo.platformplayer.views.fields.FormFieldButton import com.futo.platformplayer.views.fields.FormFieldWarning import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem +import com.stripe.android.customersheet.injection.CustomerSheetViewModelModule_Companion_ContextFactory.context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -45,6 +49,7 @@ import kotlinx.serialization.json.Json import java.io.File import java.time.OffsetDateTime + @Serializable data class MenuBottomBarSetting(val id: Int, var enabled: Boolean); @@ -58,7 +63,7 @@ class Settings : FragmentedStorageFileJson() { @Transient val onTabsChanged = Event0(); - @FormField(R.string.manage_polycentric_identity, FieldForm.BUTTON, R.string.manage_your_polycentric_identity, -6) + @FormField(R.string.manage_polycentric_identity, FieldForm.BUTTON, R.string.manage_your_polycentric_identity, -7) @FormFieldButton(R.drawable.ic_person) fun managePolycentricIdentity() { SettingsActivity.getActivity()?.let { @@ -74,7 +79,7 @@ class Settings : FragmentedStorageFileJson() { } } - @FormField(R.string.show_faq, FieldForm.BUTTON, R.string.get_answers_to_common_questions, -5) + @FormField(R.string.show_faq, FieldForm.BUTTON, R.string.get_answers_to_common_questions, -6) @FormFieldButton(R.drawable.ic_quiz) fun openFAQ() { try { @@ -84,7 +89,7 @@ class Settings : FragmentedStorageFileJson() { //Ignored } } - @FormField(R.string.show_issues, FieldForm.BUTTON, R.string.a_list_of_user_reported_and_self_reported_issues, -4) + @FormField(R.string.show_issues, FieldForm.BUTTON, R.string.a_list_of_user_reported_and_self_reported_issues, -5) @FormFieldButton(R.drawable.ic_data_alert) fun openIssues() { try { @@ -116,7 +121,7 @@ class Settings : FragmentedStorageFileJson() { } }*/ - @FormField(R.string.manage_tabs, FieldForm.BUTTON, R.string.change_tabs_visible_on_the_home_screen, -3) + @FormField(R.string.manage_tabs, FieldForm.BUTTON, R.string.change_tabs_visible_on_the_home_screen, -4) @FormFieldButton(R.drawable.ic_tabs) fun manageTabs() { try { @@ -130,7 +135,7 @@ class Settings : FragmentedStorageFileJson() { - @FormField(R.string.import_data, FieldForm.BUTTON, R.string.import_data_description, -2) + @FormField(R.string.import_data, FieldForm.BUTTON, R.string.import_data_description, -3) @FormFieldButton(R.drawable.ic_move_up) fun import() { val act = SettingsActivity.getActivity() ?: return; @@ -139,7 +144,7 @@ class Settings : FragmentedStorageFileJson() { act.startActivity(intent); } - @FormField(R.string.link_handling, FieldForm.BUTTON, R.string.allow_grayjay_to_handle_links, -1) + @FormField(R.string.link_handling, FieldForm.BUTTON, R.string.allow_grayjay_to_handle_links, -2) @FormFieldButton(R.drawable.ic_link) fun manageLinks() { try { @@ -149,6 +154,24 @@ class Settings : FragmentedStorageFileJson() { } } + /*@FormField(R.string.disable_battery_optimization, FieldForm.BUTTON, R.string.click_to_go_to_battery_optimization_settings_disabling_battery_optimization_will_prevent_the_os_from_killing_media_sessions, -1) + @FormFieldButton(R.drawable.battery_full_24px) + fun ignoreBatteryOptimization() { + SettingsActivity.getActivity()?.let { + val intent = Intent() + val packageName = it.packageName + val pm = it.getSystemService(POWER_SERVICE) as PowerManager; + if (!pm.isIgnoringBatteryOptimizations(packageName)) { + intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) + intent.setData(Uri.parse("package:$packageName")) + it.startActivity(intent) + UIDialogs.toast(it, "Please ignore battery optimizations for Grayjay") + } else { + UIDialogs.toast(it, "Battery optimizations already disabled for Grayjay") + } + } + }*/ + @FormField(R.string.language, "group", -1, 0) var language = LanguageSettings(); @Serializable diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt index 518e6792..e12e24af 100644 --- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt +++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt @@ -98,7 +98,7 @@ class MediaPlaybackService : Service() { _mediaSession?.setPlaybackState(PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f) .build()); - _mediaSession?.setCallback(object: MediaSessionCompat.Callback() { + _mediaSession?.setCallback(object : MediaSessionCompat.Callback() { override fun onSeekTo(pos: Long) { super.onSeekTo(pos) Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)"); @@ -120,7 +120,9 @@ class MediaPlaybackService : Service() { override fun onStop() { super.onStop(); Logger.i(TAG, "Media session callback onStop()"); - MediaControlReceiver.onCloseReceived.emit(); + //MediaControlReceiver.onCloseReceived.emit(); + MediaControlReceiver.onPauseReceived.emit(); + updateMediaSession( null); } override fun onSkipToPrevious() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index beecf75e..a8eef4fb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -768,6 +768,8 @@ Zoom Check to see if an update is available. Scroll to top + Disable Battery Optimization + Click to go to battery optimization settings. Disabling battery optimization will prevent the OS from killing media sessions. Recommendations Subscriptions