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;
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(
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),
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));
}
}
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;
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);
}
/**
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;
Dependency.get(NotificationListener.class),
Dependency.get(MetricsLogger.class),
Dependency.get(DeviceProvisionedController.class),
+ Dependency.get(VisualStabilityManager.class),
Dependency.get(UiOffloadThread.class),
context));
}
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;
* 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;
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;
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
protected Callback mCallback;
- protected NotificationStackScrollLayout mStackScroller;
protected PowerManager mPowerManager;
protected SystemServicesProxy mSystemServicesProxy;
protected NotificationListenerService.RankingMap mLatestRankingMap;
protected ContentObserver mHeadsUpObserver;
protected boolean mUseHeadsUp = false;
protected boolean mDisableNotificationAlerts;
- protected VisualStabilityManager mVisualStabilityManager;
+ protected NotificationListContainer mListContainer;
private final class NotificationClicker implements View.OnClickListener {
NotificationListener notificationListener,
MetricsLogger metricsLogger,
DeviceProvisionedController deviceProvisionedController,
+ VisualStabilityManager visualStabilityManager,
UiOffloadThread uiOffloadThread, Context context) {
mLockscreenUserManager = lockscreenUserManager;
mGroupManager = groupManager;
mNotificationListener = notificationListener;
mMetricsLogger = metricsLogger;
mDeviceProvisionedController = deviceProvisionedController;
+ mVisualStabilityManager = visualStabilityManager;
mUiOffloadThread = uiOffloadThread;
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
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
});
}
+ @Override
+ public void onReorderingAllowed() {
+ updateNotifications();
+ }
+
private boolean shouldSuppressFullScreenIntent(String key) {
if (mPresenter.isDeviceInVrMode()) {
return true;
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);
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);
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
- inflateViews(entry, mStackScroller);
+ inflateViews(entry, mListContainer.getViewParentForNotification(entry));
return entry;
}
mGroupManager.onEntryUpdated(entry, oldNotification);
entry.updateIcons(mContext, notification);
- inflateViews(entry, mStackScroller);
+ inflateViews(entry, mListContainer.getViewParentForNotification(entry));
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
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) {
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;
// 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;
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;
}
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) {
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 */);
});
}
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
if (resetMenu) {
- mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
+ mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
}
}
!mAccessibilityManager.isTouchExplorationEnabled());
guts.setExposed(true /* exposed */, needsFalsingProtection);
row.closeRemoteInput();
- mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+ mListContainer.onHeightChanged(row, true /* needsAnimation */);
mNotificationGutsExposed = guts;
mGutsMenuItem = item;
}
--- /dev/null
+/*
+ * 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();
+}
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;
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.
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) {
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() {
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(
public Runnable getVisibilityReporter() {
return mVisibilityReporter;
}
+
+ /**
+ * A listener that is notified when some child locations might have changed.
+ */
+ public interface OnChildLocationsChangedListener {
+ void onChildLocationsChanged();
+ }
}
*/
NotificationEntryManager getEntryManager();
- // TODO: Remove this once the view managing code is pulled out of StatusBar.
/**
* Updates the visual representation of the notifications.
*/
* @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();
}
--- /dev/null
+/*
+ * 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();
+ }
+}
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
- mStatusBar.getMaxKeyguardNotifications(),
+ mStatusBar.getMaxNotificationsWhileLocked(),
getMaxPanelHeight(),
getExpandedHeight(),
mNotificationStackScroller.getNotGoneChildCount(),
.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;
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;
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;
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;
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ protected NotificationViewHierarchyManager mViewHierarchyManager;
// for disabling the status bar
private int mDisabled1 = 0;
goToLockedShade(null);
}
};
- private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
- mTmpChildOrderMap = new HashMap<>();
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
@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);
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);
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);
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);
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();
-
- // 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;
- }
+ mViewHierarchyManager.updateNotificationViews();
- 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();
&& !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);
}
return mDozeScrimController != null && mDozeScrimController.isPulsing();
}
- @Override
- public void onReorderingAllowed() {
- mEntryManager.updateNotifications();
- }
-
public boolean isLaunchTransitionFadingAway() {
return mLaunchTransitionFadingAway;
}
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
- updateRowStates();
+ mViewHierarchyManager.updateRowStates();
mScreenPinningRequest.onConfigurationChanged();
}
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDark(mDozing, animate);
updateQsExpansionEnabled();
- updateRowStates();
+ mViewHierarchyManager.updateRowStates();
Trace.endSection();
}
}
}
- protected int getMaxKeyguardNotifications(boolean recompute) {
+ @Override
+ public int getMaxNotificationsWhileLocked(boolean recompute) {
if (recompute) {
mMaxKeyguardNotifications = Math.max(1,
mNotificationPanel.computeMaxKeyguardNotifications(
return mMaxKeyguardNotifications;
}
- public int getMaxKeyguardNotifications() {
- return getMaxKeyguardNotifications(false /* recompute */);
+ public int getMaxNotificationsWhileLocked() {
+ return getMaxNotificationsWhileLocked(false /* recompute */);
}
// TODO: Figure out way to remove these.
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
- protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
-
+ protected VisualStabilityManager mVisualStabilityManager;
protected AccessibilityManager mAccessibilityManager;
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();
}
}
}
/**
* 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
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;
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";
* 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;
}
}
+ @Override
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
mNoAmbient = noAmbient;
}
- public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
+ @Override
+ public void setChildLocationsChangedListener(
+ NotificationLogger.OnChildLocationsChangedListener listener) {
mListener = listener;
}
true /* isDismissAll */);
}
+ @Override
public void snapViewIfNeeded(ExpandableNotificationRow child) {
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
}
@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;
return mAmbientState.isPulsing(entry);
}
+ @Override
public boolean hasPulsingNotifications() {
return mPulsing != null;
}
}
}
- /**
- * 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;
}
}
+ @Override
public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
+ @Override
public void notifyGroupChildAdded(View row) {
onViewAddedInternal(row);
}
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
}
}
- /**
- * 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) {
private void applyCurrentState() {
mCurrentStackScrollState.apply();
if (mListener != null) {
- mListener.onChildLocationsChanged(this);
+ mListener.onChildLocationsChanged();
}
runAnimationFinishedRunnables();
setAnimationRunning(false);
}
}
+ @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);
}
}
/**
- * 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 {
}
}
+ @Override
public void resetExposedMenuView(boolean animate, boolean force) {
mSwipeHelper.resetExposedMenuView(animate, force);
}
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import org.junit.Before;
import org.junit.Test;
@Mock private NotificationListener mNotificationListener;
@Mock private MetricsLogger mMetricsLogger;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private NotificationStackScrollLayout mStackScroller;
- @Mock private NotificationEntryManager.Callback mCallback;
@Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private NotificationListContainer mListContainer;
+ @Mock private NotificationEntryManager.Callback mCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationListenerService.RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
NotificationListener notificationListener,
MetricsLogger metricsLogger,
DeviceProvisionedController deviceProvisionedController,
+ VisualStabilityManager visualStabilityManager,
UiOffloadThread uiOffloadThread, Context context,
IStatusBarService barService) {
super(lockscreenUserManager, groupManager, gutsManager, remoteInputManager,
mediaManager, foregroundServiceController, notificationListener, metricsLogger,
- deviceProvisionedController, uiOffloadThread, context);
+ deviceProvisionedController, visualStabilityManager, uiOffloadThread, context);
mBarService = barService;
mCountDownLatch = new CountDownLatch(1);
mUseHeadsUp = true;
when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager);
when(mPresenter.getGroupManager()).thenReturn(mGroupManager);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- // Necessary for layout inflation.
- when(mStackScroller.generateLayoutParams(any())).thenReturn(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ when(mListContainer.getViewParentForNotification(any())).thenReturn(
+ new FrameLayout(mContext));
Notification.Builder n = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
mEntryManager = new TestableNotificationEntryManager(mLockscreenUserManager,
mGroupManager, mGutsManager, mRemoteInputManager, mMediaManager,
mForegroundServiceController, mNotificationListener, mMetricsLogger,
- mDeviceProvisionedController, mDependency.get(UiOffloadThread.class), mContext,
+ mDeviceProvisionedController, mVisualStabilityManager,
+ mDependency.get(UiOffloadThread.class), mContext,
mBarService);
- mEntryManager.setUpWithPresenter(mPresenter, mStackScroller, mCallback,
- mVisualStabilityManager, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
}
@Test
verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
verify(mRemoteInputManager).onRemoveNotification(mEntry);
verify(mForegroundServiceController).removeNotification(mSbn);
- verify(mStackScroller).cleanUpViewState(mRow);
+ verify(mListContainer).cleanUpViewState(mRow);
verify(mPresenter).updateNotificationViews();
verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
verify(mRow).setRemoved();
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;
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationListener mListener;
- @Mock private NotificationStackScrollLayout mStackScroller;
+ @Mock private NotificationListContainer mListContainer;
@Mock private IStatusBarService mBarService;
@Mock private NotificationData mNotificationData;
@Mock private ExpandableNotificationRow mRow;
mLogger = new TestableNotificationLogger(mListener, mDependency.get(UiOffloadThread.class),
mBarService);
- mLogger.setUpWithPresenter(mPresenter, mStackScroller);
+ mLogger.setUpWithPresenter(mPresenter, mListContainer);
}
@Test
public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
- when(mStackScroller.isInVisibleLocation(any())).thenReturn(true);
+ when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
waitForIdleSync(mLogger.getHandlerForTest());
waitForUiOffloadThread();
// |mEntry| won't change visibility, so it shouldn't be reported again:
Mockito.reset(mBarService);
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
waitForIdleSync(mLogger.getHandlerForTest());
waitForUiOffloadThread();
@Test
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
- when(mStackScroller.isInVisibleLocation(any())).thenReturn(true);
+ when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
waitForIdleSync(mLogger.getHandlerForTest());
waitForUiOffloadThread();
Mockito.reset(mBarService);
mHandler = new Handler(Looper.getMainLooper());
}
- public NotificationStackScrollLayout.OnChildLocationsChangedListener
+ public OnChildLocationsChangedListener
getChildLocationsChangedListenerForTest() {
return mNotificationLocationsChangedListener;
}
--- /dev/null
+/*
+ * 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 junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+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.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private NotificationData mNotificationData;
+ @Spy private FakeListContainer mListContainer = new FakeListContainer();
+
+ private NotificationViewHierarchyManager mViewHierarchyManager;
+ private NotificationTestHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHelper = new NotificationTestHelper(mContext);
+
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+ when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
+
+ mViewHierarchyManager = new NotificationViewHierarchyManager(mLockscreenUserManager,
+ mGroupManager, mVisualStabilityManager, mContext);
+ mViewHierarchyManager.setUpWithPresenter(mPresenter, mEntryManager, mListContainer);
+ }
+
+ private NotificationData.Entry createEntry() throws Exception {
+ ExpandableNotificationRow row = mHelper.createRow();
+ NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
+ entry.row = row;
+ return entry;
+ }
+
+ @Test
+ public void testNotificationsBecomingBundled() throws Exception {
+ // Tests 3 top level notifications becoming a single bundled notification with |entry0| as
+ // the summary.
+ NotificationData.Entry entry0 = createEntry();
+ NotificationData.Entry entry1 = createEntry();
+ NotificationData.Entry entry2 = createEntry();
+
+ // Set up the prior state to look like three top level notifications.
+ mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry1.row);
+ mListContainer.addContainerView(entry2.row);
+ when(mNotificationData.getActiveNotifications()).thenReturn(
+ Lists.newArrayList(entry0, entry1, entry2));
+
+ // Set up group manager to report that they should be bundled now.
+ when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
+ when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
+ when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
+ when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+
+ // Run updateNotifications - the view hierarchy should be reorganized.
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(mListContainer).notifyGroupChildAdded(entry1.row);
+ verify(mListContainer).notifyGroupChildAdded(entry2.row);
+ assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+ }
+
+ @Test
+ public void testNotificationsBecomingUnbundled() throws Exception {
+ // Tests a bundled notification becoming three top level notifications.
+ NotificationData.Entry entry0 = createEntry();
+ NotificationData.Entry entry1 = createEntry();
+ NotificationData.Entry entry2 = createEntry();
+ entry0.row.addChildNotification(entry1.row);
+ entry0.row.addChildNotification(entry2.row);
+
+ // Set up the prior state to look like one top level notification.
+ mListContainer.addContainerView(entry0.row);
+ when(mNotificationData.getActiveNotifications()).thenReturn(
+ Lists.newArrayList(entry0, entry1, entry2));
+
+ // Set up group manager to report that they should not be bundled now.
+ when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(false);
+
+ // Run updateNotifications - the view hierarchy should be reorganized.
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(mListContainer).notifyGroupChildRemoved(
+ entry1.row, entry0.row.getChildrenContainer());
+ verify(mListContainer).notifyGroupChildRemoved(
+ entry2.row, entry0.row.getChildrenContainer());
+ assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+ }
+
+ @Test
+ public void testNotificationsBecomingSuppressed() throws Exception {
+ // Tests two top level notifications becoming a suppressed summary and a child.
+ NotificationData.Entry entry0 = createEntry();
+ NotificationData.Entry entry1 = createEntry();
+ entry0.row.addChildNotification(entry1.row);
+
+ // Set up the prior state to look like a top level notification.
+ mListContainer.addContainerView(entry0.row);
+ when(mNotificationData.getActiveNotifications()).thenReturn(
+ Lists.newArrayList(entry0, entry1));
+
+ // Set up group manager to report a suppressed summary now.
+ when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false);
+ when(mGroupManager.isSummaryOfSuppressedGroup(entry0.notification)).thenReturn(true);
+
+ // Run updateNotifications - the view hierarchy should be reorganized.
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(mListContainer).notifyGroupChildRemoved(
+ entry1.row, entry0.row.getChildrenContainer());
+ assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
+ assertEquals(View.GONE, entry0.row.getVisibility());
+ assertEquals(View.VISIBLE, entry1.row.getVisibility());
+ }
+
+ private class FakeListContainer implements NotificationListContainer {
+ final LinearLayout mLayout = new LinearLayout(mContext);
+ final List<View> mRows = Lists.newArrayList();
+
+ @Override
+ public void setChildTransferInProgress(boolean childTransferInProgress) {}
+
+ @Override
+ public void changeViewPosition(View child, int newIndex) {
+ mRows.remove(child);
+ mRows.add(newIndex, child);
+ }
+
+ @Override
+ public void notifyGroupChildAdded(View row) {}
+
+ @Override
+ public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {}
+
+ @Override
+ public void generateAddAnimation(View child, boolean fromMoreCard) {}
+
+ @Override
+ public void generateChildOrderChangedEvent() {}
+
+ @Override
+ public int getContainerChildCount() {
+ return mRows.size();
+ }
+
+ @Override
+ public View getContainerChildAt(int i) {
+ return mRows.get(i);
+ }
+
+ @Override
+ public void removeContainerView(View v) {
+ mLayout.removeView(v);
+ mRows.remove(v);
+ }
+
+ @Override
+ public void addContainerView(View v) {
+ mLayout.addView(v);
+ mRows.add(v);
+ }
+
+ @Override
+ public void setMaxDisplayedNotifications(int maxNotifications) {}
+
+ @Override
+ public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+
+ @Override
+ public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+ return null;
+ }
+
+ @Override
+ public void onHeightChanged(ExpandableView view, boolean animate) {}
+
+ @Override
+ public void resetExposedMenuView(boolean animate, boolean force) {}
+
+ @Override
+ public NotificationSwipeActionHelper getSwipeActionHelper() {
+ return null;
+ }
+
+ @Override
+ public void cleanUpViewState(View view) {}
+
+ @Override
+ public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+ return true;
+ }
+
+ @Override
+ public void setChildLocationsChangedListener(
+ NotificationLogger.OnChildLocationsChangedListener listener) {}
+
+ @Override
+ public boolean hasPulsingNotifications() {
+ return false;
+ }
+ }
+}
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.NotificationListContainer;
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;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@RunWithLooper
public class StatusBarTest extends SysuiTestCase {
- StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- UnlockMethodCache mUnlockMethodCache;
- KeyguardIndicationController mKeyguardIndicationController;
- NotificationStackScrollLayout mStackScroller;
- TestableStatusBar mStatusBar;
- FakeMetricsLogger mMetricsLogger;
- HeadsUpManager mHeadsUpManager;
- NotificationData mNotificationData;
- PowerManager mPowerManager;
- SystemServicesProxy mSystemServicesProxy;
- NotificationPanelView mNotificationPanelView;
- ScrimController mScrimController;
- IStatusBarService mBarService;
- NotificationListener mNotificationListener;
- NotificationLogger mNotificationLogger;
- ArrayList<Entry> mNotificationList;
- FingerprintUnlockController mFingerprintUnlockController;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private UnlockMethodCache mUnlockMethodCache;
+ private KeyguardIndicationController mKeyguardIndicationController;
+ private NotificationStackScrollLayout mStackScroller;
+ private TestableStatusBar mStatusBar;
+ private FakeMetricsLogger mMetricsLogger;
+ private HeadsUpManager mHeadsUpManager;
+ private NotificationData mNotificationData;
+ private PowerManager mPowerManager;
+ private SystemServicesProxy mSystemServicesProxy;
+ private NotificationPanelView mNotificationPanelView;
+ private ScrimController mScrimController;
+ private IStatusBarService mBarService;
+ private NotificationListener mNotificationListener;
+ private NotificationLogger mNotificationLogger;
+ private ArrayList<Entry> mNotificationList;
+ private FingerprintUnlockController mFingerprintUnlockController;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private TestableNotificationEntryManager mEntryManager;
+ private NotificationViewHierarchyManager mViewHierarchyManager;
+ private VisualStabilityManager mVisualStabilityManager;
@Before
public void setup() throws Exception {
mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectMockDependency(ForegroundServiceController.class);
- mDependency.injectMockDependency(NotificationListener.class);
+ mNotificationListener = mDependency.injectMockDependency(NotificationListener.class);
+ mViewHierarchyManager = mDependency.injectMockDependency(
+ NotificationViewHierarchyManager.class);
+ mVisualStabilityManager = mDependency.injectMockDependency(VisualStabilityManager.class);
mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
CommandQueue commandQueue = mock(CommandQueue.class);
when(commandQueue.asBinder()).thenReturn(new Binder());
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));
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
- mNotificationLogger, mEntryManager, mScrimController, mFingerprintUnlockController);
+ mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
+ mEntryManager, mScrimController, mFingerprintUnlockController);
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
doAnswer(invocation -> {
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar,
- mock(VisualStabilityManager.class), mHeadsUpManager, mNotificationData);
+ mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
+ mNotificationData);
mNotificationLogger.setUpWithPresenter(mStatusBar, mStackScroller);
when(mStackScroller.getActivatedChild()).thenReturn(null);
PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
NotificationLogger notificationLogger,
+ VisualStabilityManager visualStabilityManager,
+ NotificationViewHierarchyManager viewHierarchyManager,
TestableNotificationEntryManager entryManager, ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController) {
mStatusBarKeyguardViewManager = man;
mNotificationListener = notificationListener;
mNotificationLogger = notificationLogger;
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
+ mVisualStabilityManager = visualStabilityManager;
+ mViewHierarchyManager = viewHierarchyManager;
mEntryManager = entryManager;
mScrimController = scrimController;
mFingerprintUnlockController = fingerprintUnlockController;
mDependency.get(NotificationListener.class),
metricsLogger,
mDependency.get(DeviceProvisionedController.class),
+ mDependency.get(VisualStabilityManager.class),
mDependency.get(UiOffloadThread.class),
context);
mSystemServicesProxy = systemServicesProxy;
}
public void setUpForTest(NotificationPresenter presenter,
- NotificationStackScrollLayout stackScroller,
+ NotificationListContainer listContainer,
Callback callback,
- VisualStabilityManager visualStabilityManager,
HeadsUpManager headsUpManager,
NotificationData notificationData) {
- super.setUpWithPresenter(presenter, stackScroller, callback, visualStabilityManager,
- headsUpManager);
+ super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
mUseHeadsUp = true;
}