[PM-28834] bug: Setting configuration for VR devices on MainActivity (#6563)

This commit is contained in:
aj-rosado
2026-04-30 17:38:40 +01:00
committed by GitHub
parent 336b13ce31
commit cdd682c5aa
4 changed files with 88 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ import androidx.navigation.compose.NavHost
import com.bitwarden.annotation.OmitFromCoverage
import com.bitwarden.ui.platform.base.util.EventsEffect
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.platform.util.setHorizonOSAppLayout
import com.bitwarden.ui.platform.util.setupEdgeToEdge
import com.bitwarden.ui.platform.util.validate
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityCompletionManager
@@ -212,6 +213,16 @@ 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 {
mainViewModel.trySendAction(MainAction.Internal.ResizeHasBeenRequested)
}
}
}
@Composable
private fun SetupEventsEffect(navController: NavController) {
EventsEffect(viewModel = mainViewModel) { event ->

View File

@@ -98,6 +98,7 @@ class MainViewModel @Inject constructor(
theme = settingsRepository.appTheme,
isScreenCaptureAllowed = settingsRepository.isScreenCaptureAllowed,
isDynamicColorsEnabled = settingsRepository.isDynamicColorsEnabled,
hasResizeBeenRequested = false,
),
) {
private var specialCircumstance: SpecialCircumstance?
@@ -222,6 +223,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()
}
}
@@ -302,6 +304,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,
@@ -531,6 +537,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.
@@ -648,6 +655,11 @@ sealed class MainAction {
* should proceed.
*/
data object CookieAcquisitionReady : Internal()
/**
* Indicates that resize has been requested on the Activity
*/
data object ResizeHasBeenRequested : Internal()
}
}

View File

@@ -1325,6 +1325,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(
@@ -1353,6 +1377,7 @@ private val DEFAULT_STATE: MainState = MainState(
theme = AppTheme.DEFAULT,
isScreenCaptureAllowed = true,
isDynamicColorsEnabled = false,
hasResizeBeenRequested = false,
)
private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(

View File

@@ -0,0 +1,40 @@
@file:OmitFromCoverage
package com.bitwarden.ui.platform.util
import android.app.Activity
import android.os.Build
import com.bitwarden.annotation.OmitFromCoverage
/**
* Requests a HorizonOS-specific window resize to 1024×640 via reflection.
* Calls [onResizeRequested] only if the resize request succeeds.
*/
@Suppress("MagicNumber", "TooGenericExceptionCaught")
fun Activity.setHorizonOSAppLayout(
onResizeRequested: () -> Unit,
) {
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)
} catch (t: Throwable) {
// Not Horizon OS / API not present / request ignored by system
return@post
}
onResizeRequested()
}
}
private fun isHorizonOSDevice(): Boolean =
Build.MANUFACTURER.equals("Oculus", ignoreCase = true) ||
Build.MANUFACTURER.equals("Meta", ignoreCase = true)