Use transitions for shade window occluded state
There is a timing issue that shows up with activity
launch animations. If keyguard occluded state is
changed in the middle of a launch animation, a flicker
can occur, sometimes pretty bad like currently
lauching the emergency call UI over SIM bouncer. Use
transition FINISHED state as a more reliable method
to send state over to the shade window.
Fixes: 344716537
Test: atest NotificationShadeWindowModelTest
Test: manual repeatedly launch emergency call UI
over SIM bouncer
Flag: com.android.systemui.use_transitions_for_keyguard_occluded
Change-Id: I14e1c3478fb76c3a9459c3a8fd0d8e4587962089
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1f1495a..0d337eb 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1263,6 +1263,16 @@
}
flag {
+ name: "use_transitions_for_keyguard_occluded"
+ namespace: "systemui"
+ description: "Use Keyguard Transitions to set Notification Shade occlusion state"
+ bug: "344716537"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "lockscreen_preview_renderer_create_on_main_thread"
namespace: "systemui"
description: "Force preview renderer to be created on the main thread"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 4a7b887..34fbcac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -60,6 +60,7 @@
import com.android.systemui.res.R;
import com.android.systemui.scene.FakeWindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -106,6 +107,7 @@
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private UserTracker mUserTracker;
+ @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
@@ -161,6 +163,7 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
+ mNotificationShadeWindowModel,
mKosmos::getCommunalInteractor) {
@Override
protected boolean isDebuggable() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
new file mode 100644
index 0000000..add33da
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationShadeWindowModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val underTest: NotificationShadeWindowModel by lazy {
+ kosmos.notificationShadeWindowModel
+ }
+
+ @Test
+ fun transitionToOccluded() =
+ testScope.runTest {
+ val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
+ assertThat(isKeyguardOccluded).isFalse()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isTrue()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isFalse()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index e5ccc4a..2a8bb47 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -220,7 +220,10 @@
interpolator = Interpolators.LINEAR
duration =
when (toState) {
+ KeyguardState.GONE -> TO_GONE_DURATION
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+ KeyguardState.PRIMARY_BOUNCER -> TO_PRIMARY_BOUNCER_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -229,9 +232,9 @@
companion object {
private const val TAG = "FromAodTransitionInteractor"
private val DEFAULT_DURATION = 500.milliseconds
- val TO_LOCKSCREEN_DURATION = 500.milliseconds
val TO_GONE_DURATION = DEFAULT_DURATION
- val TO_OCCLUDED_DURATION = DEFAULT_DURATION
+ val TO_LOCKSCREEN_DURATION = 500.milliseconds
+ val TO_OCCLUDED_DURATION = 550.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 8ef138e..61446c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -302,16 +302,25 @@
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
- duration = DEFAULT_DURATION.inWholeMilliseconds
+ duration =
+ when (toState) {
+ KeyguardState.GONE -> TO_GONE_DURATION
+ KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
+ KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+ KeyguardState.PRIMARY_BOUNCER -> TO_PRIMARY_BOUNCER_DURATION
+ else -> DEFAULT_DURATION
+ }.inWholeMilliseconds
}
}
companion object {
const val TAG = "FromDozingTransitionInteractor"
private val DEFAULT_DURATION = 500.milliseconds
- val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
- val TO_GONE_DURATION = DEFAULT_DURATION
- val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
+ val TO_GONE_DURATION = DEFAULT_DURATION
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
+ val TO_OCCLUDED_DURATION = 550.milliseconds
+ val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 206bbc5..51d92f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -393,7 +393,7 @@
val TO_DOZING_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
- val TO_OCCLUDED_DURATION = 450.milliseconds
+ val TO_OCCLUDED_DURATION = 550.milliseconds
val TO_AOD_DURATION = 500.milliseconds
val TO_AOD_FOLD_DURATION = 1100.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 04ea37e..2823b93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -245,6 +245,7 @@
KeyguardState.GONE -> TO_GONE_DURATION
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
+ KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -257,6 +258,7 @@
val TO_GONE_DURATION = 500.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
val TO_LOCKSCREEN_DURATION = 450.milliseconds
+ val TO_OCCLUDED_DURATION = 550.milliseconds
val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index b60c193..7bb31e6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -47,6 +47,7 @@
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
@@ -63,6 +64,7 @@
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -130,6 +132,7 @@
mCallbacks = new ArrayList<>();
private final SysuiColorExtractor mColorExtractor;
+ private final NotificationShadeWindowModel mNotificationShadeWindowModel;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
@@ -162,6 +165,7 @@
ShadeWindowLogger logger,
Lazy<SelectedUserInteractor> userInteractor,
UserTracker userTracker,
+ NotificationShadeWindowModel notificationShadeWindowModel,
Lazy<CommunalInteractor> communalInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
@@ -176,6 +180,7 @@
mKeyguardBypassController = keyguardBypassController;
mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
+ mNotificationShadeWindowModel = notificationShadeWindowModel;
// prefix with {slow} to make sure this dumps at the END of the critical section.
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
@@ -329,6 +334,14 @@
mCommunalInteractor.get().isCommunalVisible(),
this::onCommunalVisibleChanged
);
+
+ if (!SceneContainerFlag.isEnabled() && Flags.useTransitionsForKeyguardOccluded()) {
+ collectFlow(
+ mWindowRootView,
+ mNotificationShadeWindowModel.isKeyguardOccluded(),
+ this::setKeyguardOccluded
+ );
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
new file mode 100644
index 0000000..e1289af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the shade window. */
+@SysUISingleton
+class NotificationShadeWindowModel
+@Inject
+constructor(
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+ val isKeyguardOccluded: Flow<Boolean> =
+ keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 88a2b23..0b8f18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1114,7 +1114,9 @@
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
final Runnable postCollapseAction = () -> {
- mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+ if (!Flags.useTransitionsForKeyguardOccluded()) {
+ mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+ }
reset(true /* hideBouncerWhenShowing */);
};
if (mCentralSurfaces.isDismissingShadeForActivityLaunch()) {
@@ -1130,7 +1132,9 @@
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
- mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+ if (!Flags.useTransitionsForKeyguardOccluded()) {
+ mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+ }
// setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
// no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 9de7528..ff65887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -111,6 +111,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeWindowLogger;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -225,6 +226,7 @@
private @Mock DreamViewModel mDreamViewModel;
private @Mock CommunalTransitionViewModel mCommunalTransitionViewModel;
private @Mock SystemPropertiesHelper mSystemPropertiesHelper;
+ @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
private FakeFeatureFlags mFeatureFlags;
private final int mDefaultUserId = 100;
@@ -272,6 +274,7 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
+ mNotificationShadeWindowModel,
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 2457eb7..7f964d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -123,6 +123,7 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeWindowLogger;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationEntryHelper;
@@ -348,6 +349,7 @@
private Display mDefaultDisplay;
@Mock
private Lazy<ViewCapture> mLazyViewCapture;
+ @Mock private NotificationShadeWindowModel mNotificationShadeWindowModel;
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private ShadeInteractor mShadeInteractor;
@@ -431,6 +433,7 @@
mShadeWindowLogger,
() -> mSelectedUserInteractor,
mUserTracker,
+ mNotificationShadeWindowModel,
mKosmos::getCommunalInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
new file mode 100644
index 0000000..cd4fab8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
+ Kosmos.Fixture { NotificationShadeWindowModel(keyguardTransitionInteractor) }