diff options
7 files changed, 419 insertions, 177 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index ebeb35189008..e80d6d34b1dc 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -30,6 +30,8 @@ import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationGutsManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationLogger; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.phone.DozeParameters; @@ -37,6 +39,7 @@ import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenWallpaper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; @@ -114,10 +117,16 @@ public class SystemUIFactory { Context context) { providers.put(NotificationLockscreenUserManager.class, () -> new NotificationLockscreenUserManager(context)); + providers.put(NotificationGroupManager.class, NotificationGroupManager::new); providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager( Dependency.get(NotificationLockscreenUserManager.class), context)); providers.put(NotificationRemoteInputManager.class, () -> new NotificationRemoteInputManager( Dependency.get(NotificationLockscreenUserManager.class), context)); + providers.put(NotificationListener.class, () -> new NotificationListener( + Dependency.get(NotificationRemoteInputManager.class), context)); + providers.put(NotificationLogger.class, () -> new NotificationLogger( + Dependency.get(NotificationListener.class), + Dependency.get(UiOffloadThread.class))); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 4952da4b1fec..a72e8ac77af5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -36,13 +36,13 @@ import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins; public class NotificationListener extends NotificationListenerWithPlugins { private static final String TAG = "NotificationListener"; - private final NotificationPresenter mPresenter; private final NotificationRemoteInputManager mRemoteInputManager; private final Context mContext; - public NotificationListener(NotificationPresenter presenter, - NotificationRemoteInputManager remoteInputManager, Context context) { - mPresenter = presenter; + private NotificationPresenter mPresenter; + + public NotificationListener(NotificationRemoteInputManager remoteInputManager, + Context context) { mRemoteInputManager = remoteInputManager; mContext = context; } @@ -120,7 +120,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { } } - public void register() { + public void setUpWithPresenter(NotificationPresenter presenter) { + mPresenter = presenter; + try { registerAsSystemService(mContext, new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java new file mode 100644 index 000000000000..e58d80186b3a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2017 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; + +import android.content.Context; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.service.notification.NotificationListenerService; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.UiOffloadThread; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * Handles notification logging, in particular, logging which notifications are visible and which + * are not. + */ +public class NotificationLogger { + private static final String TAG = "NotificationLogger"; + + /** The minimum delay in ms between reports of notification visibility. */ + private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; + + /** Keys of notifications currently visible to the user. */ + private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = + new ArraySet<>(); + private final NotificationListenerService mNotificationListener; + private final UiOffloadThread mUiOffloadThread; + + protected NotificationPresenter mPresenter; + protected Handler mHandler = new Handler(); + protected IStatusBarService mBarService; + private long mLastVisibilityReportUptimeMs; + private NotificationStackScrollLayout mStackScroller; + + protected final NotificationStackScrollLayout.OnChildLocationsChangedListener + mNotificationLocationsChangedListener = + new NotificationStackScrollLayout.OnChildLocationsChangedListener() { + @Override + public void onChildLocationsChanged( + NotificationStackScrollLayout stackScrollLayout) { + if (mHandler.hasCallbacks(mVisibilityReporter)) { + // Visibilities will be reported when the existing + // callback is executed. + return; + } + // Calculate when we're allowed to run the visibility + // reporter. Note that this timestamp might already have + // passed. That's OK, the callback will just be executed + // ASAP. + long nextReportUptimeMs = + mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; + mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); + } + }; + + // Tracks notifications currently visible in mNotificationStackScroller and + // emits visibility events via NoMan on changes. + protected final Runnable mVisibilityReporter = new Runnable() { + private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = + new ArraySet<>(); + + @Override + public void run() { + mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); + + // 1. Loop over mNotificationData entries: + // A. Keep list of visible notifications. + // B. Keep list of previously hidden, now visible notifications. + // 2. Compute no-longer visible notifications by removing currently + // visible notifications from the set of previously visible + // notifications. + // 3. Report newly visible and no-longer visible notifications. + // 4. Keep currently visible notifications for next report. + ArrayList<NotificationData.Entry> activeNotifications = mPresenter. + getNotificationData().getActiveNotifications(); + int N = activeNotifications.size(); + for (int i = 0; i < N; i++) { + NotificationData.Entry entry = activeNotifications.get(i); + String key = entry.notification.getKey(); + boolean isVisible = mStackScroller.isInVisibleLocation(entry.row); + NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); + boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); + if (isVisible) { + // Build new set of visible notifications. + mTmpCurrentlyVisibleNotifications.add(visObj); + if (!previouslyVisible) { + mTmpNewlyVisibleNotifications.add(visObj); + } + } else { + // release object + visObj.recycle(); + } + } + mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); + mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); + + logNotificationVisibilityChanges( + mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); + + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); + mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); + + recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); + mTmpCurrentlyVisibleNotifications.clear(); + mTmpNewlyVisibleNotifications.clear(); + mTmpNoLongerVisibleNotifications.clear(); + } + }; + + public NotificationLogger(NotificationListenerService notificationListener, + UiOffloadThread uiOffloadThread) { + mNotificationListener = notificationListener; + mUiOffloadThread = uiOffloadThread; + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + } + + // TODO: Remove dependency on NotificationStackScrollLayout. + public void setUpWithPresenter(NotificationPresenter presenter, + NotificationStackScrollLayout stackScroller) { + mPresenter = presenter; + mStackScroller = stackScroller; + } + + public void stopNotificationLogging() { + // Report all notifications as invisible and turn down the + // reporter. + if (!mCurrentlyVisibleNotifications.isEmpty()) { + logNotificationVisibilityChanges( + Collections.emptyList(), mCurrentlyVisibleNotifications); + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); + } + mHandler.removeCallbacks(mVisibilityReporter); + mStackScroller.setChildLocationsChangedListener(null); + } + + public void startNotificationLogging() { + mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); + // 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 + // notifications. + // (Note that in cases where the scroller does emit events, this + // additional event doesn't break anything.) + mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); + } + + private void logNotificationVisibilityChanges( + Collection<NotificationVisibility> newlyVisible, + Collection<NotificationVisibility> noLongerVisible) { + if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { + return; + } + NotificationVisibility[] newlyVisibleAr = + newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); + NotificationVisibility[] noLongerVisibleAr = + noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); + mUiOffloadThread.submit(() -> { + try { + mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); + } catch (RemoteException e) { + // Ignore. + } + + final int N = newlyVisible.size(); + if (N > 0) { + String[] newlyVisibleKeyAr = new String[N]; + for (int i = 0; i < N; i++) { + newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; + } + + // TODO: Call NotificationEntryManager to do this, once it exists. + // TODO: Consider not catching all runtime exceptions here. + try { + mNotificationListener.setNotificationsShown(newlyVisibleKeyAr); + } catch (RuntimeException e) { + Log.d(TAG, "failed setNotificationsShown: ", e); + } + } + }); + } + + private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { + final int N = array.size(); + for (int i = 0 ; i < N; i++) { + array.valueAt(i).recycle(); + } + array.clear(); + } + + @VisibleForTesting + public Runnable getVisibilityReporter() { + return mVisibilityReporter; + } +} 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 d162448e09b9..fecd6bd4b9ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -136,7 +136,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.NotificationMessagingUtil; import com.android.internal.widget.LockPatternUtils; @@ -203,6 +202,7 @@ import com.android.systemui.statusbar.NotificationGutsManager; import com.android.systemui.statusbar.NotificationInfo; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLogger; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -236,8 +236,6 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.stack.NotificationStackScrollLayout - .OnChildLocationsChangedListener; import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.volume.VolumeComponent; @@ -247,7 +245,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -317,9 +314,6 @@ public class StatusBar extends SystemUI implements DemoMode, View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; private static final long AUTOHIDE_TIMEOUT_MS = 2250; - /** The minimum delay in ms between reports of notification visibility. */ - private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; - /** * The delay to reset the hint text when the hint animation is finished running. */ @@ -431,6 +425,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); private NotificationGutsManager mGutsManager; + protected NotificationLogger mNotificationLogger; // for disabling the status bar private int mDisabled1 = 0; @@ -531,10 +526,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationLockscreenUserManager mLockscreenUserManager; protected NotificationRemoteInputManager mRemoteInputManager; - /** Keys of notifications currently visible to the user. */ - private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = - new ArraySet<>(); - private long mLastVisibilityReportUptimeMs; + private Runnable mLaunchTransitionEndRunnable; protected boolean mLaunchTransitionFadingAway; @@ -557,83 +549,6 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mWereIconsJustHidden; private boolean mBouncerWasShowingWhenHidden; - private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = - new OnChildLocationsChangedListener() { - @Override - public void onChildLocationsChanged( - NotificationStackScrollLayout stackScrollLayout) { - if (mHandler.hasCallbacks(mVisibilityReporter)) { - // Visibilities will be reported when the existing - // callback is executed. - return; - } - // Calculate when we're allowed to run the visibility - // reporter. Note that this timestamp might already have - // passed. That's OK, the callback will just be executed - // ASAP. - long nextReportUptimeMs = - mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; - mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); - } - }; - - // Tracks notifications currently visible in mNotificationStackScroller and - // emits visibility events via NoMan on changes. - protected final Runnable mVisibilityReporter = new Runnable() { - private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = - new ArraySet<>(); - private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = - new ArraySet<>(); - private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = - new ArraySet<>(); - - @Override - public void run() { - mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); - - // 1. Loop over mNotificationData entries: - // A. Keep list of visible notifications. - // B. Keep list of previously hidden, now visible notifications. - // 2. Compute no-longer visible notifications by removing currently - // visible notifications from the set of previously visible - // notifications. - // 3. Report newly visible and no-longer visible notifications. - // 4. Keep currently visible notifications for next report. - ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); - int N = activeNotifications.size(); - for (int i = 0; i < N; i++) { - Entry entry = activeNotifications.get(i); - String key = entry.notification.getKey(); - boolean isVisible = mStackScroller.isInVisibleLocation(entry.row); - NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); - boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); - if (isVisible) { - // Build new set of visible notifications. - mTmpCurrentlyVisibleNotifications.add(visObj); - if (!previouslyVisible) { - mTmpNewlyVisibleNotifications.add(visObj); - } - } else { - // release object - visObj.recycle(); - } - } - mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); - mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); - - logNotificationVisibilityChanges( - mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); - - recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); - mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); - - recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); - mTmpCurrentlyVisibleNotifications.clear(); - mTmpNewlyVisibleNotifications.clear(); - mTmpNoLongerVisibleNotifications.clear(); - } - }; - // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, // this animation is tied to the scrim for historic reasons. // TODO: notify when keyguard has faded away instead of the scrim. @@ -680,14 +595,6 @@ public class StatusBar extends SystemUI implements DemoMode, private ScreenLifecycle mScreenLifecycle; @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle; - private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { - final int N = array.size(); - for (int i = 0 ; i < N; i++) { - array.valueAt(i).recycle(); - } - array.clear(); - } - private final View.OnClickListener mGoToLockedShadeListener = v -> { if (mState == StatusBarState.KEYGUARD) { wakeUpIfDozing(SystemClock.uptimeMillis(), v); @@ -715,6 +622,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void start() { + mGroupManager = Dependency.get(NotificationGroupManager.class); + mNotificationLogger = Dependency.get(NotificationLogger.class); mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); mNetworkController = Dependency.get(NetworkController.class); mUserSwitcherController = Dependency.get(UserSwitcherController.class); @@ -809,8 +718,8 @@ public class StatusBar extends SystemUI implements DemoMode, } // Set up the initial notification state. - mNotificationListener = new NotificationListener(this, mRemoteInputManager, mContext); - mNotificationListener.register(); + mNotificationListener = Dependency.get(NotificationListener.class); + mNotificationListener.setUpWithPresenter(this); if (DEBUG) { Log.d(TAG, String.format( @@ -895,6 +804,7 @@ public class StatusBar extends SystemUI implements DemoMode, // if we're here we're dead } }); + mNotificationLogger.setUpWithPresenter(this, mStackScroller); mNotificationPanel.setStatusBar(this); mNotificationPanel.setGroupManager(mGroupManager); mAboveShelfObserver = new AboveShelfObserver(mStackScroller); @@ -3617,9 +3527,9 @@ public class StatusBar extends SystemUI implements DemoMode, protected void handleVisibleToUserChanged(boolean visibleToUser) { if (visibleToUser) { handleVisibleToUserChangedImpl(visibleToUser); - startNotificationLogging(); + mNotificationLogger.startNotificationLogging(); } else { - stopNotificationLogging(); + mNotificationLogger.stopNotificationLogging(); handleVisibleToUserChangedImpl(visibleToUser); } } @@ -3671,60 +3581,6 @@ public class StatusBar extends SystemUI implements DemoMode, } - private void stopNotificationLogging() { - // Report all notifications as invisible and turn down the - // reporter. - if (!mCurrentlyVisibleNotifications.isEmpty()) { - logNotificationVisibilityChanges( - Collections.emptyList(), mCurrentlyVisibleNotifications); - recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); - } - mHandler.removeCallbacks(mVisibilityReporter); - mStackScroller.setChildLocationsChangedListener(null); - } - - private void startNotificationLogging() { - mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); - // 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 - // notifications. - // (Note that in cases where the scroller does emit events, this - // additional event doesn't break anything.) - mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); - } - - private void logNotificationVisibilityChanges( - Collection<NotificationVisibility> newlyVisible, - Collection<NotificationVisibility> noLongerVisible) { - if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { - return; - } - NotificationVisibility[] newlyVisibleAr = - newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); - NotificationVisibility[] noLongerVisibleAr = - noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); - mUiOffloadThread.submit(() -> { - try { - mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); - } catch (RemoteException e) { - // Ignore. - } - - final int N = newlyVisible.size(); - if (N > 0) { - String[] newlyVisibleKeyAr = new String[N]; - for (int i = 0; i < N; i++) { - newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; - } - - setNotificationsShown(newlyVisibleKeyAr); - } - }); - } - - // State logging - private void logStateToEventlog() { boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); @@ -5394,7 +5250,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationData mNotificationData; protected NotificationStackScrollLayout mStackScroller; - protected final NotificationGroupManager mGroupManager = new NotificationGroupManager(); + protected NotificationGroupManager mGroupManager; // for heads up notifications @@ -5562,12 +5418,8 @@ public class StatusBar extends SystemUI implements DemoMode, } protected void setNotificationShown(StatusBarNotification n) { - setNotificationsShown(new String[]{n.getKey()}); - } - - protected void setNotificationsShown(String[] keys) { try { - mNotificationListener.setNotificationsShown(keys); + mNotificationListener.setNotificationsShown(new String[]{n.getKey()}); } catch (RuntimeException e) { Log.d(TAG, "failed setNotificationsShown: ", e); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index f562340664c8..ccc300625c8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -71,9 +71,11 @@ public class NotificationListenerTest extends SysuiTestCase { when(mPresenter.getNotificationData()).thenReturn(mNotificationData); when(mRemoteInputManager.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput); - mListener = new NotificationListener(mPresenter, mRemoteInputManager, mContext); + mListener = new NotificationListener(mRemoteInputManager, mContext); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); + + mListener.setUpWithPresenter(mPresenter); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java new file mode 100644 index 000000000000..142ce63afbd8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017 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; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.UiOffloadThread; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +import com.google.android.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationLoggerTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test"; + private static final int TEST_UID = 0; + + @Mock private NotificationPresenter mPresenter; + @Mock private NotificationListener mListener; + @Mock private NotificationStackScrollLayout mStackScroller; + @Mock private IStatusBarService mBarService; + @Mock private NotificationData mNotificationData; + @Mock private ExpandableNotificationRow mRow; + + private NotificationData.Entry mEntry; + private StatusBarNotification mSbn; + private TestableNotificationLogger mLogger; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mPresenter.getNotificationData()).thenReturn(mNotificationData); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, + 0, new Notification(), UserHandle.CURRENT, null, 0); + mEntry = new NotificationData.Entry(mSbn); + mEntry.row = mRow; + + mLogger = new TestableNotificationLogger(mListener, mDependency.get(UiOffloadThread.class), + mBarService); + mLogger.setUpWithPresenter(mPresenter, mStackScroller); + } + + @Test + public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception { + when(mStackScroller.isInVisibleLocation(any())).thenReturn(true); + when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry)); + mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller); + waitForIdleSync(mLogger.getHandlerForTest()); + waitForUiOffloadThread(); + + NotificationVisibility[] newlyVisibleKeys = { + NotificationVisibility.obtain(mEntry.key, 0, true) + }; + NotificationVisibility[] noLongerVisibleKeys = {}; + verify(mBarService).onNotificationVisibilityChanged(newlyVisibleKeys, noLongerVisibleKeys); + + // |mEntry| won't change visibility, so it shouldn't be reported again: + Mockito.reset(mBarService); + mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller); + waitForIdleSync(mLogger.getHandlerForTest()); + waitForUiOffloadThread(); + + verify(mBarService, never()).onNotificationVisibilityChanged(any(), any()); + } + + @Test + public void testStoppingNotificationLoggingReportsCurrentNotifications() + throws Exception { + when(mStackScroller.isInVisibleLocation(any())).thenReturn(true); + when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry)); + mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller); + waitForIdleSync(mLogger.getHandlerForTest()); + waitForUiOffloadThread(); + Mockito.reset(mBarService); + + mLogger.stopNotificationLogging(); + waitForUiOffloadThread(); + // The visibility objects are recycled by NotificationLogger, so we can't use specific + // matchers here. + verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); + } + + private class TestableNotificationLogger extends NotificationLogger { + + public TestableNotificationLogger( + NotificationListenerService notificationListener, + UiOffloadThread uiOffloadThread, + IStatusBarService barService) { + super(notificationListener, uiOffloadThread); + mBarService = barService; + // Make this on the main thread so we can wait for it during tests. + mHandler = new Handler(Looper.getMainLooper()); + } + + public NotificationStackScrollLayout.OnChildLocationsChangedListener + getChildLocationsChangedListenerForTest() { + return mNotificationLocationsChangedListener; + } + + public Handler getHandlerForTest() { + return mHandler; + } + } +} 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 e4c33f11221b..073286617979 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 @@ -43,7 +43,6 @@ import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IPowerManager; -import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -52,7 +51,6 @@ import android.support.test.filters.SmallTest; import android.support.test.metricshelper.MetricsAsserts; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.testing.TestableLooper.MessageHandler; import android.testing.TestableLooper.RunWithLooper; import android.util.DisplayMetrics; import android.util.SparseArray; @@ -65,6 +63,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.UiOffloadThread; import com.android.systemui.assist.AssistManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -73,7 +72,9 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; +import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLogger; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -107,6 +108,8 @@ public class StatusBarTest extends SysuiTestCase { NotificationPanelView mNotificationPanelView; ScrimController mScrimController; IStatusBarService mBarService; + NotificationListener mNotificationListener; + NotificationLogger mNotificationLogger; ArrayList<Entry> mNotificationList; FingerprintUnlockController mFingerprintUnlockController; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @@ -143,12 +146,16 @@ public class StatusBarTest extends SysuiTestCase { new Handler(handlerThread.getLooper())); when(powerManagerService.isInteractive()).thenReturn(true); mBarService = mock(IStatusBarService.class); + mNotificationListener = mock(NotificationListener.class); + mNotificationLogger = new NotificationLogger(mNotificationListener, mDependency.get( + UiOffloadThread.class)); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache, mKeyguardIndicationController, mStackScroller, mHeadsUpManager, mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView, - mBarService, mScrimController, mFingerprintUnlockController); + mBarService, mNotificationListener, mNotificationLogger, mScrimController, + mFingerprintUnlockController); mStatusBar.mContext = mContext; mStatusBar.mComponents = mContext.getComponents(); doAnswer(invocation -> { @@ -163,15 +170,14 @@ public class StatusBarTest extends SysuiTestCase { return null; }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any()); + mNotificationLogger.setUpWithPresenter(mStatusBar, mStackScroller); + when(mStackScroller.getActivatedChild()).thenReturn(null); - TestableLooper.get(this).setMessageHandler(new MessageHandler() { - @Override - public boolean onMessageHandled(Message m) { - if (m.getCallback() == mStatusBar.mVisibilityReporter) { - return false; - } - return true; + TestableLooper.get(this).setMessageHandler(m -> { + if (m.getCallback() == mStatusBar.mNotificationLogger.getVisibilityReporter()) { + return false; } + return true; }); } @@ -560,7 +566,8 @@ public class StatusBarTest extends SysuiTestCase { UnlockMethodCache unlock, KeyguardIndicationController key, NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd, PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView, - IStatusBarService barService, ScrimController scrimController, + IStatusBarService barService, NotificationListener notificationListener, + NotificationLogger notificationLogger, ScrimController scrimController, FingerprintUnlockController fingerprintUnlockController) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; @@ -573,6 +580,8 @@ public class StatusBarTest extends SysuiTestCase { mSystemServicesProxy = ssp; mNotificationPanel = panelView; mBarService = barService; + mNotificationListener = notificationListener; + mNotificationLogger = notificationLogger; mWakefulnessLifecycle = createAwakeWakefulnessLifecycle(); mScrimController = scrimController; mFingerprintUnlockController = fingerprintUnlockController; |