Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe75d5cd authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[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
parent 417fc153
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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,
+6 −2
Original line number Diff line number Diff line
@@ -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;
    }
+77 −0
Original line number Diff line number Diff line
/*
 * 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 },
            )
    }
}
+13 −1
Original line number Diff line number Diff line
@@ -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 {
+5 −11
Original line number Diff line number Diff line
@@ -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(),
                    )
                }
        }
Loading