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

Commit 07320490 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[CS] 4/4: Update IStatusBarService from WindowRootViewVisIntr/Repo.

Removes code from CentralSurfaces by having the window root view
visibility interactor & repository handle notifying IStatusBar when
lockscreen/shade is shown & hidden.

Bug: 296050180
Test: pull down shade from homescreen -> verified IStatusBarService is
notified of panel revealed with clearEffects=false,
notifLoad={number of notifs}
Test: receive HUN while on homescreen -> verified IStatusBarService is
notified of panel revealed with clearEffects=false, notifLoad=1
Test: receive notif while on AOD & pull down to expan notif -> verified
IStatusBarService is notified of panel revealed with clearEffects=true,
notifLoad={number of notifs}
Test: AOD -> lockscreen transition -> verfieid notifLoad = number of
visible notifs, not all notifs
Test: atest WindowRootViewVisibilityInteractorTest
WindowRootViewVisibilityRepositoryTest CentralSurfacesImplTest

Change-Id: Id5ed29e786fb20b619016d5a76132d0474c40278
parent 0bc3b285
Loading
Loading
Loading
Loading
+23 −17
Original line number Original line Diff line number Diff line
@@ -158,7 +158,7 @@ interface KeyguardRepository {
    val lastDozeTapToWakePosition: StateFlow<Point?>
    val lastDozeTapToWakePosition: StateFlow<Point?>


    /** Observable for the [StatusBarState] */
    /** Observable for the [StatusBarState] */
    val statusBarState: Flow<StatusBarState>
    val statusBarState: StateFlow<StatusBarState>


    /** Observable for device wake/sleep state */
    /** Observable for device wake/sleep state */
    val wakefulness: StateFlow<WakefulnessModel>
    val wakefulness: StateFlow<WakefulnessModel>
@@ -520,23 +520,29 @@ constructor(
        return keyguardBypassController.bypassEnabled
        return keyguardBypassController.bypassEnabled
    }
    }


    override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
    // TODO(b/297345631): Expose this at the interactor level instead so that it can be powered by
    // [SceneInteractor] when scenes are ready.
    override val statusBarState: StateFlow<StatusBarState> =
        conflatedCallbackFlow {
                val callback =
                val callback =
                    object : StatusBarStateController.StateListener {
                    object : StatusBarStateController.StateListener {
                        override fun onStateChanged(state: Int) {
                        override fun onStateChanged(state: Int) {
                    trySendWithFailureLogging(statusBarStateIntToObject(state), TAG, "state")
                }
            }

        statusBarStateController.addCallback(callback)
                            trySendWithFailureLogging(
                            trySendWithFailureLogging(
            statusBarStateIntToObject(statusBarStateController.getState()),
                                statusBarStateIntToObject(state),
                                TAG,
                                TAG,
            "initial state"
                                "state"
                            )
                            )
                        }
                    }


                statusBarStateController.addCallback(callback)
                awaitClose { statusBarStateController.removeCallback(callback) }
                awaitClose { statusBarStateController.removeCallback(callback) }
            }
            }
            .stateIn(
                scope,
                SharingStarted.Eagerly,
                statusBarStateIntToObject(statusBarStateController.state)
            )


    override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
    override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
        fun dispatchUpdate() {
        fun dispatchUpdate() {
+41 −1
Original line number Original line Diff line number Diff line
@@ -16,7 +16,11 @@


package com.android.systemui.scene.data.repository
package com.android.systemui.scene.data.repository


import android.os.RemoteException
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.UiBackground
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -24,11 +28,47 @@ import kotlinx.coroutines.flow.asStateFlow


/** Source of truth for the visibility of various parts of the window root view. */
/** Source of truth for the visibility of various parts of the window root view. */
@SysUISingleton
@SysUISingleton
class WindowRootViewVisibilityRepository @Inject constructor() {
class WindowRootViewVisibilityRepository
@Inject
constructor(
    private val statusBarService: IStatusBarService,
    @UiBackground private val uiBgExecutor: Executor,
) {
    private val _isLockscreenOrShadeVisible = MutableStateFlow(false)
    private val _isLockscreenOrShadeVisible = MutableStateFlow(false)
    val isLockscreenOrShadeVisible: StateFlow<Boolean> = _isLockscreenOrShadeVisible.asStateFlow()
    val isLockscreenOrShadeVisible: StateFlow<Boolean> = _isLockscreenOrShadeVisible.asStateFlow()


    fun setIsLockscreenOrShadeVisible(visible: Boolean) {
    fun setIsLockscreenOrShadeVisible(visible: Boolean) {
        _isLockscreenOrShadeVisible.value = visible
        _isLockscreenOrShadeVisible.value = visible
    }
    }

    /**
     * Called when the lockscreen or shade has been shown and can be interacted with so that SysUI
     * can notify external services.
     */
    fun onLockscreenOrShadeInteractive(
        shouldClearNotificationEffects: Boolean,
        notificationCount: Int,
    ) {
        executeServiceCallOnUiBg {
            statusBarService.onPanelRevealed(shouldClearNotificationEffects, notificationCount)
        }
    }

    /**
     * Called when the lockscreen or shade no longer can be interactecd with so that SysUI can
     * notify external services.
     */
    fun onLockscreenOrShadeNotInteractive() {
        executeServiceCallOnUiBg { statusBarService.onPanelHidden() }
    }

    private fun executeServiceCallOnUiBg(runnable: () -> Unit) {
        uiBgExecutor.execute {
            try {
                runnable.invoke()
            } catch (ex: RemoteException) {
                // Won't fail unless the world has ended
            }
        }
    }
}
}
+58 −3
Original line number Original line Diff line number Diff line
@@ -16,26 +16,39 @@


package com.android.systemui.scene.domain.interactor
package com.android.systemui.scene.domain.interactor


import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.init.NotificationsController
import com.android.systemui.statusbar.policy.HeadsUpManager
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch


/** Business logic about the visibility of various parts of the window root view. */
/** Business logic about the visibility of various parts of the window root view. */
@SysUISingleton
@SysUISingleton
class WindowRootViewVisibilityInteractor
class WindowRootViewVisibilityInteractor
@Inject
@Inject
constructor(
constructor(
    @Application scope: CoroutineScope,
    @Application private val scope: CoroutineScope,
    private val windowRootViewVisibilityRepository: WindowRootViewVisibilityRepository,
    private val windowRootViewVisibilityRepository: WindowRootViewVisibilityRepository,
    keyguardRepository: KeyguardRepository,
    private val keyguardRepository: KeyguardRepository,
) {
    private val headsUpManager: HeadsUpManager,
) : CoreStartable {

    private var notificationPresenter: NotificationPresenter? = null
    private var notificationsController: NotificationsController? = null

    private val isNotifPresenterFullyCollapsed: Boolean
        get() = notificationPresenter?.isPresenterFullyCollapsed ?: true


    /**
    /**
     * True if lockscreen (including AOD) or the shade is visible and false otherwise. Notably,
     * True if lockscreen (including AOD) or the shade is visible and false otherwise. Notably,
@@ -60,7 +73,49 @@ constructor(
            }
            }
            .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
            .stateIn(scope, SharingStarted.Eagerly, initialValue = false)


    /**
     * Sets classes that aren't easily injectable on this class.
     *
     * TODO(b/277762009): Inject these directly instead.
     */
    fun setUp(
        presenter: NotificationPresenter?,
        notificationsController: NotificationsController?,
    ) {
        this.notificationPresenter = presenter
        this.notificationsController = notificationsController
    }

    override fun start() {
        scope.launch {
            isLockscreenOrShadeVisibleAndInteractive.collect { interactive ->
                if (interactive) {
                    windowRootViewVisibilityRepository.onLockscreenOrShadeInteractive(
                        getShouldClearNotificationEffects(keyguardRepository.statusBarState.value),
                        getNotificationLoad(),
                    )
                } else {
                    windowRootViewVisibilityRepository.onLockscreenOrShadeNotInteractive()
                }
            }
        }
    }

    fun setIsLockscreenOrShadeVisible(visible: Boolean) {
    fun setIsLockscreenOrShadeVisible(visible: Boolean) {
        windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(visible)
        windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(visible)
    }
    }

    private fun getShouldClearNotificationEffects(statusBarState: StatusBarState): Boolean {
        return !isNotifPresenterFullyCollapsed &&
            (statusBarState == StatusBarState.SHADE ||
                statusBarState == StatusBarState.SHADE_LOCKED)
    }

    private fun getNotificationLoad(): Int {
        return if (headsUpManager.hasPinnedHeadsUp() && isNotifPresenterFullyCollapsed) {
            1
        } else {
            notificationsController?.getActiveNotificationsCount() ?: 0
        }
    }
}
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable
package com.android.systemui.scene.domain.startable


import com.android.systemui.CoreStartable
import com.android.systemui.CoreStartable
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import dagger.Binds
import dagger.Binds
import dagger.Module
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.ClassKey
@@ -29,4 +30,11 @@ interface SceneContainerStartableModule {
    @IntoMap
    @IntoMap
    @ClassKey(SceneContainerStartable::class)
    @ClassKey(SceneContainerStartable::class)
    fun bind(impl: SceneContainerStartable): CoreStartable
    fun bind(impl: SceneContainerStartable): CoreStartable

    @Binds
    @IntoMap
    @ClassKey(WindowRootViewVisibilityInteractor::class)
    fun bindWindowRootViewVisibilityInteractor(
        impl: WindowRootViewVisibilityInteractor
    ): CoreStartable
}
}
+6 −62
Original line number Original line Diff line number Diff line
@@ -171,6 +171,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -456,6 +457,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
    private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
    private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
    private final PluginManager mPluginManager;
    private final PluginManager mPluginManager;
    private final ShadeController mShadeController;
    private final ShadeController mShadeController;
    private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
    private final InitController mInitController;
    private final InitController mInitController;
    private final Lazy<CameraLauncher> mCameraLauncherLazy;
    private final Lazy<CameraLauncher> mCameraLauncherLazy;
    private final AlternateBouncerInteractor mAlternateBouncerInteractor;
    private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -704,6 +706,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
            Lazy<CentralSurfacesCommandQueueCallbacks> commandQueueCallbacksLazy,
            Lazy<CentralSurfacesCommandQueueCallbacks> commandQueueCallbacksLazy,
            PluginManager pluginManager,
            PluginManager pluginManager,
            ShadeController shadeController,
            ShadeController shadeController,
            WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            ViewMediatorCallback viewMediatorCallback,
            ViewMediatorCallback viewMediatorCallback,
            InitController initController,
            InitController initController,
@@ -814,6 +817,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
        mCommandQueueCallbacksLazy = commandQueueCallbacksLazy;
        mCommandQueueCallbacksLazy = commandQueueCallbacksLazy;
        mPluginManager = pluginManager;
        mPluginManager = pluginManager;
        mShadeController = shadeController;
        mShadeController = shadeController;
        mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mKeyguardViewMediatorCallback = viewMediatorCallback;
        mKeyguardViewMediatorCallback = viewMediatorCallback;
        mInitController = initController;
        mInitController = initController;
@@ -1516,6 +1520,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
                mNotifListContainer,
                mNotifListContainer,
                mStackScrollerController.getNotifStackController(),
                mStackScrollerController.getNotifStackController(),
                mNotificationActivityStarter);
                mNotificationActivityStarter);
        mWindowRootViewVisibilityInteractor.setUp(mPresenter, mNotificationsController);
    }
    }


    /**
    /**
@@ -2123,48 +2128,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
                com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
                com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
    }
    }


    protected void handleVisibleToUserChanged(boolean visibleToUser) {
        if (visibleToUser) {
            onVisibleToUser();
        } else {
            onInvisibleToUser();
        }
    }

    void onVisibleToUser() {
        /* The LEDs are turned off when the notification panel is shown, even just a little bit.
         * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
         * this.
         */
        boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
        boolean clearNotificationEffects =
                !mPresenter.isPresenterFullyCollapsed() && (mState == StatusBarState.SHADE
                        || mState == StatusBarState.SHADE_LOCKED);
        int notificationLoad = mNotificationsController.getActiveNotificationsCount();
        if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
            notificationLoad = 1;
        }
        final int finalNotificationLoad = notificationLoad;
        mUiBgExecutor.execute(() -> {
            try {
                mBarService.onPanelRevealed(clearNotificationEffects,
                        finalNotificationLoad);
            } catch (RemoteException ex) {
                // Won't fail unless the world has ended.
            }
        });
    }

    void onInvisibleToUser() {
        mUiBgExecutor.execute(() -> {
            try {
                mBarService.onPanelHidden();
            } catch (RemoteException ex) {
                // Won't fail unless the world has ended.
            }
        });
    }

    private void logStateToEventlog() {
    private void logStateToEventlog() {
        boolean isShowing = mKeyguardStateController.isShowing();
        boolean isShowing = mKeyguardStateController.isShowing();
        boolean isOccluded = mKeyguardStateController.isOccluded();
        boolean isOccluded = mKeyguardStateController.isOccluded();
@@ -2693,7 +2656,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
            releaseGestureWakeLock();
            releaseGestureWakeLock();
            mLaunchCameraWhenFinishedWaking = false;
            mLaunchCameraWhenFinishedWaking = false;
            mDeviceInteractive = false;
            mDeviceInteractive = false;
            updateVisibleToUser();


            updateNotificationPanelTouchState();
            updateNotificationPanelTouchState();
            getNotificationShadeWindowViewController().cancelCurrentTouch();
            getNotificationShadeWindowViewController().cancelCurrentTouch();
@@ -2790,7 +2752,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
                        /* wakingUp= */ true,
                        /* wakingUp= */ true,
                        mShouldDelayWakeUpAnimation);
                        mShouldDelayWakeUpAnimation);


                updateVisibleToUser();
                updateIsKeyguard();
                updateIsKeyguard();
                mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
                mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
                        && mFeatureFlags.isEnabled(
                        && mFeatureFlags.isEnabled(
@@ -3117,9 +3078,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {


    protected boolean mVisible;
    protected boolean mVisible;


    // mScreenOnFromKeyguard && mVisible.
    private boolean mVisibleToUser;

    protected DevicePolicyManager mDevicePolicyManager;
    protected DevicePolicyManager mDevicePolicyManager;
    private final PowerManager mPowerManager;
    private final PowerManager mPowerManager;
    protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3248,20 +3206,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
                        true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
                        true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
            }
            }
        }
        }
        updateVisibleToUser();
    }

    /**
     * @deprecated use {@link
     * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive} instead.
     */
    protected void updateVisibleToUser() {
        boolean oldVisibleToUser = mVisibleToUser;
        mVisibleToUser = mVisible && mDeviceInteractive;

        if (oldVisibleToUser != mVisibleToUser) {
            handleVisibleToUserChanged(mVisibleToUser);
        }
    }
    }


    /**
    /**
@@ -3442,7 +3386,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
                    // If we're visible and switched to SHADE_LOCKED (the user dragged
                    // If we're visible and switched to SHADE_LOCKED (the user dragged
                    // down on the lockscreen), clear notification LED, vibration,
                    // down on the lockscreen), clear notification LED, vibration,
                    // ringing.
                    // ringing.
                    // Other transitions are covered in handleVisibleToUserChanged().
                    // Other transitions are covered in WindowRootViewVisibilityInteractor.
                    if (mVisible && (newState == StatusBarState.SHADE_LOCKED
                    if (mVisible && (newState == StatusBarState.SHADE_LOCKED
                            || mStatusBarStateController.goingToFullShade())) {
                            || mStatusBarStateController.goingToFullShade())) {
                        clearNotificationEffects();
                        clearNotificationEffects();
Loading