summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Caitlin Shkuratov <caitlinshk@google.com> 2023-08-29 20:27:06 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-08-29 20:27:06 +0000
commitcf5fd08f38a8853a339081b34ca8aa70a51d6ed0 (patch)
tree11c1c70b77e01191cd97a3045e49b1c211573fe8
parentc56011bb93b01bbf33e14f1d3848dbcd6e7e2169 (diff)
parentf882c6671bff2553257ebfba6ef43e81d416c9e3 (diff)
Merge changes from topics "caitlinshk-main-backgesture", "caitlinshk-main-cs-notiflogger", "caitlinshk-main-statusbarservice" into main
* changes: [CS] Have NotificationGutsManager listen for shade/LS vis directly. [CS] 4/4: Update IStatusBarService from WindowRootViewVisIntr/Repo. [CS] 3/4: Move back gesture callback registration to back interactor. [CS] 2/4: Have NotificationLogger listen to shade/LS visibility directly [CS] 1/4: Add WindowRootViewVisibilityInteractor visibility flows
-rw-r--r--packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java161
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt204
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt346
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java107
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java195
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt2
24 files changed, 1267 insertions, 473 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 3c74bf484e98..066cba230b76 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -16,24 +16,69 @@
package com.android.systemui.back.domain.interactor
+import android.window.BackEvent
+import android.window.OnBackAnimationCallback
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import android.window.WindowOnBackInvokedDispatcher
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Handles requests to go back either from a button or gesture. */
@SysUISingleton
class BackActionInteractor
@Inject
constructor(
+ @Application private val scope: CoroutineScope,
private val statusBarStateController: StatusBarStateController,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val shadeController: ShadeController
-) {
+ private val shadeController: ShadeController,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
+ featureFlags: FeatureFlags,
+) : CoreStartable {
+
+ private var isCallbackRegistered = false
+
+ private val callback =
+ if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+ /**
+ * New callback that handles back gesture invoked, cancel, progress and provides
+ * feedback via Shade animation.
+ */
+ object : OnBackAnimationCallback {
+ override fun onBackInvoked() {
+ onBackRequested()
+ }
+
+ override fun onBackProgressed(backEvent: BackEvent) {
+ if (shouldBackBeHandled() && shadeViewController.canBeCollapsed()) {
+ shadeViewController.onBackProgressed(backEvent.progress)
+ }
+ }
+ }
+ } else {
+ OnBackInvokedCallback { onBackRequested() }
+ }
+
+ private val onBackInvokedDispatcher: WindowOnBackInvokedDispatcher?
+ get() =
+ notificationShadeWindowController.windowRootView?.viewRootImpl?.onBackInvokedDispatcher
+
private lateinit var shadeViewController: ShadeViewController
private lateinit var qsController: QuickSettingsController
@@ -42,6 +87,19 @@ constructor(
this.shadeViewController = svController
}
+ override fun start() {
+ scope.launch {
+ windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive.collect {
+ visible ->
+ if (visible) {
+ registerBackCallback()
+ } else {
+ unregisterBackCallback()
+ }
+ }
+ }
+ }
+
fun shouldBackBeHandled(): Boolean {
return statusBarStateController.state != StatusBarState.KEYGUARD &&
statusBarStateController.state != StatusBarState.SHADE_LOCKED &&
@@ -74,4 +132,24 @@ constructor(
}
return false
}
+
+ private fun registerBackCallback() {
+ if (isCallbackRegistered) {
+ return
+ }
+ onBackInvokedDispatcher?.let {
+ it.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback)
+ isCallbackRegistered = true
+ }
+ }
+
+ private fun unregisterBackCallback() {
+ if (!isCallbackRegistered) {
+ return
+ }
+ onBackInvokedDispatcher?.let {
+ it.unregisterOnBackInvokedCallback(callback)
+ isCallbackRegistered = false
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 1b2a9ebc9ca4..7ce7ce94e4c9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -23,6 +23,7 @@ import com.android.systemui.ScreenDecorations
import com.android.systemui.SliceBroadcastRelayHandler
import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.WindowMagnification
+import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
@@ -346,4 +347,9 @@ abstract class SystemUICoreStartableModule {
abstract fun bindStatusBarHeadsUpChangeListener(
impl: StatusBarHeadsUpChangeListener
): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(BackActionInteractor::class)
+ abstract fun bindBackActionInteractor(impl: BackActionInteractor): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 42cd3a576926..d399e4c145fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -158,7 +158,7 @@ interface KeyguardRepository {
val lastDozeTapToWakePosition: StateFlow<Point?>
/** Observable for the [StatusBarState] */
- val statusBarState: Flow<StatusBarState>
+ val statusBarState: StateFlow<StatusBarState>
/** Observable for device wake/sleep state */
val wakefulness: StateFlow<WakefulnessModel>
@@ -520,23 +520,29 @@ constructor(
return keyguardBypassController.bypassEnabled
}
- override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
- val callback =
- object : StatusBarStateController.StateListener {
- override fun onStateChanged(state: Int) {
- trySendWithFailureLogging(statusBarStateIntToObject(state), TAG, "state")
- }
- }
-
- statusBarStateController.addCallback(callback)
- trySendWithFailureLogging(
- statusBarStateIntToObject(statusBarStateController.getState()),
- TAG,
- "initial state"
- )
+ // 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 =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(state: Int) {
+ trySendWithFailureLogging(
+ statusBarStateIntToObject(state),
+ TAG,
+ "state"
+ )
+ }
+ }
- awaitClose { statusBarStateController.removeCallback(callback) }
- }
+ statusBarStateController.addCallback(callback)
+ awaitClose { statusBarStateController.removeCallback(callback) }
+ }
+ .stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ statusBarStateIntToObject(statusBarStateController.state)
+ )
override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
fun dispatchUpdate() {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt
new file mode 100644
index 000000000000..d833e56a1795
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 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.scene.data.repository
+
+import android.os.RemoteException
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Source of truth for the visibility of various parts of the window root view. */
+@SysUISingleton
+class WindowRootViewVisibilityRepository
+@Inject
+constructor(
+ private val statusBarService: IStatusBarService,
+ @UiBackground private val uiBgExecutor: Executor,
+) {
+ private val _isLockscreenOrShadeVisible = MutableStateFlow(false)
+ val isLockscreenOrShadeVisible: StateFlow<Boolean> = _isLockscreenOrShadeVisible.asStateFlow()
+
+ fun setIsLockscreenOrShadeVisible(visible: Boolean) {
+ _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
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
new file mode 100644
index 000000000000..16ffcc27a70f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 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.scene.domain.interactor
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+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.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.init.NotificationsController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Business logic about the visibility of various parts of the window root view. */
+@SysUISingleton
+class WindowRootViewVisibilityInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val windowRootViewVisibilityRepository: WindowRootViewVisibilityRepository,
+ 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,
+ * false if the bouncer is visible.
+ *
+ * TODO(b/297080059): Use [SceneInteractor] as the source of truth if the scene flag is on.
+ */
+ val isLockscreenOrShadeVisible: StateFlow<Boolean> =
+ windowRootViewVisibilityRepository.isLockscreenOrShadeVisible
+
+ /**
+ * True if lockscreen (including AOD) or the shade is visible **and** the user is currently
+ * interacting with the device, false otherwise. Notably, false if the bouncer is visible and
+ * false if the device is asleep.
+ */
+ val isLockscreenOrShadeVisibleAndInteractive: StateFlow<Boolean> =
+ combine(
+ isLockscreenOrShadeVisible,
+ keyguardRepository.wakefulness,
+ ) { isKeyguardAodOrShadeVisible, wakefulness ->
+ isKeyguardAodOrShadeVisible && wakefulness.isDeviceInteractive()
+ }
+ .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) {
+ 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
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
index 8da1803053f4..fcfdcebba2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -29,4 +30,11 @@ interface SceneContainerStartableModule {
@IntoMap
@ClassKey(SceneContainerStartable::class)
fun bind(impl: SceneContainerStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(WindowRootViewVisibilityInteractor::class)
+ fun bindWindowRootViewVisibilityInteractor(
+ impl: WindowRootViewVisibilityInteractor
+ ): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index c9a73e665293..ef688a8e4337 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 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.scene.ui.view
import android.annotation.SuppressLint
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index d7a339210412..9a3e4e577634 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.ShadeTouchLog;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -59,6 +60,7 @@ public final class ShadeControllerImpl implements ShadeController {
private final CommandQueue mCommandQueue;
private final Executor mMainExecutor;
private final LogBuffer mTouchLog;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private final KeyguardStateController mKeyguardStateController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarStateController mStatusBarStateController;
@@ -83,6 +85,7 @@ public final class ShadeControllerImpl implements ShadeController {
CommandQueue commandQueue,
@Main Executor mainExecutor,
@ShadeTouchLog LogBuffer touchLog,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -97,6 +100,7 @@ public final class ShadeControllerImpl implements ShadeController {
mCommandQueue = commandQueue;
mMainExecutor = mainExecutor;
mTouchLog = touchLog;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mShadeViewControllerLazy = shadeViewControllerLazy;
mStatusBarStateController = statusBarStateController;
mStatusBarWindowController = statusBarWindowController;
@@ -391,6 +395,7 @@ public final class ShadeControllerImpl implements ShadeController {
private void notifyVisibilityChanged(boolean visible) {
mShadeVisibilityListener.visibilityChanged(visible);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible);
}
private void notifyExpandedVisibleChanged(boolean expandedVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 2532bad1d7a7..b553f0fccd91 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -99,9 +99,6 @@ interface ShadeSurface : ShadeViewController {
/** Sets the view's alpha to max. */
fun resetAlpha()
- /** Sets progress of the predictive back animation. */
- fun onBackProgressed(progressFraction: Float)
-
/** @see com.android.systemui.keyguard.ScreenLifecycle.Observer.onScreenTurningOn */
fun onScreenTurningOn()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 182a676c9841..1121834f196d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -155,6 +155,9 @@ interface ShadeViewController {
/** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
fun onBackPressed()
+ /** Sets progress of the predictive back animation. */
+ fun onBackProgressed(progressFraction: Float)
+
/** Sets whether the status bar launch animation is currently running. */
fun setIsLaunchAnimationRunning(running: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 09b74b213ebf..6a2bef296b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -58,6 +58,7 @@ class ShadeViewControllerEmptyImpl @Inject constructor() : ShadeViewController {
return false
}
override fun onBackPressed() {}
+ override fun onBackProgressed(progressFraction: Float) {}
override fun setIsLaunchAnimationRunning(running: Boolean) {}
override fun setAlpha(alpha: Int, animate: Boolean) {}
override fun setAlphaChangeAnimationEndAction(r: Runnable) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 637637d39957..09be41b56a10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -19,12 +19,13 @@ package com.android.systemui.statusbar.notification.dagger;
import android.content.Context;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.shade.ShadeEventsModule;
-import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -76,10 +77,13 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
import java.util.concurrent.Executor;
@@ -110,6 +114,13 @@ public interface NotificationsModule {
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
+ /** Binds {@link NotificationGutsManager} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationGutsManager.class)
+ CoreStartable bindsNotificationGutsManager(NotificationGutsManager notificationGutsManager);
+
+
/** Provides an instance of {@link VisibilityLocationProvider} */
@Binds
VisibilityLocationProvider bindVisibilityLocationProvider(
@@ -125,7 +136,8 @@ public interface NotificationsModule {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
return new NotificationLogger(
@@ -135,11 +147,18 @@ public interface NotificationsModule {
visibilityProvider,
notifPipeline,
statusBarStateController,
- shadeExpansionStateManager,
+ windowRootViewVisibilityInteractor,
+ javaAdapter,
expansionStateLogger,
notificationPanelLogger);
}
+ /** Binds {@link NotificationLogger} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationLogger.class)
+ CoreStartable bindsNotificationLogger(NotificationLogger notificationLogger);
+
/** Provides an instance of {@link NotificationPanelLogger} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 26f97de95070..d2034d7a8564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -33,10 +33,11 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -48,6 +49,7 @@ import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.util.Collection;
import java.util.Collections;
@@ -62,7 +64,7 @@ import javax.inject.Inject;
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
-public class NotificationLogger implements StateListener {
+public class NotificationLogger implements StateListener, CoreStartable {
static final String TAG = "NotificationLogger";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -81,6 +83,8 @@ public class NotificationLogger implements StateListener {
private final NotifPipeline mNotifPipeline;
private final NotificationPanelLogger mNotificationPanelLogger;
private final ExpansionStateLogger mExpansionStateLogger;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+ private final JavaAdapter mJavaAdapter;
protected Handler mHandler = new Handler();
protected IStatusBarService mBarService;
@@ -88,10 +92,7 @@ public class NotificationLogger implements StateListener {
private NotificationListContainer mListContainer;
private final Object mDozingLock = new Object();
@GuardedBy("mDozingLock")
- private Boolean mDozing = null; // Use null to indicate state is not yet known
- @GuardedBy("mDozingLock")
private Boolean mLockscreen = null; // Use null to indicate state is not yet known
- private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known
private boolean mLogging = false;
// Tracks notifications currently visible in mNotificationStackScroller and
@@ -202,7 +203,8 @@ public class NotificationLogger implements StateListener {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
@@ -214,9 +216,10 @@ public class NotificationLogger implements StateListener {
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mExpansionStateLogger = expansionStateLogger;
mNotificationPanelLogger = notificationPanelLogger;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
+ mJavaAdapter = javaAdapter;
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
registerNewPipelineListener();
}
@@ -239,6 +242,22 @@ public class NotificationLogger implements StateListener {
mListContainer = listContainer;
}
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive(),
+ this::onLockscreenOrShadeVisibleAndInteractiveChanged);
+ }
+
+ private void onLockscreenOrShadeVisibleAndInteractiveChanged(boolean visible) {
+ if (visible) {
+ startNotificationLogging();
+ } else {
+ // Ensure we stop notification logging when the device isn't interactive.
+ stopNotificationLogging();
+ }
+ }
+
public void stopNotificationLogging() {
if (mLogging) {
mLogging = false;
@@ -257,16 +276,19 @@ public class NotificationLogger implements StateListener {
}
}
+ @GuardedBy("mDozingLock")
public void startNotificationLogging() {
if (!mLogging) {
mLogging = true;
if (DEBUG) {
Log.i(TAG, "startNotificationLogging");
}
+ boolean lockscreen = mLockscreen != null && mLockscreen;
+ mNotificationPanelLogger.logPanelShown(lockscreen, getVisibleNotifications());
mListContainer.setChildLocationsChangedListener(this::onChildLocationsChanged);
- // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
- // cause the scroller to emit child location events. Hence generate
- // one ourselves to guarantee that we're reporting visible
+ // Sometimes, the transition from lockscreenOrShadeVisible=false ->
+ // lockscreenOrShadeVisible=true doesn't cause the scroller to emit child location
+ // events. Hence generate one ourselves to guarantee that we're reporting visible
// notifications.
// (Note that in cases where the scroller does emit events, this
// additional event doesn't break anything.)
@@ -274,13 +296,6 @@ public class NotificationLogger implements StateListener {
}
}
- private void setDozing(boolean dozing) {
- synchronized (mDozingLock) {
- mDozing = dozing;
- maybeUpdateLoggingStatus();
- }
- }
-
private void logNotificationVisibilityChanges(
Collection<NotificationVisibility> newlyVisible,
Collection<NotificationVisibility> noLongerVisible) {
@@ -362,39 +377,6 @@ public class NotificationLogger implements StateListener {
}
}
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (DEBUG) {
- Log.i(TAG, "onDozingChanged: new=" + isDozing);
- }
- setDozing(isDozing);
- }
-
- @GuardedBy("mDozingLock")
- private void maybeUpdateLoggingStatus() {
- if (mPanelExpanded == null || mDozing == null) {
- if (DEBUG) {
- Log.i(TAG, "Panel status unclear: panelExpandedKnown="
- + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null));
- }
- return;
- }
- // Once we know panelExpanded and Dozing, turn logging on & off when appropriate
- boolean lockscreen = mLockscreen == null ? false : mLockscreen;
- if (mPanelExpanded && !mDozing) {
- mNotificationPanelLogger.logPanelShown(lockscreen, getVisibleNotifications());
- if (DEBUG) {
- Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen);
- }
- startNotificationLogging();
- } else {
- if (DEBUG) {
- Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen);
- }
- stopNotificationLogging();
- }
- }
-
/**
* Called when the notification is expanded / collapsed.
*/
@@ -404,20 +386,6 @@ public class NotificationLogger implements StateListener {
}
@VisibleForTesting
- void onShadeExpansionFullyChanged(Boolean isExpanded) {
- // mPanelExpanded is initialized as null
- if (mPanelExpanded == null || !mPanelExpanded.equals(isExpanded)) {
- if (DEBUG) {
- Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
- }
- mPanelExpanded = isExpanded;
- synchronized (mDozingLock) {
- maybeUpdateLoggingStatus();
- }
- }
- }
-
- @VisibleForTesting
void onChildLocationsChanged() {
if (mHandler.hasCallbacks(mVisibilityReporter)) {
// Visibilities will be reported when the existing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6f79ea8c543b..44ead26de012 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -45,6 +45,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.notification.ConversationIconFactory;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -53,6 +54,7 @@ import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -69,6 +71,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
@@ -80,7 +83,7 @@ import javax.inject.Inject;
* closing guts, and keeping track of the currently exposed notification guts.
*/
@SysUISingleton
-public class NotificationGutsManager implements NotifGutsViewManager {
+public class NotificationGutsManager implements NotifGutsViewManager, CoreStartable {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -109,6 +112,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
private final Handler mMainHandler;
private final Handler mBgHandler;
+ private final JavaAdapter mJavaAdapter;
private final Optional<BubblesManager> mBubblesManagerOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
@@ -121,6 +125,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
private final UserContextProvider mContextTracker;
private final UiEventLogger mUiEventLogger;
private final ShadeController mShadeController;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private NotifGutsViewListener mGutsListener;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final ActivityStarter mActivityStarter;
@@ -129,6 +134,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
public NotificationGutsManager(Context context,
@Main Handler mainHandler,
@Background Handler bgHandler,
+ JavaAdapter javaAdapter,
AccessibilityManager accessibilityManager,
HighPriorityProvider highPriorityProvider,
INotificationManager notificationManager,
@@ -143,6 +149,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
ShadeController shadeController,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
NotificationLockscreenUserManager notificationLockscreenUserManager,
StatusBarStateController statusBarStateController,
DeviceProvisionedController deviceProvisionedController,
@@ -152,6 +159,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
mContext = context;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
+ mJavaAdapter = javaAdapter;
mAccessibilityManager = accessibilityManager;
mHighPriorityProvider = highPriorityProvider;
mNotificationManager = notificationManager;
@@ -166,6 +174,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mShadeController = shadeController;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mLockscreenUserManager = notificationLockscreenUserManager;
mStatusBarStateController = statusBarStateController;
mDeviceProvisionedController = deviceProvisionedController;
@@ -187,6 +196,25 @@ public class NotificationGutsManager implements NotifGutsViewManager {
mNotificationActivityStarter = notificationActivityStarter;
}
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible(),
+ this::onLockscreenShadeVisibilityChanged);
+ }
+
+ private void onLockscreenShadeVisibilityChanged(boolean visible) {
+ if (!visible) {
+ closeAndSaveGuts(
+ /* removeLeavebehind= */ true ,
+ /* force= */ true,
+ /* removeControls= */ true,
+ /* x= */ -1,
+ /* y= */ -1,
+ /* resetMenu= */ true);
+ }
+ }
+
public void onDensityOrFontScaleChanged(NotificationEntry entry) {
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
@@ -512,7 +540,7 @@ public class NotificationGutsManager implements NotifGutsViewManager {
mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
- if (resetMenu) {
+ if (resetMenu && mListContainer != null) {
mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 605b3d23be2d..42b29f8831e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -92,17 +92,12 @@ import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
-import android.window.BackEvent;
-import android.window.OnBackAnimationCallback;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
@@ -176,6 +171,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -221,7 +217,6 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -462,6 +457,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
private final PluginManager mPluginManager;
private final ShadeController mShadeController;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private final InitController mInitController;
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -489,13 +485,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private View mReportRejectedTouch;
private final NotificationGutsManager mGutsManager;
- private final NotificationLogger mNotificationLogger;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final KeyguardViewMediator mKeyguardViewMediator;
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
- private final boolean mAnimateBack;
private final FragmentService mFragmentService;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final WallpaperController mWallpaperController;
@@ -508,13 +502,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private CentralSurfacesComponent mCentralSurfacesComponent;
- /**
- * This keeps track of whether we have (or haven't) registered the predictive back callback.
- * Since we can have visible -> visible transitions, we need to avoid
- * double-registering (or double-unregistering) our callback.
- */
- private boolean mIsBackCallbackRegistered = false;
-
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -640,31 +627,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final InteractionJankMonitor mJankMonitor;
- /** Existing callback that handles back gesture invoked for the Shade. */
- private final OnBackInvokedCallback mOnBackInvokedCallback;
-
- /**
- * New callback that handles back gesture invoked, cancel, progress
- * and provides feedback via Shade animation.
- * (enabled via the WM_SHADE_ANIMATE_BACK_GESTURE flag)
- */
- private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() {
- @Override
- public void onBackInvoked() {
- mBackActionInteractor.onBackRequested();
- }
-
- @Override
- public void onBackProgressed(BackEvent event) {
- if (mBackActionInteractor.shouldBackBeHandled()) {
- if (mShadeSurface.canBeCollapsed()) {
- float fraction = event.getProgress();
- mShadeSurface.onBackProgressed(fraction);
- }
- }
- }
- };
-
/**
* Public constructor for CentralSurfaces.
*
@@ -694,7 +656,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
ShadeExpansionStateManager shadeExpansionStateManager,
KeyguardViewMediator keyguardViewMediator,
@@ -745,6 +706,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
Lazy<CentralSurfacesCommandQueueCallbacks> commandQueueCallbacksLazy,
PluginManager pluginManager,
ShadeController shadeController,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -803,7 +765,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mGutsManager = notificationGutsManager;
- mNotificationLogger = notificationLogger;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mShadeExpansionStateManager = shadeExpansionStateManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -856,6 +817,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mCommandQueueCallbacksLazy = commandQueueCallbacksLazy;
mPluginManager = pluginManager;
mShadeController = shadeController;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
mInitController = initController;
@@ -926,14 +888,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
}
- // Based on teamfood flag, enable predictive back animation for the Shade.
- mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
- mOnBackInvokedCallback = () -> {
- if (DEBUG) {
- Log.d(TAG, "mOnBackInvokedCallback() called");
- }
- mBackActionInteractor.onBackRequested();
- };
}
private void initBubbles(Bubbles bubbles) {
@@ -1171,7 +1125,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
new FoldStateListener(mContext, this::onFoldedStateChanged));
}
- @VisibleForTesting
+ /**
+ * @deprecated use {@link
+ * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
+ */ @VisibleForTesting
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
@@ -1563,6 +1520,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotifListContainer,
mStackScrollerController.getNotifStackController(),
mNotificationActivityStarter);
+ mWindowRootViewVisibilityInteractor.setUp(mPresenter, mNotificationsController);
}
/**
@@ -2149,82 +2107,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
}
- protected void handleVisibleToUserChanged(boolean visibleToUser) {
- if (visibleToUser) {
- onVisibleToUser();
- mNotificationLogger.startNotificationLogging();
-
- if (!mIsBackCallbackRegistered) {
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.getOnBackInvokedDispatcher()
- .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- mAnimateBack ? mOnBackAnimationCallback
- : mOnBackInvokedCallback);
- mIsBackCallbackRegistered = true;
- if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
- }
- } else {
- if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
- }
- } else {
- mNotificationLogger.stopNotificationLogging();
- onInvisibleToUser();
-
- if (mIsBackCallbackRegistered) {
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.getOnBackInvokedDispatcher()
- .unregisterOnBackInvokedCallback(
- mAnimateBack ? mOnBackAnimationCallback
- : mOnBackInvokedCallback);
- mIsBackCallbackRegistered = false;
- if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
- }
- } else {
- if (DEBUG) {
- Log.d(TAG,
- "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
- + "unregistered)");
- }
- }
- }
- }
-
- 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() {
boolean isShowing = mKeyguardStateController.isShowing();
boolean isOccluded = mKeyguardStateController.isOccluded();
@@ -2708,11 +2590,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNavigationBarController.showPinningEscapeToast(mDisplayId);
}
- protected ViewRootImpl getViewRootImpl() {
- View root = mNotificationShadeWindowController.getWindowRootView();
- if (root != null) return root.getViewRootImpl();
- return null;
- }
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -2759,7 +2636,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
- updateVisibleToUser();
updateNotificationPanelTouchState();
getNotificationShadeWindowViewController().cancelCurrentTouch();
@@ -2858,7 +2734,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
/* wakingUp= */ true,
mShouldDelayWakeUpAnimation);
- updateVisibleToUser();
updateIsKeyguard();
mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
&& mFeatureFlags.isEnabled(
@@ -3187,9 +3062,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected boolean mVisible;
- // mScreenOnFromKeyguard && mVisible.
- private boolean mVisibleToUser;
-
protected DevicePolicyManager mDevicePolicyManager;
private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3313,21 +3185,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (visible) {
DejankUtils.notifyRendererOfExpensiveFrame(
getNotificationShadeWindowView(), "onShadeVisibilityChanged");
- } else {
- mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
}
- updateVisibleToUser();
- }
-
- protected void updateVisibleToUser() {
- boolean oldVisibleToUser = mVisibleToUser;
- mVisibleToUser = mVisible && mDeviceInteractive;
-
- if (oldVisibleToUser != mVisibleToUser) {
- handleVisibleToUserChanged(mVisibleToUser);
- }
}
/**
@@ -3503,7 +3362,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// If we're visible and switched to SHADE_LOCKED (the user dragged
// down on the lockscreen), clear notification LED, vibration,
// ringing.
- // Other transitions are covered in handleVisibleToUserChanged().
+ // Other transitions are covered in WindowRootViewVisibilityInteractor.
if (mVisible && (newState == StatusBarState.SHADE_LOCKED
|| mStatusBarStateController.goingToFullShade())) {
clearNotificationEffects();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 88c710aedf93..ddb482f0623c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,18 +16,46 @@
package com.android.systemui.back.domain.interactor
+import android.view.ViewRootImpl
+import android.window.BackEvent
+import android.window.BackEvent.EDGE_LEFT
+import android.window.OnBackAnimationCallback
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import android.window.WindowOnBackInvokedDispatcher
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+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.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -41,7 +69,12 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class BackActionInteractorTest : SysuiTestCase() {
+ private val testScope = TestScope()
+ private val featureFlags = FakeFeatureFlags()
+ private val executor = FakeExecutor(FakeSystemClock())
+
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -49,18 +82,42 @@ class BackActionInteractorTest : SysuiTestCase() {
@Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var qsController: QuickSettingsController
@Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var windowRootView: WindowRootView
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
+ @Mock private lateinit var iStatusBarService: IStatusBarService
+ @Mock private lateinit var headsUpManager: HeadsUpManager
- private lateinit var backActionInteractor: BackActionInteractor
+ private val keyguardRepository = FakeKeyguardRepository()
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ WindowRootViewVisibilityRepository(iStatusBarService, executor),
+ keyguardRepository,
+ headsUpManager,
+ )
+ }
- @Before
- fun setup() {
- backActionInteractor =
- BackActionInteractor(
+ private val backActionInteractor: BackActionInteractor by lazy {
+ BackActionInteractor(
+ testScope.backgroundScope,
statusBarStateController,
statusBarKeyguardViewManager,
shadeController,
+ notificationShadeWindowController,
+ windowRootViewVisibilityInteractor,
+ featureFlags,
)
- backActionInteractor.setup(qsController, shadeViewController)
+ .apply { this.setup(qsController, shadeViewController) }
+ }
+
+ @Before
+ fun setUp() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
+ whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
+ whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
+ whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
}
@Test
@@ -117,4 +174,139 @@ class BackActionInteractorTest : SysuiTestCase() {
verify(statusBarKeyguardViewManager, never()).onBackPressed()
verify(shadeViewController, never()).animateCollapseQs(anyBoolean())
}
+
+ @Test
+ fun shadeVisibleAndDeviceAwake_callbackRegistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher)
+ .registerOnBackInvokedCallback(eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any())
+ }
+
+ @Test
+ fun noWindowRootView_noCrashAttemptingCallbackRegistration() {
+ whenever(notificationShadeWindowController.windowRootView).thenReturn(null)
+
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ testScope.runCurrent()
+ // No assert necessary, just testing no crash
+ }
+
+ @Test
+ fun shadeNotVisible_callbackUnregistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher).unregisterOnBackInvokedCallback(callback)
+ }
+
+ @Test
+ fun deviceAsleep_callbackUnregistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+
+ setWakefulness(WakefulnessState.ASLEEP)
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher).unregisterOnBackInvokedCallback(callback)
+ }
+
+ @Test
+ fun animationFlagOff_onBackInvoked_keyguardNotified() {
+ backActionInteractor.start()
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+ whenever(statusBarKeyguardViewManager.canHandleBackPressed()).thenReturn(true)
+
+ callback.onBackInvoked()
+
+ verify(statusBarKeyguardViewManager).onBackPressed()
+ }
+
+ @Test
+ fun animationFlagOn_onBackInvoked_keyguardNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+ whenever(statusBarKeyguardViewManager.canHandleBackPressed()).thenReturn(true)
+
+ callback.onBackInvoked()
+
+ verify(statusBarKeyguardViewManager).onBackPressed()
+ }
+
+ @Test
+ fun animationFlagOn_callbackIsAnimationCallback() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ val callback = getBackInvokedCallback()
+
+ assertThat(callback).isInstanceOf(OnBackAnimationCallback::class.java)
+ }
+
+ @Test
+ fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback() as OnBackAnimationCallback
+
+ whenever(shadeViewController.canBeCollapsed()).thenReturn(false)
+
+ callback.onBackProgressed(createBackEvent(0.3f))
+
+ verify(shadeViewController, never()).onBackProgressed(0.3f)
+ }
+
+ @Test
+ fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback() as OnBackAnimationCallback
+
+ whenever(shadeViewController.canBeCollapsed()).thenReturn(true)
+
+ callback.onBackProgressed(createBackEvent(0.4f))
+
+ verify(shadeViewController).onBackProgressed(0.4f)
+ }
+
+ private fun getBackInvokedCallback(): OnBackInvokedCallback {
+ testScope.runCurrent()
+ val captor = argumentCaptor<OnBackInvokedCallback>()
+ verify(onBackInvokedDispatcher).registerOnBackInvokedCallback(any(), captor.capture())
+ return captor.value!!
+ }
+
+ private fun createBackEvent(progress: Float): BackEvent =
+ BackEvent(/* touchX= */ 0f, /* touchY= */ 0f, progress, /* swipeEdge= */ EDGE_LEFT)
+
+ private fun setWakefulness(state: WakefulnessState) {
+ val model = WakefulnessModel(state, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ keyguardRepository.setWakefulnessModel(model)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 5ead16bdc10f..2691860bc25b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -46,6 +46,7 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -322,20 +323,20 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
val captor = argumentCaptor<StatusBarStateController.StateListener>()
runCurrent()
- verify(statusBarStateController).addCallback(captor.capture())
+ verify(statusBarStateController, atLeastOnce()).addCallback(captor.capture())
- captor.value.onDozeAmountChanged(0.433f, 0.4f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.433f, 0.4f) }
runCurrent()
- captor.value.onDozeAmountChanged(0.498f, 0.5f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.498f, 0.5f) }
runCurrent()
- captor.value.onDozeAmountChanged(0.661f, 0.65f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.661f, 0.65f) }
runCurrent()
assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
runCurrent()
- verify(statusBarStateController).removeCallback(captor.value)
+ verify(statusBarStateController).removeCallback(any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
new file mode 100644
index 000000000000..2187f3657848
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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.scene.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class WindowRootViewVisibilityRepositoryTest : SysuiTestCase() {
+ private val iStatusBarService = mock<IStatusBarService>()
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val underTest = WindowRootViewVisibilityRepository(iStatusBarService, executor)
+
+ @Test
+ fun isLockscreenOrShadeVisible_true() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_false() {
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun onLockscreenOrShadeInteractive_statusBarServiceNotified() {
+ underTest.onLockscreenOrShadeInteractive(
+ shouldClearNotificationEffects = true,
+ notificationCount = 3,
+ )
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelRevealed(true, 3)
+ }
+
+ @Test
+ fun onLockscreenOrShadeNotInteractive_statusBarServiceNotified() {
+ underTest.onLockscreenOrShadeNotInteractive()
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelHidden()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
new file mode 100644
index 000000000000..f304435b7a7a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 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.scene.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+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.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 com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+ private val iStatusBarService = mock<IStatusBarService>()
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val windowRootViewVisibilityRepository =
+ WindowRootViewVisibilityRepository(iStatusBarService, executor)
+ private val keyguardRepository = FakeKeyguardRepository()
+ private val headsUpManager = mock<HeadsUpManager>()
+ private val notificationPresenter = mock<NotificationPresenter>()
+ private val notificationsController = mock<NotificationsController>()
+
+ private val underTest =
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ windowRootViewVisibilityRepository,
+ keyguardRepository,
+ headsUpManager,
+ )
+ .apply { setUp(notificationPresenter, notificationsController) }
+
+ @Test
+ fun isLockscreenOrShadeVisible_true() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_false() {
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_matchesRepo() {
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_notVisible_false() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_deviceAsleep_false() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ setWakefulness(WakefulnessState.ASLEEP)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndAwake_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndStartingToWake_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.STARTING_TO_WAKE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndStartingToSleep_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.STARTING_TO_SLEEP)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_statusBarServiceNotified() =
+ testScope.runTest {
+ underTest.start()
+
+ makeLockscreenShadeVisible()
+ testScope.runCurrent()
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelRevealed(any(), any())
+ }
+
+ @Test
+ fun lockscreenShadeNotInteractive_statusBarServiceNotified() =
+ testScope.runTest {
+ underTest.start()
+
+ // First, make the shade visible
+ makeLockscreenShadeVisible()
+ testScope.runCurrent()
+ reset(iStatusBarService)
+
+ // WHEN lockscreen or shade is no longer visible
+ underTest.setIsLockscreenOrShadeVisible(false)
+ testScope.runCurrent()
+ executor.runAllReady()
+
+ // THEN status bar service is notified
+ verify(iStatusBarService).onPanelHidden()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_presenterCollapsed_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_nullPresenter_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ underTest.setUp(presenter = null, notificationsController)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateKeyguard_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateShade_presenterNotCollapsed_notifEffectsCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateShadeLocked_presenterNotCollapsed_notifEffectsCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_hasHeadsUpAndNotifPresenterCollapsed_notifCountOne() =
+ testScope.runTest {
+ underTest.start()
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(4)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_hasHeadsUpAndNullPresenter_notifCountOne() =
+ testScope.runTest {
+ underTest.start()
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ underTest.setUp(presenter = null, notificationsController)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_noHeadsUp_notifCountMatchesNotifController() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(9)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(9)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_notifPresenterNotCollapsed_notifCountMatchesNotifController() =
+ testScope.runTest {
+ underTest.start()
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(8)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(8)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_noHeadsUp_noNotifController_notifCountZero() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.setUp(notificationPresenter, notificationsController = null)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(0)
+ }
+
+ private fun makeLockscreenShadeVisible() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ testScope.runCurrent()
+ executor.runAllReady()
+ }
+
+ private fun setWakefulness(state: WakefulnessState) {
+ val model = WakefulnessModel(state, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ keyguardRepository.setWakefulnessModel(model)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 6a14a005b0f0..bf2d6a6f2f16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -20,20 +20,28 @@ import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.WindowManager
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.keyguard.TestScopeProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.assist.AssistManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import org.junit.Before
import org.junit.Test
@@ -47,6 +55,8 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
+ private val executor = FakeExecutor(FakeSystemClock())
+
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -61,6 +71,17 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@Mock private lateinit var touchLog: LogBuffer
+ @Mock private lateinit var iStatusBarService: IStatusBarService
+ @Mock private lateinit var headsUpManager: HeadsUpManager
+
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
+ WindowRootViewVisibilityInteractor(
+ TestScopeProvider.getTestScope(),
+ WindowRootViewVisibilityRepository(iStatusBarService, executor),
+ FakeKeyguardRepository(),
+ headsUpManager,
+ )
+ }
private lateinit var shadeController: ShadeControllerImpl
@@ -74,6 +95,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
commandQueue,
FakeExecutor(FakeSystemClock()),
touchLog,
+ windowRootViewVisibilityInteractor,
keyguardStateController,
statusBarStateController,
statusBarKeyguardViewManager,
@@ -86,6 +108,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
Lazy { gutsManager },
)
shadeController.setNotificationShadeWindowViewController(nswvc)
+ shadeController.setVisibilityListener(mock())
}
@Test
@@ -133,4 +156,24 @@ class ShadeControllerImplTest : SysuiTestCase() {
// VERIFY that cancelCurrentTouch is NOT called
verify(nswvc, never()).cancelCurrentTouch()
}
+
+ @Test
+ fun visible_changesToTrue_windowInteractorUpdated() {
+ shadeController.makeExpandedVisible(true)
+
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun visible_changesToFalse_windowInteractorUpdated() {
+ // GIVEN the shade is currently expanded
+ shadeController.makeExpandedVisible(true)
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isTrue()
+
+ // WHEN the shade is collapsed
+ shadeController.collapseShade()
+
+ // THEN the interactor is notified
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 7117c233c8c5..bbf0151a90ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -40,8 +40,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+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.scene.data.repository.WindowRootViewVisibilityRepository;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -54,7 +60,9 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.google.android.collect.Lists;
@@ -71,6 +79,8 @@ import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -89,7 +99,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
- @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
@@ -97,12 +107,23 @@ public class NotificationLoggerTest extends SysuiTestCase {
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationPanelLoggerFake mNotificationPanelLoggerFake =
new NotificationPanelLoggerFake();
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+ private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries);
+ mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
+ mTestScope.getBackgroundScope(),
+ new WindowRootViewVisibilityRepository(mBarService, mUiBgExecutor),
+ mKeyguardRepository,
+ mHeadsUpManager);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
@@ -120,10 +141,12 @@ public class NotificationLoggerTest extends SysuiTestCase {
mVisibilityProvider,
mNotifPipeline,
mock(StatusBarStateControllerImpl.class),
- mShadeExpansionStateManager,
+ mWindowRootViewVisibilityInteractor,
+ mJavaAdapter,
mBarService,
mExpansionStateLogger
);
+ mLogger.start();
mLogger.setUpWithContainer(mListContainer);
verify(mNotifPipeline).addCollectionListener(any());
}
@@ -183,31 +206,26 @@ public class NotificationLoggerTest extends SysuiTestCase {
Mockito.reset(mBarService);
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
- mLogger.onDozingChanged(true); // And go back to sleep, turning off logging
+
+ setStateAwake(); // Wake to lockscreen
+
+ setStateAsleep(); // And go back to sleep, turning off logging
mUiBgExecutor.runAllReady();
+
// The visibility objects are recycled by NotificationLogger, so we can't use specific
// matchers here.
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
}
- private void setStateAsleep() {
- mLogger.onShadeExpansionFullyChanged(true);
- mLogger.onDozingChanged(true);
- mLogger.onStateChanged(StatusBarState.KEYGUARD);
- }
-
- private void setStateAwake() {
- mLogger.onShadeExpansionFullyChanged(false);
- mLogger.onDozingChanged(false);
- mLogger.onStateChanged(StatusBarState.SHADE);
- }
-
@Test
- public void testLogPanelShownOnWake() {
+ public void testLogPanelShownOnWakeToLockscreen() {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
+
+ // Wake to lockscreen
+ mLogger.onStateChanged(StatusBarState.KEYGUARD);
+ setStateAwake();
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -222,9 +240,14 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Test
public void testLogPanelShownOnShadePull() {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
+ // Start as awake, but with the panel not visible
setStateAwake();
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+
// Now expand panel
- mLogger.onShadeExpansionFullyChanged(true);
+ mLogger.onStateChanged(StatusBarState.SHADE);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -251,13 +274,34 @@ public class NotificationLoggerTest extends SysuiTestCase {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
+
+ // Wake to lockscreen
+ setStateAwake();
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
assertEquals(0, n.instanceId);
}
+ private void setStateAsleep() {
+ mKeyguardRepository.setWakefulnessModel(
+ new WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER));
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
+ private void setStateAwake() {
+ mKeyguardRepository.setWakefulnessModel(
+ new WakefulnessModel(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER));
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
private class TestableNotificationLogger extends NotificationLogger {
TestableNotificationLogger(NotificationListener notificationListener,
@@ -266,7 +310,8 @@ public class NotificationLoggerTest extends SysuiTestCase {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
super(
@@ -276,7 +321,8 @@ public class NotificationLoggerTest extends SysuiTestCase {
visibilityProvider,
notifPipeline,
statusBarStateController,
- shadeExpansionStateManager,
+ windowRootViewVisibilityInteractor,
+ javaAdapter,
expansionStateLogger,
mNotificationPanelLoggerFake
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 705d52bcf13f..9e0f83c9fc53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -66,11 +67,16 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -84,6 +90,9 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
@@ -97,6 +106,8 @@ import org.mockito.junit.MockitoRule;
import java.util.Optional;
+import kotlinx.coroutines.test.TestScope;
+
/**
* Tests for {@link NotificationGutsManager}.
*/
@@ -108,6 +119,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+
+ private TestScope mTestScope = TestScopeProvider.getTestScope();
+ private JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private TestableLooper mTestableLooper;
private Handler mHandler;
private NotificationTestHelper mHelper;
@@ -124,6 +139,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private AccessibilityManager mAccessibilityManager;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private INotificationManager mINotificationManager;
+ @Mock private IStatusBarService mBarService;
@Mock private LauncherApps mLauncherApps;
@Mock private ShortcutManager mShortcutManager;
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@@ -140,6 +156,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private UserManager mUserManager;
+ private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
@@ -148,21 +166,42 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mGutsManager = new NotificationGutsManager(mContext, mHandler, mHandler,
+ mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
+ mTestScope.getBackgroundScope(),
+ new WindowRootViewVisibilityRepository(mBarService, mExecutor),
+ new FakeKeyguardRepository(),
+ mHeadsUpManagerPhone);
+
+ mGutsManager = new NotificationGutsManager(
+ mContext,
+ mHandler,
+ mHandler,
+ mJavaAdapter,
mAccessibilityManager,
- mHighPriorityProvider, mINotificationManager, mUserManager,
- mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
- mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
- Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
+ mHighPriorityProvider,
+ mINotificationManager,
+ mUserManager,
+ mPeopleSpaceWidgetManager,
+ mLauncherApps,
+ mShortcutManager,
+ mChannelEditorDialogController,
+ mContextTracker,
+ mAssistantFeedbackController,
+ Optional.of(mBubblesManager),
+ new UiEventLoggerFake(),
+ mOnUserInteractionCallback,
mShadeController,
+ mWindowRootViewVisibilityInteractor,
mNotificationLockscreenUserManager,
mStatusBarStateController,
mDeviceProvisionedController,
mMetricsLogger,
- mHeadsUpManagerPhone, mActivityStarter);
+ mHeadsUpManagerPhone,
+ mActivityStarter);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mGutsManager.start();
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -210,6 +249,62 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
}
@Test
+ public void testLockscreenShadeVisible_visible_gutsNotClosed() {
+ // First, start out lockscreen or shade as not visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ NotificationGuts guts = mock(NotificationGuts.class);
+ mGutsManager.setExposedGuts(guts);
+
+ // WHEN the lockscreen or shade becomes visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the guts are not closed
+ verify(guts, never()).removeCallbacks(any());
+ verify(guts, never()).closeControls(
+ anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testLockscreenShadeVisible_notVisible_gutsClosed() {
+ // First, start out lockscreen or shade as visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ NotificationGuts guts = mock(NotificationGuts.class);
+ mGutsManager.setExposedGuts(guts);
+
+ // WHEN the lockscreen or shade is no longer visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(any());
+ verify(guts).closeControls(
+ /* leavebehinds= */ eq(true),
+ /* controls= */ eq(true),
+ /* x= */ anyInt(),
+ /* y= */ anyInt(),
+ /* force= */ eq(true));
+ }
+
+ @Test
+ public void testLockscreenShadeVisible_notVisible_listContainerReset() {
+ // First, start out lockscreen or shade as visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // WHEN the lockscreen or shade is no longer visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the list container is reset
+ verify(mNotificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testChangeDensityOrFontScale() {
NotificationGuts guts = spy(new NotificationGuts(mContext));
when(guts.post(any())).thenAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index f47efe3f79c4..5a1450f1c57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -62,7 +61,6 @@ import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.service.dreams.IDreamManager;
import android.support.test.metricshelper.MetricsAsserts;
@@ -73,13 +71,7 @@ import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
-import android.window.BackEvent;
-import android.window.OnBackAnimationCallback;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
-import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -125,6 +117,7 @@ import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
@@ -158,7 +151,6 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -167,10 +159,7 @@ import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
@@ -199,7 +188,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -227,7 +215,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private NotificationStackScrollLayoutController mStackScrollerController;
- @Mock private NotificationListContainer mNotificationListContainer;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private ShadeLogger mShadeLogger;
@@ -254,7 +241,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private StatusBarSignalPolicy mStatusBarSignalPolicy;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@@ -325,18 +311,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@Mock private CameraLauncher mCameraLauncher;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
- /**
- * The process of registering/unregistering a predictive back callback requires a
- * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
- * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
- */
- @Mock private ViewRootImpl mViewRootImpl;
- @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Mock private UserTracker mUserTracker;
@Mock private FingerprintManager mFingerprintManager;
- @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@Mock IPowerManager mPowerManagerService;
@Mock ActivityStarter mActivityStarter;
+ @Mock private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -388,18 +367,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mMetricsLogger = new FakeMetricsLogger();
- NotificationLogger notificationLogger = new NotificationLogger(
- mNotificationListener,
- mUiBgExecutor,
- mNotifLiveDataStore,
- mVisibilityProvider,
- mock(NotifPipeline.class),
- mStatusBarStateController,
- mShadeExpansionStateManager,
- mExpansionStateLogger,
- new NotificationPanelLoggerFake()
- );
- notificationLogger.setVisibilityReporter(mock(Runnable.class));
when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -448,6 +415,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCommandQueue,
mMainExecutor,
mock(LogBuffer.class),
+ mock(WindowRootViewVisibilityInteractor.class),
mKeyguardStateController,
mStatusBarStateController,
mStatusBarKeyguardViewManager,
@@ -490,7 +458,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
new FalsingCollectorFake(),
mBroadcastDispatcher,
mNotificationGutsManager,
- notificationLogger,
mNotificationInterruptStateProvider,
new ShadeExpansionStateManager(),
mKeyguardViewMediator,
@@ -541,6 +508,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
() -> mCentralSurfacesCommandQueueCallbacks,
mPluginManager,
mShadeController,
+ mWindowRootViewVisibilityInteractor,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
mInitController,
@@ -578,16 +546,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mUserTracker,
() -> mFingerprintManager,
mActivityStarter
- ) {
- @Override
- protected ViewRootImpl getViewRootImpl() {
- return mViewRootImpl;
- }
- };
+ );
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
- when(mViewRootImpl.getOnBackInvokedDispatcher())
- .thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
@@ -609,7 +570,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock");
mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
- notificationLogger.setUpWithContainer(mNotificationListContainer);
mCentralSurfaces.registerCallbacks();
}
@@ -799,151 +759,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- public void testLogHidden() {
- try {
- mCentralSurfaces.handleVisibleToUserChanged(false);
- mUiBgExecutor.runAllReady();
- verify(mBarService, times(1)).onPanelHidden();
- verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
- } catch (RemoteException e) {
- fail();
- }
- }
-
- /**
- * Do the following:
- * 1. verify that a predictive back callback is registered when CSurf becomes visible
- * 2. verify that the same callback is unregistered when CSurf becomes invisible
- */
- @Test
- public void testPredictiveBackCallback_registration() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- mCentralSurfaces.handleVisibleToUserChanged(false);
- verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
- eq(mOnBackInvokedCallback.getValue()));
- }
-
- /**
- * Do the following:
- * 1. capture the predictive back callback during registration
- * 2. call the callback directly
- * 3. verify that the ShadeController's panel collapse animation is invoked
- */
- @Test
- public void testPredictiveBackCallback_invocationCollapsesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- mOnBackInvokedCallback.getValue().onBackInvoked();
- verify(mBackActionInteractor).onBackRequested();
- }
-
- /**
- * When back progress is at 100%, the onBackProgressed animation driver inside
- * NotificationPanelViewController should be invoked appropriately (with 1.0f passed in).
- */
- @Test
- public void testPredictiveBackAnimation_progressMaxScalesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- OnBackAnimationCallback onBackAnimationCallback =
- (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true);
-
- BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT);
- onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
- verify(mNotificationPanelViewController).onBackProgressed(eq(1.0f));
- }
-
- /**
- * When back progress is at 0%, the onBackProgressed animation driver inside
- * NotificationPanelViewController should be invoked appropriately (with 0.0f passed in).
- */
- @Test
- public void testPredictiveBackAnimation_progressMinScalesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- OnBackAnimationCallback onBackAnimationCallback =
- (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true);
-
- BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT);
- onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
- verify(mNotificationPanelViewController).onBackProgressed(eq(0.0f));
- }
-
- @Test
- public void testPanelOpenForHeadsUp() {
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
- mCentralSurfaces.setBarStateForTest(SHADE);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(false, 1);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
- public void testPanelOpenAndClear() {
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
-
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mCentralSurfaces.setBarStateForTest(SHADE);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(true, 5);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
- public void testPanelOpenAndNoClear() {
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(false, 5);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
public void testDump_DoesNotCrash() {
mCentralSurfaces.dump(new PrintWriter(new ByteArrayOutputStream()), null);
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index faebcaac1be3..cc0c943bdbbe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -81,7 +81,7 @@ class FakeKeyguardRepository : KeyguardRepository {
override val linearDozeAmount: Flow<Float> = _dozeAmount
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
- override val statusBarState: Flow<StatusBarState> = _statusBarState
+ override val statusBarState: StateFlow<StatusBarState> = _statusBarState
private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel