mirror of
https://github.com/bitwarden/android.git
synced 2026-05-07 11:29:37 -05:00
[PM-28834] bug: Setting configuration for VR devices on MainActivity (#6563)
This commit is contained in:
@@ -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 ->
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user