From 8d2979d8ce8ed67eb5cb07a2b4e5f637163c5290 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 12 May 2025 14:39:06 -0300 Subject: [PATCH] Fix camera rotation / phone orientation syncing. --- .../components/sensors/Orientation.java | 8 +++ .../components/webrtc/v2/DisplayMonitor.kt | 50 +++++++++++++++++++ .../webrtc/v2/WebRtcCallActivity.kt | 16 ++++++ 3 files changed, 74 insertions(+) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/DisplayMonitor.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/sensors/Orientation.java b/app/src/main/java/org/thoughtcrime/securesms/components/sensors/Orientation.java index 5f59b4e9fb..b92376fbe9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/sensors/Orientation.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/sensors/Orientation.java @@ -26,4 +26,12 @@ public enum Orientation { return PORTRAIT_BOTTOM_EDGE; } + + public static @NonNull Orientation fromSurfaceRotation(int surfaceRotation) { + return switch (surfaceRotation) { + case 1 -> LANDSCAPE_LEFT_EDGE; + case 3 -> LANDSCAPE_RIGHT_EDGE; + default -> PORTRAIT_BOTTOM_EDGE; + }; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/DisplayMonitor.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/DisplayMonitor.kt new file mode 100644 index 0000000000..a6690504c2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/DisplayMonitor.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.v2 + +import android.hardware.display.DisplayManager +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.channels.trySendBlocking +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow + +object DisplayMonitor { + + /** + * Emits a flow of events from a [DisplayManager.DisplayListener] + * callback. + */ + fun monitor(displayManager: DisplayManager): Flow { + return callbackFlow { + val displayListener = object : DisplayManager.DisplayListener { + override fun onDisplayAdded(displayId: Int) { + trySendBlocking(MonitorEvent.Added(displayId)) + } + + override fun onDisplayRemoved(displayId: Int) { + trySendBlocking(MonitorEvent.Removed(displayId)) + } + + override fun onDisplayChanged(displayId: Int) { + trySendBlocking(MonitorEvent.Changed(displayId)) + } + } + + displayManager.registerDisplayListener(displayListener, null) + awaitClose { + displayManager.unregisterDisplayListener(displayListener) + } + } + } + + sealed interface MonitorEvent { + val displayId: Int + + data class Added(override val displayId: Int) : MonitorEvent + data class Removed(override val displayId: Int) : MonitorEvent + data class Changed(override val displayId: Int) : MonitorEvent + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt index e7c11b1938..883470fa32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration +import android.hardware.display.DisplayManager import android.media.AudioManager import android.os.Build import android.os.Bundle @@ -24,6 +25,7 @@ import androidx.activity.viewModels import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.ContextCompat +import androidx.core.content.getSystemService import androidx.core.util.Consumer import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -44,6 +46,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.signal.core.util.ThreadUtil import org.signal.core.util.concurrent.LifecycleDisposable +import org.signal.core.util.concurrent.SignalDispatchers import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log import org.signal.ringrtc.GroupCall @@ -477,6 +480,19 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re viewModel.setIsInPipMode(isInPipMode()) lifecycleScope.launch { + launch(SignalDispatchers.Unconfined) { + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + val displayManager = application.getSystemService()!! + DisplayMonitor.monitor(displayManager) + .collectLatest { + val display = displayManager.getDisplay(it.displayId) ?: return@collectLatest + val orientation = Orientation.fromSurfaceRotation(display.rotation) + + AppDependencies.signalCallManager.orientationChanged(true, orientation.degrees) + } + } + } + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { viewModel.microphoneEnabled.collectLatest {