From 67784971aa4065e8cb7f1ddaa79ed4f4c87ae2cf Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Thu, 20 Jul 2023 14:26:27 -0700 Subject: [PATCH] [flexiglass] Fixes back navigation. Turns out that back navigation was implemented correctly, but the EdgeBackGestureHandler required the SysUiState to be in certain states to make the arrow and gesture enabled. This CL achieves that. Fix: 289515848 Test: manually verified that back works in the bouncer scene in Flexiglass Change-Id: I72b657afc9484569f7883c0a23ec2891a7abeff9 --- .../android/systemui/model/SysUiStateExt.kt | 47 +++++++++++++++++++ .../SystemUiDefaultSceneContainerStartable.kt | 32 +++++++++++++ .../systemui/model/SysUiStateExtTest.kt | 47 +++++++++++++++++++ ...temUiDefaultSceneContainerStartableTest.kt | 38 +++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt new file mode 100644 index 0000000000000..c74a71c522606 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.model + +import com.android.systemui.dagger.qualifiers.DisplayId + +/** + * In-bulk updates multiple flag values and commits the update. + * + * Example: + * ``` + * sysuiState.updateFlags( + * displayId, + * SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != SceneKey.Gone), + * SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == SceneKey.Shade), + * SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == SceneKey.QuickSettings), + * SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == SceneKey.Bouncer), + * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == SceneKey.Lockscreen), + * ) + * ``` + * + * You can inject [displayId] by injecting it using: + * ``` + * @DisplayId private val displayId: Int`, + * ``` + */ +fun SysUiState.updateFlags( + @DisplayId displayId: Int, + vararg flagValuePairs: Pair, +) { + flagValuePairs.forEach { (flag, enabled) -> setFlag(flag, enabled) } + commitUpdate(displayId) +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt index b23c4ec9aa8bc..92384d68157b6 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -20,14 +20,22 @@ import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.model.SysUiState +import com.android.systemui.model.updateFlags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged @@ -49,12 +57,15 @@ constructor( private val authenticationInteractor: AuthenticationInteractor, private val keyguardInteractor: KeyguardInteractor, private val featureFlags: FeatureFlags, + private val sysUiState: SysUiState, + @DisplayId private val displayId: Int, ) : CoreStartable { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { hydrateVisibility() automaticallySwitchScenes() + hydrateSystemUiState() } } @@ -121,6 +132,27 @@ constructor( } } + /** Keeps [SysUiState] up-to-date */ + private fun hydrateSystemUiState() { + applicationScope.launch { + sceneInteractor + .currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT) + .map { it.key } + .distinctUntilChanged() + .collect { sceneKey -> + sysUiState.updateFlags( + displayId, + SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != SceneKey.Gone), + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == SceneKey.Shade), + SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == SceneKey.QuickSettings), + SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == SceneKey.Bouncer), + SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to + (sceneKey == SceneKey.Lockscreen), + ) + } + } + } + private fun switchToScene(targetSceneKey: SceneKey) { sceneInteractor.setCurrentScene( containerName = CONTAINER_NAME, diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt new file mode 100644 index 0000000000000..f5a70f07c9688 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.model + +import android.view.Display +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.FakeDisplayTracker +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class SysUiStateExtTest : SysuiTestCase() { + + private val underTest = SysUiState(FakeDisplayTracker(context)) + + @Test + fun updateFlags() { + underTest.updateFlags( + Display.DEFAULT_DISPLAY, + 1 to true, + 2 to false, + 3 to true, + ) + + assertThat(underTest.flags and 1).isNotEqualTo(0) + assertThat(underTest.flags and 2).isEqualTo(0) + assertThat(underTest.flags and 3).isNotEqualTo(0) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt index 5638d708cf304..6f6c5a589f448 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -14,8 +14,11 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.scene.domain.startable +import android.view.Display import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -23,17 +26,24 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.model.SysUiState import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.map +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.times +import org.mockito.Mockito.verify @SmallTest @RunWith(JUnit4::class) @@ -53,6 +63,7 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { utils.keyguardInteractor( repository = keyguardRepository, ) + private val sysUiState: SysUiState = mock() private val underTest = SystemUiDefaultSceneContainerStartable( @@ -61,6 +72,8 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, keyguardInteractor = keyguardInteractor, featureFlags = featureFlags, + sysUiState = sysUiState, + displayId = Display.DEFAULT_DISPLAY, ) @Before @@ -375,6 +388,31 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) } + @Test + fun hydrateSystemUiState() = + testScope.runTest { + underTest.start() + runCurrent() + clearInvocations(sysUiState) + + listOf( + SceneKey.Gone, + SceneKey.Lockscreen, + SceneKey.Bouncer, + SceneKey.Shade, + SceneKey.QuickSettings, + ) + .forEachIndexed { index, sceneKey -> + sceneInteractor.setCurrentScene( + SceneContainerNames.SYSTEM_UI_DEFAULT, + SceneModel(sceneKey), + ) + runCurrent() + + verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY) + } + } + private fun prepareState( isFeatureEnabled: Boolean = true, isDeviceUnlocked: Boolean = false,