Split NotificationViewHierarchyManager out of StatusBar.
NotificationViewHierarchyManager handles bundling and unbundling of
notifications. In doing so, which notifications are parents/children of
the other can change. NotificationViewHierarchyManager makes sure the
view hierarchy of the notifications matches their grouping.
Bug: 63874929
Bug: 62602530
Test: runtest systemui
Test: Compile and run
Change-Id: Ia1c8ed75d4eb8df52897c5d6aa0713f8335b2a19
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index c32e089..47148a4 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -32,12 +32,14 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationGutsManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
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.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -122,6 +124,7 @@
Context context) {
providers.put(NotificationLockscreenUserManager.class,
() -> new NotificationLockscreenUserManager(context));
+ providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(
@@ -134,6 +137,12 @@
providers.put(NotificationLogger.class, () -> new NotificationLogger(
Dependency.get(NotificationListener.class),
Dependency.get(UiOffloadThread.class)));
+ providers.put(NotificationViewHierarchyManager.class, () ->
+ new NotificationViewHierarchyManager(
+ Dependency.get(NotificationLockscreenUserManager.class),
+ Dependency.get(NotificationGroupManager.class),
+ Dependency.get(VisualStabilityManager.class),
+ context));
providers.put(NotificationEntryManager.class, () ->
new NotificationEntryManager(
Dependency.get(NotificationLockscreenUserManager.class),
@@ -145,9 +154,8 @@
Dependency.get(NotificationListener.class),
Dependency.get(MetricsLogger.class),
Dependency.get(DeviceProvisionedController.class),
+ Dependency.get(VisualStabilityManager.class),
Dependency.get(UiOffloadThread.class),
context));
- providers.put(NotificationListener.class, () -> new NotificationListener(
- Dependency.get(NotificationRemoteInputManager.class), context));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 37172b6..20418c3 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -43,10 +44,11 @@
NotificationListener notificationListener,
MetricsLogger metricsLogger,
DeviceProvisionedController deviceProvisionedController,
+ VisualStabilityManager visualStabilityManager,
UiOffloadThread uiOffloadThread, Context context) {
super(lockscreenUserManager, groupManager, gutsManager, remoteInputManager, mediaManager,
foregroundServiceController, notificationListener, metricsLogger,
- deviceProvisionedController, uiOffloadThread, context);
+ deviceProvisionedController, visualStabilityManager, uiOffloadThread, context);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index 24f8b97..55965e7 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.volume.car.CarVolumeDialogController;
@@ -54,6 +55,7 @@
Dependency.get(NotificationListener.class),
Dependency.get(MetricsLogger.class),
Dependency.get(DeviceProvisionedController.class),
+ Dependency.get(VisualStabilityManager.class),
Dependency.get(UiOffloadThread.class),
context));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index b60d2ac..90b46c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -60,7 +60,6 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.util.leak.LeakDetector;
import java.io.FileDescriptor;
@@ -75,7 +74,8 @@
* Notification.*Manager objects.
*/
public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
- ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler {
+ ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
+ VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryManager";
protected static final boolean DEBUG = false;
protected static final boolean ENABLE_HEADS_UP = true;
@@ -90,6 +90,7 @@
protected final NotificationMediaManager mMediaManager;
protected final MetricsLogger mMetricsLogger;
protected final DeviceProvisionedController mDeviceProvisionedController;
+ protected final VisualStabilityManager mVisualStabilityManager;
protected final UiOffloadThread mUiOffloadThread;
protected final ForegroundServiceController mForegroundServiceController;
protected final NotificationListener mNotificationListener;
@@ -100,7 +101,6 @@
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
protected Callback mCallback;
- protected NotificationStackScrollLayout mStackScroller;
protected PowerManager mPowerManager;
protected SystemServicesProxy mSystemServicesProxy;
protected NotificationListenerService.RankingMap mLatestRankingMap;
@@ -109,7 +109,7 @@
protected ContentObserver mHeadsUpObserver;
protected boolean mUseHeadsUp = false;
protected boolean mDisableNotificationAlerts;
- protected VisualStabilityManager mVisualStabilityManager;
+ protected NotificationListContainer mListContainer;
private final class NotificationClicker implements View.OnClickListener {
@@ -214,6 +214,7 @@
NotificationListener notificationListener,
MetricsLogger metricsLogger,
DeviceProvisionedController deviceProvisionedController,
+ VisualStabilityManager visualStabilityManager,
UiOffloadThread uiOffloadThread, Context context) {
mLockscreenUserManager = lockscreenUserManager;
mGroupManager = groupManager;
@@ -224,6 +225,7 @@
mNotificationListener = notificationListener;
mMetricsLogger = metricsLogger;
mDeviceProvisionedController = deviceProvisionedController;
+ mVisualStabilityManager = visualStabilityManager;
mUiOffloadThread = uiOffloadThread;
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -233,19 +235,15 @@
mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
}
- // TODO: Remove dependency on NotificationStackScrollLayout
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationStackScrollLayout stackScroller,
- Callback callback,
- VisualStabilityManager visualStabilityManager,
+ NotificationListContainer listContainer, Callback callback,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
mCallback = callback;
- mStackScroller = stackScroller;
- mVisualStabilityManager = visualStabilityManager;
mNotificationData = new NotificationData(presenter);
mHeadsUpManager = headsUpManager;
mNotificationData.setHeadsUpManager(mHeadsUpManager);
+ mListContainer = listContainer;
mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) {
@Override
@@ -301,6 +299,11 @@
});
}
+ @Override
+ public void onReorderingAllowed() {
+ updateNotifications();
+ }
+
private boolean shouldSuppressFullScreenIntent(String key) {
if (mPresenter.isDeviceInVrMode()) {
return true;
@@ -379,7 +382,7 @@
int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
if (isHeadsUp(n.getKey())) {
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mStackScroller.hasPulsingNotifications()) {
+ } else if (mListContainer.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
}
mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface);
@@ -536,7 +539,7 @@
if (entry != null && entry.row != null) {
entry.row.setRemoved();
- mStackScroller.cleanUpViewState(entry.row);
+ mListContainer.cleanUpViewState(entry.row);
}
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key);
@@ -662,7 +665,7 @@
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
- inflateViews(entry, mStackScroller);
+ inflateViews(entry, mListContainer.getViewParentForNotification(entry));
return entry;
}
@@ -752,7 +755,7 @@
mGroupManager.onEntryUpdated(entry, oldNotification);
entry.updateIcons(mContext, notification);
- inflateViews(entry, mStackScroller);
+ inflateViews(entry, mListContainer.getViewParentForNotification(entry));
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
@@ -766,7 +769,7 @@
if (!notification.isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
- mStackScroller.snapViewIfNeeded(entry.row);
+ mListContainer.snapViewIfNeeded(entry.row);
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index d6a8af5..0cecadf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -42,7 +42,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.io.FileDescriptor;
@@ -76,7 +75,7 @@
// TODO: Create NotificationListContainer interface and use it instead of
// NotificationStackScrollLayout here
- private NotificationStackScrollLayout mStackScroller;
+ private NotificationListContainer mListContainer;
private NotificationInfo.CheckSaveListener mCheckSaveListener;
private OnSettingsClickListener mOnSettingsClickListener;
private String mKeyToRemoveOnGutsClosed;
@@ -96,12 +95,11 @@
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
- public void setUp(NotificationPresenter presenter,
- NotificationStackScrollLayout stackScroller,
+ public void setUp(NotificationPresenter presenter, NotificationListContainer listContainer,
NotificationInfo.CheckSaveListener checkSaveListener,
OnSettingsClickListener onSettingsClickListener) {
mPresenter = presenter;
- mStackScroller = stackScroller;
+ mListContainer = listContainer;
mCheckSaveListener = checkSaveListener;
mOnSettingsClickListener = onSettingsClickListener;
}
@@ -158,7 +156,7 @@
final NotificationGuts guts = row.getGuts();
guts.setClosedListener((NotificationGuts g) -> {
if (!g.willBeRemoved() && !row.isRemoved()) {
- mStackScroller.onHeightChanged(
+ mListContainer.onHeightChanged(
row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
}
if (mNotificationGutsExposed == g) {
@@ -176,11 +174,11 @@
View gutsView = item.getGutsView();
if (gutsView instanceof NotificationSnooze) {
NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
- snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
+ snoozeGuts.setSnoozeListener(mListContainer.getSwipeActionHelper());
snoozeGuts.setStatusBarNotification(sbn);
snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
guts.setHeightChangedListener((NotificationGuts g) -> {
- mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+ mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
});
}
@@ -258,7 +256,7 @@
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
if (resetMenu) {
- mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
+ mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
}
}
@@ -351,7 +349,7 @@
!mAccessibilityManager.isTouchExplorationEnabled());
guts.setExposed(true /* exposed */, needsFalsingProtection);
row.closeRemoteInput();
- mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+ mListContainer.onHeightChanged(row, true /* needsAnimation */);
mNotificationGutsExposed = guts;
mGutsMenuItem = item;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
new file mode 100644
index 0000000..43be44d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
@@ -0,0 +1,182 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+
+/**
+ * Interface representing the entity that contains notifications. It can have
+ * notification views added and removed from it, and will manage displaying them to the user.
+ */
+public interface NotificationListContainer {
+
+ /**
+ * Called when a child is being transferred.
+ *
+ * @param childTransferInProgress whether child transfer is in progress
+ */
+ void setChildTransferInProgress(boolean childTransferInProgress);
+
+ /**
+ * Change the position of child to a new location
+ *
+ * @param child the view to change the position for
+ * @param newIndex the new index
+ */
+ void changeViewPosition(View child, int newIndex);
+
+ /**
+ * Called when a child was added to a group.
+ *
+ * @param row row of the group child that was added
+ */
+ void notifyGroupChildAdded(View row);
+
+ /**
+ * Called when a child was removed from a group.
+ *
+ * @param row row of the child that was removed
+ * @param childrenContainer ViewGroup of the group that the child was removed from
+ */
+ void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+
+ /**
+ * Generate an animation for an added child view.
+ *
+ * @param child The view to be added.
+ * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
+ */
+ void generateAddAnimation(View child, boolean fromMoreCard);
+
+ /**
+ * Generate a child order changed event.
+ */
+ void generateChildOrderChangedEvent();
+
+ /**
+ * Returns the number of children in the NotificationListContainer.
+ *
+ * @return the number of children in the NotificationListContainer
+ */
+ int getContainerChildCount();
+
+ /**
+ * Gets the ith child in the NotificationListContainer.
+ *
+ * @param i ith child to get
+ * @return the ith child in the list container
+ */
+ View getContainerChildAt(int i);
+
+ /**
+ * Remove a view from the container
+ *
+ * @param v view to remove
+ */
+ void removeContainerView(View v);
+
+ /**
+ * Add a view to the container
+ *
+ * @param v view to add
+ */
+ void addContainerView(View v);
+
+ /**
+ * Sets the maximum number of notifications to display.
+ *
+ * @param maxNotifications max number of notifications to display
+ */
+ void setMaxDisplayedNotifications(int maxNotifications);
+
+ /**
+ * Handle snapping a non-dismissable row back if the user tried to dismiss it.
+ *
+ * @param row row to snap back
+ */
+ void snapViewIfNeeded(ExpandableNotificationRow row);
+
+ /**
+ * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
+ *
+ * @param entry entry to get the view parent for
+ * @return the view parent for entry
+ */
+ ViewGroup getViewParentForNotification(NotificationData.Entry entry);
+
+ /**
+ * Called when the height of an expandable view changes.
+ *
+ * @param view view whose height changed
+ * @param animate whether this change should be animated
+ */
+ void onHeightChanged(ExpandableView view, boolean animate);
+
+ /**
+ * Resets the currently exposed menu view.
+ *
+ * @param animate whether to animate the closing/change of menu view
+ * @param force reset the menu view even if it looks like it is already reset
+ */
+ void resetExposedMenuView(boolean animate, boolean force);
+
+ /**
+ * Returns the NotificationSwipeActionHelper for the NotificationListContainer.
+ *
+ * @return swipe action helper for the list container
+ */
+ NotificationSwipeActionHelper getSwipeActionHelper();
+
+ /**
+ * Called when a notification is removed from the shade. This cleans up the state for a
+ * given view.
+ *
+ * @param view view to clean up view state for
+ */
+ void cleanUpViewState(View view);
+
+ /**
+ * Returns whether an ExpandableNotificationRow is in a visible location or not.
+ *
+ * @param row
+ * @return true if row is in a visible location
+ */
+ boolean isInVisibleLocation(ExpandableNotificationRow row);
+
+ /**
+ * Sets a listener to listen for changes in notification locations.
+ *
+ * @param listener listener to set
+ */
+ void setChildLocationsChangedListener(
+ NotificationLogger.OnChildLocationsChangedListener listener);
+
+ /**
+ * Called when an update to the notification view hierarchy is completed.
+ */
+ default void onNotificationViewUpdateFinished() {}
+
+ /**
+ * Returns true if there are pulsing notifications.
+ *
+ * @return true if has pulsing notifications
+ */
+ boolean hasPulsingNotifications();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
index e958f3f..9ff7c18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
@@ -28,7 +28,6 @@
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;
@@ -54,14 +53,12 @@
protected Handler mHandler = new Handler();
protected IStatusBarService mBarService;
private long mLastVisibilityReportUptimeMs;
- private NotificationStackScrollLayout mStackScroller;
+ private NotificationListContainer mListContainer;
- protected final NotificationStackScrollLayout.OnChildLocationsChangedListener
- mNotificationLocationsChangedListener =
- new NotificationStackScrollLayout.OnChildLocationsChangedListener() {
+ protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
+ new OnChildLocationsChangedListener() {
@Override
- public void onChildLocationsChanged(
- NotificationStackScrollLayout stackScrollLayout) {
+ public void onChildLocationsChanged() {
if (mHandler.hasCallbacks(mVisibilityReporter)) {
// Visibilities will be reported when the existing
// callback is executed.
@@ -105,7 +102,7 @@
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
- boolean isVisible = mStackScroller.isInVisibleLocation(entry.row);
+ boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible);
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
@@ -143,11 +140,10 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
- // TODO: Remove dependency on NotificationStackScrollLayout.
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationStackScrollLayout stackScroller) {
+ NotificationListContainer listContainer) {
mPresenter = presenter;
- mStackScroller = stackScroller;
+ mListContainer = listContainer;
}
public void stopNotificationLogging() {
@@ -159,18 +155,18 @@
recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
}
mHandler.removeCallbacks(mVisibilityReporter);
- mStackScroller.setChildLocationsChangedListener(null);
+ mListContainer.setChildLocationsChangedListener(null);
}
public void startNotificationLogging() {
- mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+ mListContainer.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);
+ mNotificationLocationsChangedListener.onChildLocationsChanged();
}
private void logNotificationVisibilityChanges(
@@ -220,4 +216,11 @@
public Runnable getVisibilityReporter() {
return mVisibilityReporter;
}
+
+ /**
+ * A listener that is notified when some child locations might have changed.
+ */
+ public interface OnChildLocationsChangedListener {
+ void onChildLocationsChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index c1dd958..ec1a8ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -104,7 +104,6 @@
*/
NotificationEntryManager getEntryManager();
- // TODO: Remove this once the view managing code is pulled out of StatusBar.
/**
* Updates the visual representation of the notifications.
*/
@@ -114,4 +113,17 @@
* @return true iff the device is dozing
*/
boolean isDozing();
+
+ /**
+ * Returns the maximum number of notifications to show while locked.
+ *
+ * @param recompute whether something has changed that means we should recompute this value
+ * @return the maximum number of notifications to show while locked
+ */
+ int getMaxNotificationsWhileLocked(boolean recompute);
+
+ /**
+ * Called when the row states are updated by NotificationViewHierarchyManager.
+ */
+ void onUpdateRowStates();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
new file mode 100644
index 0000000..b73b9b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -0,0 +1,347 @@
+/*
+ * 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.content.res.Resources;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
+ * on their group structure. For example, if a notification becomes bundled with another,
+ * NotificationViewHierarchyManager will update the view hierarchy to reflect that. It also will
+ * tell NotificationListContainer which notifications to display, and inform it of changes to those
+ * notifications that might affect their display.
+ */
+public class NotificationViewHierarchyManager {
+ private static final String TAG = "NotificationViewHierarchyManager";
+
+ private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
+ mTmpChildOrderMap = new HashMap<>();
+ protected final NotificationLockscreenUserManager mLockscreenUserManager;
+ protected final NotificationGroupManager mGroupManager;
+ protected final VisualStabilityManager mVisualStabilityManager;
+
+ /**
+ * {@code true} if notifications not part of a group should by default be rendered in their
+ * expanded state. If {@code false}, then only the first notification will be expanded if
+ * possible.
+ */
+ private final boolean mAlwaysExpandNonGroupedNotification;
+
+ private NotificationPresenter mPresenter;
+ private NotificationEntryManager mEntryManager;
+ private NotificationListContainer mListContainer;
+
+ public NotificationViewHierarchyManager(
+ NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ VisualStabilityManager visualStabilityManager,
+ Context context) {
+ mLockscreenUserManager = lockscreenUserManager;
+ mGroupManager = groupManager;
+ mVisualStabilityManager = visualStabilityManager;
+
+ Resources res = context.getResources();
+ mAlwaysExpandNonGroupedNotification =
+ res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
+ }
+
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationEntryManager entryManager, NotificationListContainer listContainer) {
+ mPresenter = presenter;
+ mEntryManager = entryManager;
+ mListContainer = listContainer;
+ }
+
+ /**
+ * Updates the visual representation of the notifications.
+ */
+ public void updateNotificationViews() {
+ ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
+ .getActiveNotifications();
+ ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
+ final int N = activeNotifications.size();
+ for (int i = 0; i < N; i++) {
+ NotificationData.Entry ent = activeNotifications.get(i);
+ if (ent.row.isDismissed() || ent.row.isRemoved()) {
+ // we don't want to update removed notifications because they could
+ // temporarily become children if they were isolated before.
+ continue;
+ }
+ int userId = ent.notification.getUserId();
+
+ // Display public version of the notification if we need to redact.
+ // TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
+ // We can probably move some of this code there.
+ boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(
+ mLockscreenUserManager.getCurrentUserId());
+ boolean userPublic = devicePublic
+ || mLockscreenUserManager.isLockscreenPublicMode(userId);
+ boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent);
+ boolean sensitive = userPublic && needsRedaction;
+ boolean deviceSensitive = devicePublic
+ && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+ mLockscreenUserManager.getCurrentUserId());
+ ent.row.setSensitive(sensitive, deviceSensitive);
+ ent.row.setNeedsRedaction(needsRedaction);
+ if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
+ ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
+ ent.row.getStatusBarNotification());
+ List<ExpandableNotificationRow> orderedChildren =
+ mTmpChildOrderMap.get(summary);
+ if (orderedChildren == null) {
+ orderedChildren = new ArrayList<>();
+ mTmpChildOrderMap.put(summary, orderedChildren);
+ }
+ orderedChildren.add(ent.row);
+ } else {
+ toShow.add(ent.row);
+ }
+
+ }
+
+ ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+ for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
+ View child = mListContainer.getContainerChildAt(i);
+ if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
+ toRemove.add((ExpandableNotificationRow) child);
+ }
+ }
+
+ for (ExpandableNotificationRow remove : toRemove) {
+ if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
+ // we are only transferring this notification to its parent, don't generate an
+ // animation
+ mListContainer.setChildTransferInProgress(true);
+ }
+ if (remove.isSummaryWithChildren()) {
+ remove.removeAllChildren();
+ }
+ mListContainer.removeContainerView(remove);
+ mListContainer.setChildTransferInProgress(false);
+ }
+
+ removeNotificationChildren();
+
+ for (int i = 0; i < toShow.size(); i++) {
+ View v = toShow.get(i);
+ if (v.getParent() == null) {
+ mVisualStabilityManager.notifyViewAddition(v);
+ mListContainer.addContainerView(v);
+ }
+ }
+
+ addNotificationChildrenAndSort();
+
+ // So after all this work notifications still aren't sorted correctly.
+ // Let's do that now by advancing through toShow and mListContainer in
+ // lock-step, making sure mListContainer matches what we see in toShow.
+ int j = 0;
+ for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
+ View child = mListContainer.getContainerChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow targetChild = toShow.get(j);
+ if (child != targetChild) {
+ // Oops, wrong notification at this position. Put the right one
+ // here and advance both lists.
+ if (mVisualStabilityManager.canReorderNotification(targetChild)) {
+ mListContainer.changeViewPosition(targetChild, i);
+ } else {
+ mVisualStabilityManager.addReorderingAllowedCallback(mEntryManager);
+ }
+ }
+ j++;
+
+ }
+
+ mVisualStabilityManager.onReorderingFinished();
+ // clear the map again for the next usage
+ mTmpChildOrderMap.clear();
+
+ updateRowStates();
+
+ mListContainer.onNotificationViewUpdateFinished();
+ }
+
+ private void addNotificationChildrenAndSort() {
+ // Let's now add all notification children which are missing
+ boolean orderChanged = false;
+ for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
+ View view = mListContainer.getContainerChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+ for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
+ childIndex++) {
+ ExpandableNotificationRow childView = orderedChildren.get(childIndex);
+ if (children == null || !children.contains(childView)) {
+ if (childView.getParent() != null) {
+ Log.wtf(TAG, "trying to add a notification child that already has " +
+ "a parent. class:" + childView.getParent().getClass() +
+ "\n child: " + childView);
+ // This shouldn't happen. We can recover by removing it though.
+ ((ViewGroup) childView.getParent()).removeView(childView);
+ }
+ mVisualStabilityManager.notifyViewAddition(childView);
+ parent.addChildNotification(childView, childIndex);
+ mListContainer.notifyGroupChildAdded(childView);
+ }
+ }
+
+ // Finally after removing and adding has been performed we can apply the order.
+ orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager,
+ mEntryManager);
+ }
+ if (orderChanged) {
+ mListContainer.generateChildOrderChangedEvent();
+ }
+ }
+
+ private void removeNotificationChildren() {
+ // First let's remove all children which don't belong in the parents
+ ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+ for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
+ View view = mListContainer.getContainerChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+ if (children != null) {
+ toRemove.clear();
+ for (ExpandableNotificationRow childRow : children) {
+ if ((orderedChildren == null
+ || !orderedChildren.contains(childRow))
+ && !childRow.keepInParent()) {
+ toRemove.add(childRow);
+ }
+ }
+ for (ExpandableNotificationRow remove : toRemove) {
+ parent.removeChildNotification(remove);
+ if (mEntryManager.getNotificationData().get(
+ remove.getStatusBarNotification().getKey()) == null) {
+ // We only want to add an animation if the view is completely removed
+ // otherwise it's just a transfer
+ mListContainer.notifyGroupChildRemoved(remove,
+ parent.getChildrenContainer());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates expanded, dimmed and locked states of notification rows.
+ */
+ public void updateRowStates() {
+ final int N = mListContainer.getContainerChildCount();
+
+ int visibleNotifications = 0;
+ boolean isLocked = mPresenter.isPresenterLocked();
+ int maxNotifications = -1;
+ if (isLocked) {
+ maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
+ }
+ mListContainer.setMaxDisplayedNotifications(maxNotifications);
+ Stack<ExpandableNotificationRow> stack = new Stack<>();
+ for (int i = N - 1; i >= 0; i--) {
+ View child = mListContainer.getContainerChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ stack.push((ExpandableNotificationRow) child);
+ }
+ while(!stack.isEmpty()) {
+ ExpandableNotificationRow row = stack.pop();
+ NotificationData.Entry entry = row.getEntry();
+ boolean isChildNotification =
+ mGroupManager.isChildInGroupWithSummary(entry.notification);
+
+ row.setOnKeyguard(isLocked);
+
+ if (!isLocked) {
+ // If mAlwaysExpandNonGroupedNotification is false, then only expand the
+ // very first notification and if it's not a child of grouped notifications.
+ row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
+ || (visibleNotifications == 0 && !isChildNotification
+ && !row.isLowPriority()));
+ }
+
+ entry.row.setShowAmbient(mPresenter.isDozing());
+ int userId = entry.notification.getUserId();
+ boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
+ entry.notification) && !entry.row.isRemoved();
+ boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
+ .notification);
+ if (suppressedSummary
+ || (mLockscreenUserManager.isLockscreenPublicMode(userId)
+ && !mLockscreenUserManager.shouldShowLockscreenNotifications())
+ || (isLocked && !showOnKeyguard)) {
+ entry.row.setVisibility(View.GONE);
+ } else {
+ boolean wasGone = entry.row.getVisibility() == View.GONE;
+ if (wasGone) {
+ entry.row.setVisibility(View.VISIBLE);
+ }
+ if (!isChildNotification && !entry.row.isRemoved()) {
+ if (wasGone) {
+ // notify the scroller of a child addition
+ mListContainer.generateAddAnimation(entry.row,
+ !showOnKeyguard /* fromMoreCard */);
+ }
+ visibleNotifications++;
+ }
+ }
+ if (row.isSummaryWithChildren()) {
+ List<ExpandableNotificationRow> notificationChildren =
+ row.getNotificationChildren();
+ int size = notificationChildren.size();
+ for (int i = size - 1; i >= 0; i--) {
+ stack.push(notificationChildren.get(i));
+ }
+ }
+ }
+
+ mPresenter.onUpdateRowStates();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 61dd22f..f0bd1f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -455,7 +455,7 @@
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
- mStatusBar.getMaxKeyguardNotifications(),
+ mStatusBar.getMaxNotificationsWhileLocked(),
getMaxPanelHeight(),
getExpandedHeight(),
mNotificationStackScroller.getNotGoneChildCount(),
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 04fe7f2..72a52d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -28,7 +28,6 @@
.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -198,6 +197,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
@@ -233,14 +233,12 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Stack;
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
- OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks,
+ OnHeadsUpChangedListener, CommandQueue.Callbacks,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
public static final boolean MULTIUSER_DEBUG = false;
@@ -371,13 +369,6 @@
protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
private TextView mNotificationPanelDebugText;
- /**
- * {@code true} if notifications not part of a group should by default be rendered in their
- * expanded state. If {@code false}, then only the first notification will be expanded if
- * possible.
- */
- private boolean mAlwaysExpandNonGroupedNotification;
-
// settings
private QSPanel mQSPanel;
@@ -407,6 +398,7 @@
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ protected NotificationViewHierarchyManager mViewHierarchyManager;
// for disabling the status bar
private int mDisabled1 = 0;
@@ -577,8 +569,6 @@
goToLockedShade(null);
}
};
- private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
- mTmpChildOrderMap = new HashMap<>();
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
@@ -598,6 +588,7 @@
@Override
public void start() {
mGroupManager = Dependency.get(NotificationGroupManager.class);
+ mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
mNotificationLogger = Dependency.get(NotificationLogger.class);
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mNotificationListener = Dependency.get(NotificationListener.class);
@@ -616,6 +607,7 @@
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
@@ -628,8 +620,6 @@
Resources res = mContext.getResources();
mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
- mAlwaysExpandNonGroupedNotification =
- res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
putComponent(StatusBar.class, this);
@@ -814,8 +804,8 @@
mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
- mEntryManager.setUpWithPresenter(this, mStackScroller, this, mVisualStabilityManager,
- mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
+ mViewHierarchyManager.setUpWithPresenter(this, mEntryManager, mStackScroller);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
@@ -1361,112 +1351,8 @@
return;
}
- ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
- .getActiveNotifications();
- ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
- final int N = activeNotifications.size();
- for (int i = 0; i < N; i++) {
- Entry ent = activeNotifications.get(i);
- if (ent.row.isDismissed() || ent.row.isRemoved()) {
- // we don't want to update removed notifications because they could
- // temporarily become children if they were isolated before.
- continue;
- }
- int userId = ent.notification.getUserId();
+ mViewHierarchyManager.updateNotificationViews();
- // Display public version of the notification if we need to redact.
- // TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
- // We can probably move some of this code there.
- boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(
- mLockscreenUserManager.getCurrentUserId());
- boolean userPublic = devicePublic
- || mLockscreenUserManager.isLockscreenPublicMode(userId);
- boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent);
- boolean sensitive = userPublic && needsRedaction;
- boolean deviceSensitive = devicePublic
- && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
- mLockscreenUserManager.getCurrentUserId());
- ent.row.setSensitive(sensitive, deviceSensitive);
- ent.row.setNeedsRedaction(needsRedaction);
- if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
- ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
- ent.row.getStatusBarNotification());
- List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary);
- if (orderedChildren == null) {
- orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary, orderedChildren);
- }
- orderedChildren.add(ent.row);
- } else {
- toShow.add(ent.row);
- }
-
- }
-
- ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
- for (int i=0; i< mStackScroller.getChildCount(); i++) {
- View child = mStackScroller.getChildAt(i);
- if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
- toRemove.add((ExpandableNotificationRow) child);
- }
- }
-
- for (ExpandableNotificationRow remove : toRemove) {
- if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
- // we are only transferring this notification to its parent, don't generate an
- // animation
- mStackScroller.setChildTransferInProgress(true);
- }
- if (remove.isSummaryWithChildren()) {
- remove.removeAllChildren();
- }
- mStackScroller.removeView(remove);
- mStackScroller.setChildTransferInProgress(false);
- }
-
- removeNotificationChildren();
-
- for (int i = 0; i < toShow.size(); i++) {
- View v = toShow.get(i);
- if (v.getParent() == null) {
- mVisualStabilityManager.notifyViewAddition(v);
- mStackScroller.addView(v);
- }
- }
-
- addNotificationChildrenAndSort();
-
- // So after all this work notifications still aren't sorted correctly.
- // Let's do that now by advancing through toShow and mStackScroller in
- // lock-step, making sure mStackScroller matches what we see in toShow.
- int j = 0;
- for (int i = 0; i < mStackScroller.getChildCount(); i++) {
- View child = mStackScroller.getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)) {
- // We don't care about non-notification views.
- continue;
- }
-
- ExpandableNotificationRow targetChild = toShow.get(j);
- if (child != targetChild) {
- // Oops, wrong notification at this position. Put the right one
- // here and advance both lists.
- if (mVisualStabilityManager.canReorderNotification(targetChild)) {
- mStackScroller.changeViewPosition(targetChild, i);
- } else {
- mVisualStabilityManager.addReorderingAllowedCallback(this);
- }
- }
- j++;
-
- }
-
- mVisualStabilityManager.onReorderingFinished();
- // clear the map again for the next usage
- mTmpChildOrderMap.clear();
-
- updateRowStates();
updateSpeedBumpIndex();
updateClearAll();
updateEmptyShadeView();
@@ -1521,82 +1407,6 @@
&& !ONLY_CORE_APPS);
}
- private void addNotificationChildrenAndSort() {
- // Let's now add all notification children which are missing
- boolean orderChanged = false;
- for (int i = 0; i < mStackScroller.getChildCount(); i++) {
- View view = mStackScroller.getChildAt(i);
- if (!(view instanceof ExpandableNotificationRow)) {
- // We don't care about non-notification views.
- continue;
- }
-
- ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
-
- for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
- childIndex++) {
- ExpandableNotificationRow childView = orderedChildren.get(childIndex);
- if (children == null || !children.contains(childView)) {
- if (childView.getParent() != null) {
- Log.wtf(TAG, "trying to add a notification child that already has " +
- "a parent. class:" + childView.getParent().getClass() +
- "\n child: " + childView);
- // This shouldn't happen. We can recover by removing it though.
- ((ViewGroup) childView.getParent()).removeView(childView);
- }
- mVisualStabilityManager.notifyViewAddition(childView);
- parent.addChildNotification(childView, childIndex);
- mStackScroller.notifyGroupChildAdded(childView);
- }
- }
-
- // Finally after removing and adding has been performed we can apply the order.
- orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this);
- }
- if (orderChanged) {
- mStackScroller.generateChildOrderChangedEvent();
- }
- }
-
- private void removeNotificationChildren() {
- // First let's remove all children which don't belong in the parents
- ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
- for (int i = 0; i < mStackScroller.getChildCount(); i++) {
- View view = mStackScroller.getChildAt(i);
- if (!(view instanceof ExpandableNotificationRow)) {
- // We don't care about non-notification views.
- continue;
- }
-
- ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
-
- if (children != null) {
- toRemove.clear();
- for (ExpandableNotificationRow childRow : children) {
- if ((orderedChildren == null
- || !orderedChildren.contains(childRow))
- && !childRow.keepInParent()) {
- toRemove.add(childRow);
- }
- }
- for (ExpandableNotificationRow remove : toRemove) {
- parent.removeChildNotification(remove);
- if (mEntryManager.getNotificationData().get(
- remove.getStatusBarNotification().getKey()) == null) {
- // We only want to add an animation if the view is completely removed
- // otherwise it's just a transfer
- mStackScroller.notifyGroupChildRemoved(remove,
- parent.getChildrenContainer());
- }
- }
- }
- }
- }
-
public void addQsTile(ComponentName tile) {
mQSPanel.getHost().addTile(tile);
}
@@ -2171,11 +1981,6 @@
return mDozeScrimController != null && mDozeScrimController.isPulsing();
}
- @Override
- public void onReorderingAllowed() {
- mEntryManager.updateNotifications();
- }
-
public boolean isLaunchTransitionFadingAway() {
return mLaunchTransitionFadingAway;
}
@@ -3146,7 +2951,7 @@
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
- updateRowStates();
+ mViewHierarchyManager.updateRowStates();
mScreenPinningRequest.onConfigurationChanged();
}
@@ -3919,7 +3724,7 @@
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDark(mDozing, animate);
updateQsExpansionEnabled();
- updateRowStates();
+ mViewHierarchyManager.updateRowStates();
Trace.endSection();
}
@@ -4123,7 +3928,8 @@
}
}
- protected int getMaxKeyguardNotifications(boolean recompute) {
+ @Override
+ public int getMaxNotificationsWhileLocked(boolean recompute) {
if (recompute) {
mMaxKeyguardNotifications = Math.max(1,
mNotificationPanel.computeMaxKeyguardNotifications(
@@ -4133,8 +3939,8 @@
return mMaxKeyguardNotifications;
}
- public int getMaxKeyguardNotifications() {
- return getMaxKeyguardNotifications(false /* recompute */);
+ public int getMaxNotificationsWhileLocked() {
+ return getMaxNotificationsWhileLocked(false /* recompute */);
}
// TODO: Figure out way to remove these.
@@ -4962,8 +4768,7 @@
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
- protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
-
+ protected VisualStabilityManager mVisualStabilityManager;
protected AccessibilityManager mAccessibilityManager;
@@ -5340,10 +5145,10 @@
if (mState == StatusBarState.KEYGUARD) {
// Since the number of notifications is determined based on the height of the view, we
// need to update them.
- int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
- int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
+ int maxBefore = getMaxNotificationsWhileLocked(false /* recompute */);
+ int maxNotifications = getMaxNotificationsWhileLocked(true /* recompute */);
if (maxBefore != maxNotifications) {
- updateRowStates();
+ mViewHierarchyManager.updateRowStates();
}
}
}
@@ -5444,76 +5249,8 @@
/**
* Updates expanded, dimmed and locked states of notification rows.
*/
- protected void updateRowStates() {
- final int N = mStackScroller.getChildCount();
-
- int visibleNotifications = 0;
- boolean onKeyguard = mState == StatusBarState.KEYGUARD;
- int maxNotifications = -1;
- if (onKeyguard) {
- maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
- }
- mStackScroller.setMaxDisplayedNotifications(maxNotifications);
- Stack<ExpandableNotificationRow> stack = new Stack<>();
- for (int i = N - 1; i >= 0; i--) {
- View child = mStackScroller.getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)) {
- continue;
- }
- stack.push((ExpandableNotificationRow) child);
- }
- while(!stack.isEmpty()) {
- ExpandableNotificationRow row = stack.pop();
- NotificationData.Entry entry = row.getEntry();
- boolean isChildNotification =
- mGroupManager.isChildInGroupWithSummary(entry.notification);
-
- row.setOnKeyguard(onKeyguard);
-
- if (!onKeyguard) {
- // If mAlwaysExpandNonGroupedNotification is false, then only expand the
- // very first notification and if it's not a child of grouped notifications.
- row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
- || (visibleNotifications == 0 && !isChildNotification
- && !row.isLowPriority()));
- }
-
- entry.row.setShowAmbient(isDozing());
- int userId = entry.notification.getUserId();
- boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.row.isRemoved();
- boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
- .notification);
- if (suppressedSummary
- || (mLockscreenUserManager.isLockscreenPublicMode(userId)
- && !mLockscreenUserManager.shouldShowLockscreenNotifications())
- || (onKeyguard && !showOnKeyguard)) {
- entry.row.setVisibility(View.GONE);
- } else {
- boolean wasGone = entry.row.getVisibility() == View.GONE;
- if (wasGone) {
- entry.row.setVisibility(View.VISIBLE);
- }
- if (!isChildNotification && !entry.row.isRemoved()) {
- if (wasGone) {
- // notify the scroller of a child addition
- mStackScroller.generateAddAnimation(entry.row,
- !showOnKeyguard /* fromMoreCard */);
- }
- visibleNotifications++;
- }
- }
- if (row.isSummaryWithChildren()) {
- List<ExpandableNotificationRow> notificationChildren =
- row.getNotificationChildren();
- int size = notificationChildren.size();
- for (int i = size - 1; i >= 0; i--) {
- stack.push(notificationChildren.get(i));
- }
- }
- }
- mNotificationPanel.setNoVisibleNotifications(visibleNotifications == 0);
-
+ @Override
+ public void onUpdateRowStates() {
// The following views will be moved to the end of mStackScroller. This counter represents
// the offset from the last child. Initialized to 1 for the very last position. It is post-
// incremented in the following "changeViewPosition" calls so that its value is correct for
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index fe39a89..369e7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -80,6 +80,8 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationListContainer;
+import com.android.systemui.statusbar.NotificationLogger;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.StackScrollerDecorView;
@@ -112,7 +114,8 @@
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
- NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider {
+ NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
+ NotificationListContainer {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -207,7 +210,7 @@
* The raw amount of the overScroll on the bottom, which is not rubber-banded.
*/
private float mOverScrolledBottomPixels;
- private OnChildLocationsChangedListener mListener;
+ private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
@@ -447,6 +450,7 @@
}
}
+ @Override
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@@ -614,7 +618,9 @@
mNoAmbient = noAmbient;
}
- public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
+ @Override
+ public void setChildLocationsChangedListener(
+ NotificationLogger.OnChildLocationsChangedListener listener) {
mListener = listener;
}
@@ -1325,6 +1331,7 @@
true /* isDismissAll */);
}
+ @Override
public void snapViewIfNeeded(ExpandableNotificationRow child) {
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
@@ -1333,6 +1340,11 @@
}
@Override
+ public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+ return this;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked()== MotionEvent.ACTION_UP;
@@ -2053,6 +2065,7 @@
return mAmbientState.isPulsing(entry);
}
+ @Override
public boolean hasPulsingNotifications() {
return mPulsing != null;
}
@@ -2610,10 +2623,7 @@
}
}
- /**
- * Called when a notification is removed from the shade. This cleans up the state for a given
- * view.
- */
+ @Override
public void cleanUpViewState(View child) {
if (child == mTranslatingParentView) {
mTranslatingParentView = null;
@@ -2922,10 +2932,12 @@
}
}
+ @Override
public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
+ @Override
public void notifyGroupChildAdded(View row) {
onViewAddedInternal(row);
}
@@ -2963,12 +2975,8 @@
return mNeedsAnimation
&& (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
}
- /**
- * Generate an animation for an added child view.
- *
- * @param child The view to be added.
- * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
- */
+
+ @Override
public void generateAddAnimation(View child, boolean fromMoreCard) {
if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
// Generate Animations
@@ -2984,12 +2992,7 @@
}
}
- /**
- * Change the position of child to a new location
- *
- * @param child the view to change the position for
- * @param newIndex the new index
- */
+ @Override
public void changeViewPosition(View child, int newIndex) {
int currentIndex = indexOfChild(child);
if (child != null && child.getParent() == this && currentIndex != newIndex) {
@@ -3705,7 +3708,7 @@
private void applyCurrentState() {
mCurrentStackScrollState.apply();
if (mListener != null) {
- mListener.onChildLocationsChanged(this);
+ mListener.onChildLocationsChanged();
}
runAnimationFinishedRunnables();
setAnimationRunning(false);
@@ -4189,6 +4192,26 @@
}
}
+ @Override
+ public int getContainerChildCount() {
+ return getChildCount();
+ }
+
+ @Override
+ public View getContainerChildAt(int i) {
+ return getChildAt(i);
+ }
+
+ @Override
+ public void removeContainerView(View v) {
+ removeView(v);
+ }
+
+ @Override
+ public void addContainerView(View v) {
+ addView(v);
+ }
+
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
}
@@ -4445,13 +4468,6 @@
}
/**
- * A listener that is notified when some child locations might have changed.
- */
- public interface OnChildLocationsChangedListener {
- void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
- }
-
- /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
public interface OnEmptySpaceClickListener {
@@ -4706,6 +4722,7 @@
}
}
+ @Override
public void resetExposedMenuView(boolean animate, boolean force) {
mSwipeHelper.resetExposedMenuView(animate, force);
}