Intermediate commit point

This commit is contained in:
Koen J 2024-08-20 14:25:07 +02:00
commit 1167c314ee
2 changed files with 154 additions and 22 deletions

View file

@ -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<Int>()
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)
}
}

View file

@ -1,17 +1,22 @@
package com.futo.platformplayer.fragment.mainactivity.main package com.futo.platformplayer.fragment.mainactivity.main
import AdvancedOrientationListener
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.GradientDrawable.Orientation
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.OrientationEventListener
import android.view.OrientationListener
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets import android.view.WindowInsets
import android.view.WindowInsetsController import android.view.WindowInsetsController
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs 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.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.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class VideoDetailFragment : MainFragment { class VideoDetailFragment : MainFragment {
override val isMainView : Boolean = false; override val isMainView : Boolean = false;
@ -39,6 +48,8 @@ class VideoDetailFragment : MainFragment {
private var _viewDetail : VideoDetailView? = null; private var _viewDetail : VideoDetailView? = null;
private var _view : SingleViewTouchableMotionLayout? = null; private var _view : SingleViewTouchableMotionLayout? = null;
private lateinit var _autoRotateChangeListener: AutoRotateChangeListener private lateinit var _autoRotateChangeListener: AutoRotateChangeListener
private lateinit var _orientationListener: AdvancedOrientationListener
private var _currentOrientation = 0
var isFullscreen : Boolean = false; var isFullscreen : Boolean = false;
val onFullscreenChanged = Event1<Boolean>(); val onFullscreenChanged = Event1<Boolean>();
@ -89,37 +100,37 @@ class VideoDetailFragment : MainFragment {
} }
private fun updateOrientation() { private fun updateOrientation() {
val a = activity ?: return
val isMaximized = state == State.MAXIMIZED; val isMaximized = state == State.MAXIMIZED;
val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait; val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait;
val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate(); val isAutoRotateAllowed = Settings.instance.playback.isAutoRotate();
val currentOrientation = _currentOrientation;
var desiredOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
if (isFullscreen && isMaximized) { if (isMaximized) {
if (isFullScreenPortraitAllowed) { if (isFullscreen) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR val isCurrentlyPortrait = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
} else if (isAutoRotateAllowed) { if (isAutoRotateAllowed && isCurrentlyPortrait) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR _viewDetail?.setFullscreen(false)
return
} else {
desiredOrientation = if (isFullScreenPortraitAllowed) ActivityInfo.SCREEN_ORIENTATION_SENSOR else ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
}
} else { } else {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE val isCurrentlyLandscape = currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
} if (isAutoRotateAllowed && isCurrentlyLandscape) {
} else { _viewDetail?.setFullscreen(true)
if (isMaximized && isAutoRotateAllowed) { return
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}"); if (a.requestedOrientation != desiredOrientation) {
} Log.i(TAG, "onConfigurationChanged setting requestedOrientation (desiredOrientation = ${desiredOrientation}, requestedOrientation = ${activity?.requestedOrientation})");
a.requestedOrientation = desiredOrientation
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 (isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isMaximized = ${isMaximized}, isFullScreenPortraitAllowed = ${isFullScreenPortraitAllowed}, isAutoRotateAllowed = ${isAutoRotateAllowed}) resulted in requested orientation ${activity?.requestedOrientation}");
} }
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
@ -294,6 +305,12 @@ class VideoDetailFragment : MainFragment {
updateOrientation() updateOrientation()
} }
_orientationListener = AdvancedOrientationListener(requireActivity(), lifecycleScope)
_orientationListener.onOrientationChanged.subscribe {
_currentOrientation = it
Logger.i(TAG, "Current orientation changed (_currentOrientation = ${_currentOrientation})")
updateOrientation()
}
return _view!!; return _view!!;
} }
@ -393,6 +410,7 @@ class VideoDetailFragment : MainFragment {
super.onDestroyMainView(); super.onDestroyMainView();
Logger.v(TAG, "onDestroyMainView"); Logger.v(TAG, "onDestroyMainView");
_autoRotateChangeListener?.unregister() _autoRotateChangeListener?.unregister()
_orientationListener.stopListening()
SettingsActivity.settingsActivityClosed.remove(this) SettingsActivity.settingsActivityClosed.remove(this)
StatePlayer.instance.onRotationLockChanged.remove(this) StatePlayer.instance.onRotationLockChanged.remove(this)