summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java223
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java172
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java31
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;