From 434ec812f18bff78a9437fa51c2d087c32219107 Mon Sep 17 00:00:00 2001 From: Andre Rosado Date: Mon, 23 Feb 2026 13:59:09 +0000 Subject: [PATCH] Setting configuration for HorizonOS devices on MainActivity --- .../com/x8bit/bitwarden/MainActivity.kt | 35 +++++++++++++++++++ .../com/x8bit/bitwarden/MainViewModel.kt | 12 +++++++ .../com/x8bit/bitwarden/MainViewModelTest.kt | 25 +++++++++++++ 3 files changed, 72 insertions(+) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt b/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt index 593155a9283..85967c0b7b0 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt @@ -202,6 +202,14 @@ class MainActivity : AppCompatActivity() { .takeIf { it } ?: super.dispatchKeyEvent(event) + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + // resize only one time at the start + if (!mainViewModel.stateFlow.value.hasResizeBeenRequested) { + setHorizonOSAppLayout() + } + } + @Composable private fun SetupEventsEffect(navController: NavController) { EventsEffect(viewModel = mainViewModel) { event -> @@ -257,4 +265,31 @@ class MainActivity : AppCompatActivity() { window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) } } + + private fun isHorizonOSDevice(): Boolean { + return Build.MANUFACTURER.equals("Oculus", ignoreCase = true) || + Build.MANUFACTURER.equals("Meta", ignoreCase = true) + } + + @Suppress("MagicNumber", "TooGenericExceptionCaught") + private fun setHorizonOSAppLayout() { + if (!isHorizonOSDevice()) { + return + } + window.decorView.post { + try { + val clazz = Class.forName("horizonos.view.WindowExt") + val method = clazz.getMethod( + "requestWindowResize", + android.view.Window::class.java, + Int::class.javaPrimitiveType, + Int::class.javaPrimitiveType, + ) + method.invoke(null, window, 1024, 640) + mainViewModel.trySendAction(MainAction.Internal.ResizeHasBeenRequested) + } catch (t: Throwable) { + // Not Horizon OS / API not present / request ignored by system + } + } + } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt index 31e5fadb81c..16c643e5a2b 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt @@ -96,6 +96,7 @@ class MainViewModel @Inject constructor( theme = settingsRepository.appTheme, isScreenCaptureAllowed = settingsRepository.isScreenCaptureAllowed, isDynamicColorsEnabled = settingsRepository.isDynamicColorsEnabled, + hasResizeBeenRequested = false, ), ) { private var specialCircumstance: SpecialCircumstance? @@ -228,6 +229,7 @@ class MainViewModel @Inject constructor( is MainAction.Internal.ThemeUpdate -> handleAppThemeUpdated(action) is MainAction.Internal.DynamicColorsUpdate -> handleDynamicColorsUpdate(action) is MainAction.Internal.CookieAcquisitionReady -> handleCookieAcquisitionReady() + is MainAction.Internal.ResizeHasBeenRequested -> handleResizeHasBeenRequested() } } @@ -296,6 +298,10 @@ class MainViewModel @Inject constructor( sendEvent(MainEvent.NavigateToCookieAcquisition) } + private fun handleResizeHasBeenRequested() { + mutableStateFlow.update { it.copy(hasResizeBeenRequested = true) } + } + private fun handleFirstIntentReceived(action: MainAction.ReceiveFirstIntent) { handleIntent( intent = action.intent, @@ -517,6 +523,7 @@ data class MainState( val theme: AppTheme, val isScreenCaptureAllowed: Boolean, val isDynamicColorsEnabled: Boolean, + val hasResizeBeenRequested: Boolean, ) : Parcelable { /** * Contains all feature flags that are available to the UI. @@ -620,6 +627,11 @@ sealed class MainAction { * should proceed. */ data object CookieAcquisitionReady : Internal() + + /** + * Indicates that resize has been requested on the Activity + */ + data object ResizeHasBeenRequested : Internal() } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt index 19cff30ab86..d97592d749e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt @@ -1185,6 +1185,30 @@ class MainViewModelTest : BaseViewModelTest() { } } + @Test + fun `on handleResizeHasBeenRequested should set hasResizeBeenRequested as true`() = runTest { + val viewModel = createViewModel() + val initialState = MainState( + theme = settingsRepository.appTheme, + isScreenCaptureAllowed = settingsRepository.isScreenCaptureAllowed, + isDynamicColorsEnabled = settingsRepository.isDynamicColorsEnabled, + hasResizeBeenRequested = false, + ) + viewModel.stateFlow.test { + assertEquals( + initialState, + awaitItem(), + ) + viewModel.trySendAction(MainAction.Internal.ResizeHasBeenRequested) + assertEquals( + initialState.copy( + hasResizeBeenRequested = true, + ), + awaitItem(), + ) + } + } + private fun createViewModel( initialSpecialCircumstance: SpecialCircumstance? = null, ) = MainViewModel( @@ -1213,6 +1237,7 @@ private val DEFAULT_STATE: MainState = MainState( theme = AppTheme.DEFAULT, isScreenCaptureAllowed = true, isDynamicColorsEnabled = false, + hasResizeBeenRequested = false, ) private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(