diff options
| author | 2024-01-26 14:06:09 -0800 | |
|---|---|---|
| committer | 2024-01-26 14:12:03 -0800 | |
| commit | fe75d5cdff80cd5bb277c38b571003488c24382f (patch) | |
| tree | fa6c601ff40020eed76d1c3d6df2fb1d34f76b89 | |
| parent | 417fc153215ee1361f365cc9e515b9212e8a376a (diff) | |
[flexiglass] Overrides some SysUiState flags.
SysUiState is a central holder of state flags for a lot of System UI, it
models state as a collection of "flags" where each flag has a unique
integer ID and either a true or false value. The internal modeling uses
bitwise operations to keep that state, likely for legacy performance
reasons that have since become more Cargo Cult than reality.
While it's true that Flexiglass' SceneContainerStartable already
actively hydrated SysUiState flags related to current scene and scene
switching (for example: shade visible, bouncher showing, etc.), there
are other non-Flexiglass classes who also write values for the same
flags and, since those classes don't read their own state from
Flexiglass, they often provide incorrect state to SysUiState, leading to
the current bug related to back navigation from the bouncer.
While the full long-term approach is to eliminate SysUiState and convert
its state to a collection of repositories and interactors, such a change
would be large and risky. This is captured in b/322510930.
Instead, a shorter-term approach is used by this CL. We introduce a
"plugin" class that knows about Flexiglass and provides "overrides" for
flag values. When any other class in the codebase attempts to write
values for certain flags in SysUiState, the plugin is consulted to check
if Flexiglass has a different value and, if so, the different value is
maintained.
Test: manually verified, together with the followup CL on this chain,
that the bouncer scene's back gesture and button (in 3-button nav mode)
actually works as intended - before this CL, there was no back
affordance at all.
Test: unit tests updated.
Bug: 322518343
Bug: 303131743
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: I3bf55f10f98fe61dfac689959b6655fecc3c3c62
12 files changed, 149 insertions, 20 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 1e5ebd0c2acd..e207ff278a42 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.model.SysUiState +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor @@ -244,7 +245,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.fakeDeviceEntryRepository.setUnlocked(false) val displayTracker = FakeDisplayTracker(context) - val sysUiState = SysUiState(displayTracker) + val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin) val startable = SceneContainerStartable( applicationScope = testScope.backgroundScope, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 92300efdc930..c1870c0de6e9 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -66,6 +66,7 @@ import com.android.systemui.log.dagger.MonitorLog; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.mediaprojection.appselector.MediaProjectionModule; import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule; +import com.android.systemui.model.SceneContainerPlugin; import com.android.systemui.model.SysUiState; import com.android.systemui.motiontool.MotionToolModule; import com.android.systemui.navigationbar.NavigationBarComponent; @@ -268,8 +269,11 @@ public abstract class SystemUIModule { @SysUISingleton @Provides - static SysUiState provideSysUiState(DisplayTracker displayTracker, DumpManager dumpManager) { - final SysUiState state = new SysUiState(displayTracker); + static SysUiState provideSysUiState( + DisplayTracker displayTracker, + DumpManager dumpManager, + SceneContainerPlugin sceneContainerPlugin) { + final SysUiState state = new SysUiState(displayTracker, sceneContainerPlugin); dumpManager.registerDumpable(state); return state; } diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt new file mode 100644 index 000000000000..6eb62263eb9a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 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.SysUISingleton +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.ObservableTransitionState +import com.android.systemui.scene.shared.model.SceneKey +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 dagger.Lazy +import javax.inject.Inject + +/** + * A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled + * from the scene framework when that framework is enabled. + */ +@SysUISingleton +class SceneContainerPlugin +@Inject +constructor( + private val interactor: Lazy<SceneInteractor>, +) { + /** + * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled + * or if the flag value doesn't need to be overridden. + */ + fun flagValueOverride(flag: Int): Boolean? { + if (!SceneContainerFlag.isEnabled) { + return null + } + + val transitionState = interactor.get().transitionState.value + val idleTransitionStateOrNull = transitionState as? ObservableTransitionState.Idle + val currentSceneOrNull = idleTransitionStateOrNull?.scene + return currentSceneOrNull?.let { sceneKey -> EvaluatorByFlag[flag]?.invoke(sceneKey) } + } + + companion object { + + /** + * Value evaluator function by state flag ID. + * + * The value evaluator function can be invoked, passing in the current [SceneKey] to know + * the override value of the flag ID. + * + * If the map doesn't contain an entry for a certain flag ID, it means that it doesn't need + * to be overridden by the scene framework. + */ + val EvaluatorByFlag = + mapOf<Int, (SceneKey) -> Boolean>( + SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != SceneKey.Gone }, + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == SceneKey.Shade }, + SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == SceneKey.QuickSettings }, + SYSUI_STATE_BOUNCER_SHOWING to { it == SceneKey.Bouncer }, + SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == SceneKey.Lockscreen }, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java index 3cdcb2c4f550..2dd2327e5387 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java @@ -41,13 +41,15 @@ public class SysUiState implements Dumpable { public static final boolean DEBUG = false; private final DisplayTracker mDisplayTracker; + private final SceneContainerPlugin mSceneContainerPlugin; private @QuickStepContract.SystemUiStateFlags int mFlags; private final List<SysUiStateCallback> mCallbacks = new ArrayList<>(); private int mFlagsToSet = 0; private int mFlagsToClear = 0; - public SysUiState(DisplayTracker displayTracker) { + public SysUiState(DisplayTracker displayTracker, SceneContainerPlugin sceneContainerPlugin) { mDisplayTracker = displayTracker; + mSceneContainerPlugin = sceneContainerPlugin; } /** @@ -71,6 +73,16 @@ public class SysUiState implements Dumpable { /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */ public SysUiState setFlag(int flag, boolean enabled) { + final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag); + if (overrideOrNull != null && enabled != overrideOrNull) { + if (DEBUG) { + Log.d(TAG, "setFlag for flag " + flag + " and value " + enabled + " overridden to " + + overrideOrNull + " by scene container plugin"); + } + + enabled = overrideOrNull; + } + if (enabled) { mFlagsToSet |= flag; } else { diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index c96651c1057e..23688e1214ae 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.model.SceneContainerPlugin import com.android.systemui.model.SysUiState import com.android.systemui.model.updateFlags import com.android.systemui.power.domain.interactor.PowerInteractor @@ -39,11 +40,6 @@ import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState 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 com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled import com.android.systemui.util.asIndenting @@ -291,12 +287,10 @@ constructor( .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), + *SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) -> + flag to evaluator.invoke(sceneKey) + } + .toTypedArray(), ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 044881e57897..04aef82cef43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -92,6 +92,7 @@ import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.model.SysUiState; import com.android.systemui.res.R; import com.android.systemui.settings.FakeDisplayTracker; @@ -159,6 +160,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { private View mSpyView; private View.OnTouchListener mTouchListener; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); + private KosmosJavaAdapter mKosmos; /** * return whether window magnification is supported for current test context. @@ -170,6 +172,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mKosmos = new KosmosJavaAdapter(this); mContext = Mockito.spy(getContext()); mHandler = new FakeHandler(TestableLooper.get(this).getLooper()); mInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -185,7 +188,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { return null; }).when(mSfVsyncFrameProvider).postFrameCallback( any(FrameCallback.class)); - mSysUiState = new SysUiState(mDisplayTracker); + mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin()); mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class)); when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then( returnsSecondArg()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt index f5a70f07c968..8e0541008f59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt @@ -20,6 +20,7 @@ import android.view.Display import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.FakeDisplayTracker +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -29,7 +30,13 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class SysUiStateExtTest : SysuiTestCase() { - private val underTest = SysUiState(FakeDisplayTracker(context)) + private val kosmos = testKosmos() + + private val underTest = + SysUiState( + FakeDisplayTracker(context), + kosmos.sceneContainerPlugin, + ) @Test fun updateFlags() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java index 1a93adc7a631..f03f4f7375f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.settings.FakeDisplayTracker; import org.junit.Before; @@ -42,13 +43,15 @@ public class SysUiStateTest extends SysuiTestCase { private static final int FLAG_4 = 1 << 3; private static final int DISPLAY_ID = DEFAULT_DISPLAY; + private KosmosJavaAdapter mKosmos; private SysUiState.SysUiStateCallback mCallback; private SysUiState mFlagsContainer; @Before public void setup() { FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext); - mFlagsContainer = new SysUiState(displayTracker); + mKosmos = new KosmosJavaAdapter(this); + mFlagsContainer = new SysUiState(displayTracker, mKosmos.getSceneContainerPlugin()); mCallback = mock(SysUiState.SysUiStateCallback.class); mFlagsContainer.addCallback(mCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt index 70a48f574949..f390e12045af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager import com.android.systemui.model.SysUiState +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP @@ -50,6 +51,7 @@ import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_GOING_TO import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_WAKING import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.testKosmos import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -79,11 +81,12 @@ class OverviewProxyServiceTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() + private val kosmos = testKosmos() private lateinit var subject: OverviewProxyService private val dumpManager = DumpManager() private val displayTracker = FakeDisplayTracker(mContext) private val fakeSystemClock = FakeSystemClock() - private val sysUiState = SysUiState(displayTracker) + private val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin) private val featureFlags = FakeFeatureFlags() private val wakefulnessLifecycle = WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 98f3ede850f4..7eba3f0602d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -548,7 +548,7 @@ public class BubblesTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - mSysUiState = new SysUiState(mDisplayTracker); + mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin()); mSysUiState.addCallback(sysUiFlags -> { mSysUiStateBubblesManageMenuExpanded = (sysUiFlags diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 321f94468a2e..11f2938141b4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -32,6 +32,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor @@ -74,6 +75,7 @@ class KosmosJavaAdapter( val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor } val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } val communalInteractor by lazy { kosmos.communalInteractor } + val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin } init { kosmos.applicationContext = testCase.context diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt new file mode 100644 index 000000000000..b1027b923a26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 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.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.scene.domain.interactor.sceneInteractor + +val Kosmos.sceneContainerPlugin by Fixture { SceneContainerPlugin { sceneInteractor } } |