mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-03 15:05:24 +00:00
OrientationManager
The new OrientationManager encapsulate the orientation logic and sends out a mutable state flow
This commit is contained in:
parent
503f361f63
commit
a1c8974e7b
@ -0,0 +1,87 @@
|
||||
package org.thoughtcrime.securesms.calls
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.provider.Settings
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity.SENSOR_SERVICE
|
||||
import org.thoughtcrime.securesms.webrtc.Orientation
|
||||
import kotlin.math.asin
|
||||
|
||||
class OrientationManager(private val context: Context): SensorEventListener {
|
||||
private var sensorManager: SensorManager? = null
|
||||
private var rotationVectorSensor: Sensor? = null
|
||||
|
||||
private val _orientation = MutableStateFlow(Orientation.UNKNOWN)
|
||||
val orientation: StateFlow<Orientation> = _orientation
|
||||
|
||||
fun startOrientationListener(){
|
||||
// create the sensor manager if it's still null
|
||||
if(sensorManager == null) {
|
||||
sensorManager = context.getSystemService(SENSOR_SERVICE) as SensorManager
|
||||
}
|
||||
|
||||
if(rotationVectorSensor == null) {
|
||||
rotationVectorSensor = sensorManager!!.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
}
|
||||
|
||||
sensorManager?.registerListener(this, rotationVectorSensor, SensorManager.SENSOR_DELAY_UI)
|
||||
}
|
||||
|
||||
fun stopOrientationListener(){
|
||||
sensorManager?.unregisterListener(this)
|
||||
}
|
||||
|
||||
fun destroy(){
|
||||
stopOrientationListener()
|
||||
sensorManager = null
|
||||
rotationVectorSensor = null
|
||||
_orientation.value = Orientation.UNKNOWN
|
||||
}
|
||||
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
|
||||
// if auto-rotate is off, bail and send UNKNOWN
|
||||
if (!isAutoRotateOn()) {
|
||||
_orientation.value = Orientation.UNKNOWN
|
||||
return
|
||||
}
|
||||
|
||||
// Get the quaternion from the rotation vector sensor
|
||||
val quaternion = FloatArray(4)
|
||||
SensorManager.getQuaternionFromVector(quaternion, event.values)
|
||||
|
||||
// Calculate Euler angles from the quaternion
|
||||
val pitch = asin(2.0 * (quaternion[0] * quaternion[2] - quaternion[3] * quaternion[1]))
|
||||
|
||||
// Convert radians to degrees
|
||||
val pitchDegrees = Math.toDegrees(pitch).toFloat()
|
||||
|
||||
// Determine the device's orientation based on the pitch and roll values
|
||||
val currentOrientation = when {
|
||||
pitchDegrees > 45 -> Orientation.LANDSCAPE
|
||||
pitchDegrees < -45 -> Orientation.REVERSED_LANDSCAPE
|
||||
else -> Orientation.PORTRAIT
|
||||
}
|
||||
|
||||
if (currentOrientation != _orientation.value) {
|
||||
_orientation.value = currentOrientation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Function to check if Android System Auto-rotate is on or off
|
||||
private fun isAutoRotateOn(): Boolean {
|
||||
return Settings.System.getInt(
|
||||
context.contentResolver,
|
||||
Settings.System.ACCELEROMETER_ROTATION, 0
|
||||
) == 1
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
||||
}
|
@ -8,7 +8,6 @@ import android.content.IntentFilter
|
||||
import android.graphics.Outline
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
@ -16,10 +15,8 @@ import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
@ -57,7 +54,7 @@ import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SP
|
||||
import kotlin.math.asin
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WebRtcCallActivity : PassphraseRequiredActionBarActivity(), SensorEventListener {
|
||||
class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
|
||||
|
||||
companion object {
|
||||
const val ACTION_PRE_OFFER = "pre-offer"
|
||||
@ -81,9 +78,13 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity(), SensorEventLis
|
||||
}
|
||||
private var hangupReceiver: BroadcastReceiver? = null
|
||||
|
||||
private var sensorManager: SensorManager? = null
|
||||
private var rotationVectorSensor: Sensor? = null
|
||||
private var lastOrientation = Orientation.UNKNOWN
|
||||
/**
|
||||
* We need to track the device's orientation so we can calculate whether or not to rotate the video streams
|
||||
* This works a lot better than using `OrientationEventListener > onOrientationChanged'
|
||||
* which gives us a rotation angle that doesn't take into account pitch vs roll, so tipping the device from front to back would
|
||||
* trigger the video rotation logic, while we really only want it when the device is in portrait or landscape.
|
||||
*/
|
||||
private var orientationManager = OrientationManager(this)
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
@ -105,15 +106,6 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity(), SensorEventLis
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
|
||||
// Only enable auto-rotate if system auto-rotate is enabled
|
||||
if (isAutoRotateOn()) {
|
||||
// Initialize the SensorManager
|
||||
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
|
||||
|
||||
// Initialize the sensors
|
||||
rotationVectorSensor = sensorManager!!.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
}
|
||||
|
||||
binding = ActivityWebrtcBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
@ -200,6 +192,13 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity(), SensorEventLis
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
orientationManager.orientation.collect { orientation ->
|
||||
viewModel.deviceOrientation = orientation
|
||||
updateControlsRotation()
|
||||
}
|
||||
}
|
||||
|
||||
clipFloatingInsets()
|
||||
}
|
||||
|
||||
@ -222,68 +221,24 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity(), SensorEventLis
|
||||
binding.floatingRendererContainer.clipToOutline = true
|
||||
}
|
||||
|
||||
//Function to check if Android System Auto-rotate is on or off
|
||||
private fun isAutoRotateOn(): Boolean {
|
||||
return Settings.System.getInt(
|
||||
contentResolver,
|
||||
Settings.System.ACCELEROMETER_ROTATION, 0
|
||||
) == 1
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
rotationVectorSensor?.also { sensor ->
|
||||
sensorManager?.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI)
|
||||
}
|
||||
orientationManager.startOrientationListener()
|
||||
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
sensorManager?.unregisterListener(this)
|
||||
orientationManager.stopOrientationListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to track the device's orientation so we can calculate whether or not to rotate the video streams
|
||||
* This works a lot better than using `OrientationEventListener > onOrientationChanged'
|
||||
* which gives us a rotation angle that doesn't take into account pitch vs roll, so tipping the device from front to back would
|
||||
* trigger the video rotation logic, while we really only want it when the device is in portrait or landscape.
|
||||
*/
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
|
||||
// Get the quaternion from the rotation vector sensor
|
||||
val quaternion = FloatArray(4)
|
||||
SensorManager.getQuaternionFromVector(quaternion, event.values)
|
||||
|
||||
// Calculate Euler angles from the quaternion
|
||||
val pitch = asin(2.0 * (quaternion[0] * quaternion[2] - quaternion[3] * quaternion[1]))
|
||||
|
||||
// Convert radians to degrees
|
||||
val pitchDegrees = Math.toDegrees(pitch).toFloat()
|
||||
|
||||
// Determine the device's orientation based on the pitch and roll values
|
||||
val currentOrientation = when {
|
||||
pitchDegrees > 45 -> Orientation.LANDSCAPE
|
||||
pitchDegrees < -45 -> Orientation.REVERSED_LANDSCAPE
|
||||
else -> Orientation.PORTRAIT
|
||||
}
|
||||
|
||||
if (currentOrientation != lastOrientation) {
|
||||
lastOrientation = currentOrientation
|
||||
viewModel.deviceOrientation = currentOrientation
|
||||
updateControlsRotation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
hangupReceiver?.let { receiver ->
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
|
||||
}
|
||||
|
||||
rotationVectorSensor = null
|
||||
orientationManager.destroy()
|
||||
}
|
||||
|
||||
private fun answerCall() {
|
||||
|
Loading…
Reference in New Issue
Block a user