From ea2010c1ec91ba33439bec6fa02286ce48293c50 Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 4 Nov 2019 09:55:29 -0500 Subject: [PATCH] LightsOut has its own controller Apps can request a low profile mode where status bar and navigation icons dim. In this mode, a notification dot appears where the notification icons would appear if they would be there outside of this mode. This CL moves the logic for this dot to its own controller Test: manual (ie: NY times app) Test: atest LightsOutNotifControllerTest Change-Id: I46dfab2c7f83cc26c25c257ee02d84058b127036 --- .../systemui/statusbar/car/CarStatusBar.java | 3 + .../statusbar/car/CarStatusBarModule.java | 3 + .../phone/LightsOutNotifController.java | 179 +++++++++++++ .../statusbar/phone/ShadeController.java | 6 - .../systemui/statusbar/phone/StatusBar.java | 54 +--- .../statusbar/phone/StatusBarModule.java | 2 + .../phone/StatusBarNotificationPresenter.java | 15 +- .../phone/LightsOutNotifControllerTest.java | 251 ++++++++++++++++++ .../statusbar/phone/StatusBarTest.java | 2 + 9 files changed, 448 insertions(+), 67 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index ab63b06cffa9f..c1515fc30b608 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -116,6 +116,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -306,6 +307,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt RemoteInputUriController remoteInputUriController, Optional dividerOptional, SuperStatusBarViewFactory superStatusBarViewFactory, + LightsOutNotifController lightsOutNotifController, /* Car Settings injected components. */ CarNavigationBarController carNavigationBarController) { super( @@ -376,6 +378,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt pluginManager, remoteInputUriController, dividerOptional, + lightsOutNotifController, superStatusBarViewFactory); mScrimController = scrimController; mCarNavigationBarController = carNavigationBarController; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 7de2212eef4d2..9b91843576cec 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -71,6 +71,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -175,6 +176,7 @@ public class CarStatusBarModule { RemoteInputUriController remoteInputUriController, Optional dividerOptional, SuperStatusBarViewFactory superStatusBarViewFactory, + LightsOutNotifController lightsOutNotifController, CarNavigationBarController carNavigationBarController) { return new CarStatusBar( context, @@ -244,6 +246,7 @@ public class CarStatusBarModule { remoteInputUriController, dividerOptional, superStatusBarViewFactory, + lightsOutNotifController, carNavigationBarController); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java new file mode 100644 index 0000000000000..93887a6617f94 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2019 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.statusbar.phone; + +import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.Nullable; +import android.view.View; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.internal.view.AppearanceRegion; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} + * where status bar and navigation icons dim. In this mode, a notification dot appears + * where the notification icons would appear if they would be shown outside of this mode. + * + * This controller shows and hides the notification dot in the status bar to indicate + * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}. + */ +@Singleton +public class LightsOutNotifController { + private final CommandQueue mCommandQueue; + private final NotificationEntryManager mEntryManager; + private final WindowManager mWindowManager; + + /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ + @VisibleForTesting @WindowInsetsController.Appearance int mAppearance; + + private int mDisplayId; + private View mLightsOutNotifView; + + @Inject + LightsOutNotifController(WindowManager windowManager, + NotificationEntryManager entryManager, + CommandQueue commandQueue) { + mWindowManager = windowManager; + mEntryManager = entryManager; + mCommandQueue = commandQueue; + } + + /** + * Sets the notification dot view after it is created in the StatusBar. + * This is the view this controller will show and hide depending on whether: + * 1. there are active notifications + * 2. an app has requested {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} + */ + void setLightsOutNotifView(View lightsOutNotifView) { + destroy(); + mLightsOutNotifView = lightsOutNotifView; + + if (mLightsOutNotifView != null) { + mLightsOutNotifView.setVisibility(View.GONE); + mLightsOutNotifView.setAlpha(0f); + init(); + } + } + + private void destroy() { + mEntryManager.removeNotificationEntryListener(mEntryListener); + mCommandQueue.removeCallback(mCallback); + } + + private void init() { + mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId(); + mEntryManager.addNotificationEntryListener(mEntryListener); + mCommandQueue.addCallback(mCallback); + + updateLightsOutView(); + } + + private boolean hasActiveNotifications() { + return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty(); + } + + @VisibleForTesting + void updateLightsOutView() { + if (mLightsOutNotifView == null) { + return; + } + + final boolean showDot = shouldShowDot(); + if (showDot != isShowingDot()) { + if (showDot) { + mLightsOutNotifView.setAlpha(0f); + mLightsOutNotifView.setVisibility(View.VISIBLE); + } + + mLightsOutNotifView.animate() + .alpha(showDot ? 1 : 0) + .setDuration(showDot ? 750 : 250) + .setInterpolator(new AccelerateInterpolator(2.0f)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator a) { + mLightsOutNotifView.setAlpha(showDot ? 1 : 0); + mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE); + } + }) + .start(); + } + } + + @VisibleForTesting + boolean isShowingDot() { + return mLightsOutNotifView.getVisibility() == View.VISIBLE + && mLightsOutNotifView.getAlpha() == 1.0f; + } + + @VisibleForTesting + boolean shouldShowDot() { + return hasActiveNotifications() && areLightsOut(); + } + + @VisibleForTesting + boolean areLightsOut() { + return 0 != (mAppearance & APPEARANCE_LOW_PROFILE_BARS); + } + + private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() { + @Override + public void onSystemBarAppearanceChanged(int displayId, + @WindowInsetsController.Appearance int appearance, + AppearanceRegion[] appearanceRegions, + boolean navbarColorManagedByIme) { + if (displayId != mDisplayId) { + return; + } + mAppearance = appearance; + updateLightsOutView(); + } + }; + + private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { + // Cares about notifications post-filtering + @Override + public void onNotificationAdded(NotificationEntry entry) { + updateLightsOutView(); + } + + @Override + public void onPostEntryUpdated(NotificationEntry entry) { + updateLightsOutView(); + } + + @Override + public void onEntryRemoved(@Nullable NotificationEntry entry, + NotificationVisibility visibility, boolean removedByUser) { + updateLightsOutView(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java index 70cd43a5e17a8..f359fe7cfec47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -141,12 +141,6 @@ public interface ShadeController { */ void onLaunchAnimationCancelled(); - /** - * When notifications update, give the shade controller a chance to do thing in response to - * the new data set - */ - void updateAreThereNotifications(); - /** * Callback to notify the shade controller that a {@link ActivatableNotificationView} has become * inactive diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 57dcf77b9d6b1..6be881669146c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -47,8 +47,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -117,7 +115,6 @@ import android.view.WindowInsetsController.Appearance; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateInterpolator; import android.widget.DateTimeView; import com.android.internal.annotations.VisibleForTesting; @@ -399,6 +396,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final RemoteInputUriController mRemoteInputUriController; private final Optional mDividerOptional; private final SuperStatusBarViewFactory mSuperStatusBarViewFactory; + private final LightsOutNotifController mLightsOutNotifController; // expanded notifications protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window @@ -704,6 +702,7 @@ public class StatusBar extends SystemUI implements DemoMode, PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional dividerOptional, + LightsOutNotifController lightsOutNotifController, SuperStatusBarViewFactory superStatusBarViewFactory) { super(context); mFeatureFlags = featureFlags; @@ -774,7 +773,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDividerOptional = dividerOptional; mSuperStatusBarViewFactory = superStatusBarViewFactory; - + mLightsOutNotifController = lightsOutNotifController; mBubbleExpandListener = (isExpanding, key) -> { mEntryManager.updateNotifications("onBubbleExpandChanged"); @@ -928,7 +927,6 @@ public class StatusBar extends SystemUI implements DemoMode, mConfigurationController.addCallback(this); // set the initial view visibility - Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications); int disabledFlags1 = result.mDisabledFlags1; int disabledFlags2 = result.mDisabledFlags2; Dependency.get(InitController.class).addPostInitTask( @@ -1057,8 +1055,10 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController, mKeyguardBypassController, mKeyguardStateController, mWakeUpCoordinator, mCommandQueue); mHeadsUpAppearanceController.readFrom(oldController); + + mLightsOutNotifController.setLightsOutNotifView( + mStatusBarView.findViewById(R.id.notification_lights_out)); mStatusBarWindowViewController.setStatusBarView(mStatusBarView); - updateAreThereNotifications(); checkBarModes(); }).getFragmentManager() .beginTransaction() @@ -1546,38 +1546,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public void updateAreThereNotifications() { - if (SPEW) { - final boolean clearable = hasActiveNotifications() && - mNotificationPanel.hasActiveClearableNotifications(); - Log.d(TAG, "updateAreThereNotifications: N=" + - mEntryManager.getNotificationData().getActiveNotifications().size() + " any=" + - hasActiveNotifications() + " clearable=" + clearable); - } - - if (mStatusBarView != null) { - final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); - final boolean showDot = hasActiveNotifications() && !areLightsOn(); - if (showDot != (nlo.getAlpha() == 1.0f)) { - if (showDot) { - nlo.setAlpha(0f); - nlo.setVisibility(View.VISIBLE); - } - nlo.animate() - .alpha(showDot ? 1 : 0) - .setDuration(showDot ? 750 : 250) - .setInterpolator(new AccelerateInterpolator(2.0f)) - .setListener(showDot ? null : new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator _a) { - nlo.setVisibility(View.GONE); - } - }) - .start(); - } - } - } - private void updateReportRejectedTouchVisibility() { if (mReportRejectedTouch == null) { return; @@ -2306,14 +2274,8 @@ public class StatusBar extends SystemUI implements DemoMode, return; } boolean barModeChanged = false; - final int diff = mAppearance ^ appearance; if (mAppearance != appearance) { mAppearance = appearance; - - // update low profile - if ((diff & APPEARANCE_LOW_PROFILE_BARS) != 0) { - updateAreThereNotifications(); - } barModeChanged = updateBarMode(barMode(mTransientShown, appearance)); } mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged, @@ -2494,10 +2456,6 @@ public class StatusBar extends SystemUI implements DemoMode, return mAppImmersive; } - private boolean areLightsOn() { - return 0 == (mAppearance & APPEARANCE_LOW_PROFILE_BARS); - } - public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java index 9e0eb3570b595..2364c336b7b1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -161,6 +161,7 @@ public class StatusBarModule { PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional dividerOptional, + LightsOutNotifController lightsOutNotifController, SuperStatusBarViewFactory superStatusBarViewFactory) { return new StatusBar( context, @@ -230,6 +231,7 @@ public class StatusBarModule { pluginManager, remoteInputUriController, dividerOptional, + lightsOutNotifController, superStatusBarViewFactory); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 02e5ebfe5705e..38ff86227733b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -79,7 +79,8 @@ import java.util.ArrayList; public class StatusBarNotificationPresenter implements NotificationPresenter, ConfigurationController.ConfigurationListener, - NotificationRowBinderImpl.BindRowCallback { + NotificationRowBinderImpl.BindRowCallback, + CommandQueue.Callbacks { private final LockscreenGestureLogger mLockscreenGestureLogger = Dependency.get(LockscreenGestureLogger.class); @@ -184,17 +185,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller; Dependency.get(InitController.class).addPostInitTask(() -> { NotificationEntryListener notificationEntryListener = new NotificationEntryListener() { - @Override - public void onNotificationAdded(NotificationEntry entry) { - // Recalculate the position of the sliding windows and the titles. - mShadeController.updateAreThereNotifications(); - } - - @Override - public void onPostEntryUpdated(NotificationEntry entry) { - mShadeController.updateAreThereNotifications(); - } - @Override public void onEntryRemoved( @Nullable NotificationEntry entry, @@ -333,7 +323,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } } } - mShadeController.updateAreThereNotifications(); } public boolean hasActiveNotifications() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java new file mode 100644 index 0000000000000..64c1b510cbf97 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2019 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.statusbar.phone; + +import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.Display; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +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; + +import java.util.ArrayList; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class LightsOutNotifControllerTest extends SysuiTestCase { + private static final int LIGHTS_ON = 0; + private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS; + + @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationData mNotificationData; + @Mock private CommandQueue mCommandQueue; + @Mock private WindowManager mWindowManager; + @Mock private Display mDisplay; + + @Captor private ArgumentCaptor mListenerCaptor; + @Captor private ArgumentCaptor mCallbacksCaptor; + + private View mLightsOutView; + private LightsOutNotifController mLightsOutNotifController; + private ArrayList mActiveNotifications = new ArrayList<>(); + private int mDisplayId; + private NotificationEntryListener mEntryListener; + private CommandQueue.Callbacks mCallbacks; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mDisplayId = mContext.getDisplayId(); + mLightsOutView = new View(mContext); + when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); + when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications); + when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay); + when(mDisplay.getDisplayId()).thenReturn(mDisplayId); + + mLightsOutNotifController = new LightsOutNotifController(mWindowManager, mEntryManager, + mCommandQueue); + mLightsOutNotifController.setLightsOutNotifView(mLightsOutView); + + // Capture the entry listener object so we can simulate events in tests below + verify(mEntryManager).addNotificationEntryListener(mListenerCaptor.capture()); + mEntryListener = checkNotNull(mListenerCaptor.getValue()); + + // Capture the callback object so we can simulate callback events in tests below + verify(mCommandQueue).addCallback(mCallbacksCaptor.capture()); + mCallbacks = checkNotNull(mCallbacksCaptor.getValue()); + } + + @Test + public void testAreLightsOut_lightsOut() { + mCallbacks.onSystemBarAppearanceChanged( + mDisplayId /* display id */, + LIGHTS_OUT /* appearance */, + null /* appearanceRegions */, + false /* navbarColorManagedByIme */); + assertTrue(mLightsOutNotifController.areLightsOut()); + } + + @Test + public void testAreLightsOut_lightsOn() { + mCallbacks.onSystemBarAppearanceChanged( + mDisplayId /* display id */, + LIGHTS_ON /* appearance */, + null /* appearanceRegions */, + false /* navbarColorManagedByIme */); + assertFalse(mLightsOutNotifController.areLightsOut()); + } + + @Test + public void testIsShowingDot_visible() { + mLightsOutView.setVisibility(View.VISIBLE); + mLightsOutView.setAlpha(1.0f); + assertTrue(mLightsOutNotifController.isShowingDot()); + } + + @Test + public void testIsShowingDot_gone() { + mLightsOutView.setVisibility(View.GONE); + mLightsOutView.setAlpha(0f); + assertFalse(mLightsOutNotifController.isShowingDot()); + } + + @Test + public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() { + // GIVEN active visible notifications + mActiveNotifications.add(mock(NotificationEntry.class)); + + // WHEN lights out + mCallbacks.onSystemBarAppearanceChanged( + mDisplayId /* display id */, + LIGHTS_OUT /* appearance */, + null /* appearanceRegions */, + false /* navbarColorManagedByIme */); + + // THEN we should show dot + assertTrue(mLightsOutNotifController.shouldShowDot()); + assertIsShowingDot(true); + } + + @Test + public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() { + // GIVEN no active visible notifications + mActiveNotifications.clear(); + + // WHEN lights out + mCallbacks.onSystemBarAppearanceChanged( + mDisplayId /* display id */, + LIGHTS_OUT /* appearance */, + null /* appearanceRegions */, + false /* navbarColorManagedByIme */); + + // THEN we shouldn't show the dot + assertFalse(mLightsOutNotifController.shouldShowDot()); + assertIsShowingDot(false); + } + + @Test + public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() { + // GIVEN active visible notifications + mActiveNotifications.add(mock(NotificationEntry.class)); + + // WHEN lights on + mCallbacks.onSystemBarAppearanceChanged( + mDisplayId /* display id */, + LIGHTS_ON /* appearance */, + null /* appearanceRegions */, + false /* navbarColorManagedByIme */); + + // THEN we shouldn't show the dot + assertFalse(mLightsOutNotifController.shouldShowDot()); + assertIsShowingDot(false); + } + + @Test + public void testEntryAdded() { + // GIVEN no visible notifications and lights out + mActiveNotifications.clear(); + mLightsOutNotifController.mAppearance = LIGHTS_OUT; + mLightsOutNotifController.updateLightsOutView(); + assertIsShowingDot(false); + + // WHEN an active notification is added + mActiveNotifications.add(mock(NotificationEntry.class)); + assertTrue(mLightsOutNotifController.shouldShowDot()); + mEntryListener.onNotificationAdded(mock(NotificationEntry.class)); + + // THEN we should see the dot view + assertIsShowingDot(true); + } + + @Test + public void testEntryRemoved() { + // GIVEN a visible notification and lights out + mActiveNotifications.add(mock(NotificationEntry.class)); + mLightsOutNotifController.mAppearance = LIGHTS_OUT; + mLightsOutNotifController.updateLightsOutView(); + assertIsShowingDot(true); + + // WHEN all active notifications are removed + mActiveNotifications.clear(); + assertFalse(mLightsOutNotifController.shouldShowDot()); + mEntryListener.onEntryRemoved(mock(NotificationEntry.class), null, false); + + // THEN we shouldn't see the dot view + assertIsShowingDot(false); + } + + @Test + public void testEntryUpdated() { + // GIVEN no visible notifications and lights out + mActiveNotifications.clear(); + mLightsOutNotifController.mAppearance = LIGHTS_OUT; + mLightsOutNotifController.updateLightsOutView(); + assertIsShowingDot(false); + + // WHEN an active notification is added + mActiveNotifications.add(mock(NotificationEntry.class)); + assertTrue(mLightsOutNotifController.shouldShowDot()); + mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class)); + + // THEN we should see the dot view + assertIsShowingDot(true); + } + + private void assertIsShowingDot(boolean isShowing) { + // cancel the animation so we can check the end state + final ViewPropertyAnimator animation = mLightsOutView.animate(); + if (animation != null) { + animation.cancel(); + } + + if (isShowing) { + assertTrue(mLightsOutNotifController.isShowingDot()); + } else { + assertFalse(mLightsOutNotifController.isShowingDot()); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 1db926dfd196b..7d6f006eb8334 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -237,6 +237,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private PluginManager mPluginManager; @Mock private Divider mDivider; @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory; + @Mock private LightsOutNotifController mLightsOutNotifController; @Before public void setup() throws Exception { @@ -376,6 +377,7 @@ public class StatusBarTest extends SysuiTestCase { mPluginManager, mRemoteInputUriController, Optional.of(mDivider), + mLightsOutNotifController, mSuperStatusBarViewFactory); when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn(