From cc70765f6af2a154565d49353642e6ed0f8d0a7c Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Thu, 15 Jun 2023 13:00:11 -0400 Subject: Update EmptyShadeView during GONE->AOD transition Internal changes to keyguard have caused our call to NSSLC#updateShowEmptyShadeView to happen *before* keyguard states have been updated for the GONE->AOD transition. This is due to an implicit assumption that by the time our unrelated call to updateShowEmptyShadeView occurs, the transition state is already set. This is fixed by explcitly listening for the GONE->AOD transition and calling updateShowEmptyShadeView when it starts and ends. Fixes: 286209517 Test: atest NotificationStackScrollLayoutControllerTest Change-Id: Ie81ea7f95fa42294fb32cffd305f840ca9c1c9e3 --- .../keyguard/shared/model/TransitionStep.kt | 4 ++- .../NotificationStackScrollLayoutController.java | 26 +++++++++++++-- ...otificationStackScrollLayoutControllerTest.java | 38 ++++++++++++++++++++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt index 767fd58f78e9..0fa6f4fa4f0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt @@ -16,7 +16,9 @@ package com.android.systemui.keyguard.shared.model /** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */ -data class TransitionStep( +data class TransitionStep +@JvmOverloads +constructor( val from: KeyguardState = KeyguardState.OFF, val to: KeyguardState = KeyguardState.OFF, val value: Float = 0f, // constrained [0.0, 1.0] diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ad7cdc4eb50f..45d5e2b0ece6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -29,6 +29,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows; import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; import android.content.res.Resources; @@ -63,7 +64,10 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.shared.model.KeyguardState; +import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -189,6 +193,7 @@ public class NotificationStackScrollLayoutController { private final GroupExpansionManager mGroupExpansionManager; private final NotifPipelineFlags mNotifPipelineFlags; private final SeenNotificationsProvider mSeenNotificationsProvider; + private final KeyguardTransitionRepository mKeyguardTransitionRepo; private NotificationStackScrollLayout mView; private NotificationSwipeHelper mSwipeHelper; @@ -197,6 +202,8 @@ public class NotificationStackScrollLayoutController { private int mBarState; private boolean mIsBouncerShowingFromCentralSurfaces; private HeadsUpAppearanceController mHeadsUpAppearanceController; + private boolean mIsInTransitionToAod = false; + private final FeatureFlags mFeatureFlags; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; @@ -633,6 +640,7 @@ public class NotificationStackScrollLayoutController { KeyguardBypassController keyguardBypassController, KeyguardInteractor keyguardInteractor, PrimaryBouncerInteractor primaryBouncerInteractor, + KeyguardTransitionRepository keyguardTransitionRepo, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, Optional nsslViewModel, @@ -665,6 +673,7 @@ public class NotificationStackScrollLayoutController { NotificationDismissibilityProvider dismissibilityProvider, ActivityStarter activityStarter) { mView = view; + mKeyguardTransitionRepo = keyguardTransitionRepo; mStackStateLogger = stackLogger; mLogger = logger; mAllowLongPress = allowLongPress; @@ -819,6 +828,9 @@ public class NotificationStackScrollLayoutController { mViewModel.ifPresent( vm -> NotificationListViewBinder .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController)); + + collectFlow(mView, mKeyguardTransitionRepo.getTransitions(), + this::onKeyguardTransitionChanged); } private boolean isInVisibleLocation(NotificationEntry entry) { @@ -1241,10 +1253,10 @@ public class NotificationStackScrollLayoutController { final boolean shouldShow = getVisibleNotificationCount() == 0 && !mView.isQsFullScreen() - // Hide empty shade view when in transition to Keyguard. + // Hide empty shade view when in transition to AOD. // That avoids "No Notifications" to blink when transitioning to AOD. // For more details, see: b/228790482 - && !isInTransitionToKeyguard() + && !mIsInTransitionToAod // Don't show any notification content if the bouncer is showing. See b/267060171. && !isBouncerShowing(); @@ -1643,6 +1655,16 @@ public class NotificationStackScrollLayoutController { return shelf == null ? 0 : shelf.getIntrinsicHeight(); } + @VisibleForTesting + void onKeyguardTransitionChanged(TransitionStep transitionStep) { + boolean isTransitionToAod = transitionStep.getFrom().equals(KeyguardState.GONE) + && transitionStep.getTo().equals(KeyguardState.AOD); + if (mIsInTransitionToAod != isTransitionToAod) { + mIsInTransitionToAod = isTransitionToAod; + updateShowEmptyShadeView(); + } + } + /** * Enum for UiEvent logged from this class */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index bb9937bc743d..6ae7dca296c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -32,10 +33,14 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import android.content.res.Resources; import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.View; +import android.view.ViewTreeObserver; import androidx.test.filters.SmallTest; @@ -49,8 +54,12 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.shared.model.KeyguardState; +import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -105,6 +114,7 @@ import java.util.Optional; * Tests for {@link NotificationStackScrollLayoutController}. */ @SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @@ -151,6 +161,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock private NotificationIconAreaController mIconAreaController; @Mock private ActivityStarter mActivityStarter; + @Mock private KeyguardTransitionRepository mKeyguardTransitionRepo; @Captor private ArgumentCaptor mStateListenerArgumentCaptor; @@ -162,11 +173,13 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Before public void setUp() { + allowTestableLooperAsMainThread(); MockitoAnnotations.initMocks(this); mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false); when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper); + when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow()); } @Test @@ -572,17 +585,33 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } + @Test + public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() { + initController(/* viewIsAttached= */ true); + mController.onKeyguardTransitionChanged( + new TransitionStep( + /* from= */ KeyguardState.GONE, + /* to= */ KeyguardState.AOD)); + verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean()); + } + private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } private void setupShowEmptyShadeViewState(boolean toShow) { if (toShow) { - when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(SHADE); + mController.onKeyguardTransitionChanged( + new TransitionStep( + /* from= */ KeyguardState.LOCKSCREEN, + /* to= */ KeyguardState.GONE)); mController.setQsFullScreen(false); mController.getView().removeAllViews(); } else { - when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); + mController.onKeyguardTransitionChanged( + new TransitionStep( + /* from= */ KeyguardState.GONE, + /* to= */ KeyguardState.AOD)); mController.setQsFullScreen(true); mController.getView().addContainerView(mock(ExpandableNotificationRow.class)); } @@ -590,7 +619,9 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { private void initController(boolean viewIsAttached) { when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(viewIsAttached); - + ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class); + when(mNotificationStackScrollLayout.getViewTreeObserver()) + .thenReturn(viewTreeObserver); mController = new NotificationStackScrollLayoutController( mNotificationStackScrollLayout, true, @@ -608,6 +639,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mKeyguardBypassController, mKeyguardInteractor, mPrimaryBouncerInteractor, + mKeyguardTransitionRepo, mZenModeController, mNotificationLockscreenUserManager, Optional.empty(), -- cgit v1.2.3-59-g8ed1b