Merge changes I41959627,Iba316772 into rvc-dev
* changes: Bind/unbind notif children content dynamically Support freeing contracted/expanded view
This commit is contained in:
committed by
Android (Google) Code Review
commit
9a979fb217
@@ -30,6 +30,7 @@ import com.android.systemui.bubbles.BubbleController;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.dagger.StatusBarModule;
|
||||
import com.android.systemui.statusbar.notification.DynamicChildBindController;
|
||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
@@ -59,11 +60,12 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
//TODO: change this top <Entry, List<Entry>>?
|
||||
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
|
||||
mTmpChildOrderMap = new HashMap<>();
|
||||
/** Re-usable map of notifications to their sorted children.*/
|
||||
private final HashMap<NotificationEntry, List<NotificationEntry>> mTmpChildOrderMap =
|
||||
new HashMap<>();
|
||||
|
||||
// Dependencies:
|
||||
private final DynamicChildBindController mDynamicChildBindController;
|
||||
protected final NotificationLockscreenUserManager mLockscreenUserManager;
|
||||
protected final NotificationGroupManager mGroupManager;
|
||||
protected final VisualStabilityManager mVisualStabilityManager;
|
||||
@@ -105,7 +107,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
KeyguardBypassController bypassController,
|
||||
BubbleController bubbleController,
|
||||
DynamicPrivacyController privacyController,
|
||||
ForegroundServiceSectionController fgsSectionController) {
|
||||
ForegroundServiceSectionController fgsSectionController,
|
||||
DynamicChildBindController dynamicChildBindController) {
|
||||
mContext = context;
|
||||
mHandler = mainHandler;
|
||||
mLockscreenUserManager = notificationLockscreenUserManager;
|
||||
@@ -121,6 +124,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
mBubbleController = bubbleController;
|
||||
mDynamicPrivacyController = privacyController;
|
||||
privacyController.addListener(this);
|
||||
mDynamicChildBindController = dynamicChildBindController;
|
||||
}
|
||||
|
||||
public void setUpWithPresenter(NotificationPresenter presenter,
|
||||
@@ -175,13 +179,12 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
ent.getRow().setNeedsRedaction(needsRedaction);
|
||||
if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) {
|
||||
NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn());
|
||||
List<ExpandableNotificationRow> orderedChildren =
|
||||
mTmpChildOrderMap.get(summary.getRow());
|
||||
List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(summary);
|
||||
if (orderedChildren == null) {
|
||||
orderedChildren = new ArrayList<>();
|
||||
mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
|
||||
mTmpChildOrderMap.put(summary, orderedChildren);
|
||||
}
|
||||
orderedChildren.add(ent.getRow());
|
||||
orderedChildren.add(ent);
|
||||
} else {
|
||||
toShow.add(ent.getRow());
|
||||
}
|
||||
@@ -260,6 +263,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
|
||||
}
|
||||
|
||||
mDynamicChildBindController.updateChildContentViews(mTmpChildOrderMap);
|
||||
mVisualStabilityManager.onReorderingFinished();
|
||||
// clear the map again for the next usage
|
||||
mTmpChildOrderMap.clear();
|
||||
@@ -274,6 +278,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
private void addNotificationChildrenAndSort() {
|
||||
// Let's now add all notification children which are missing
|
||||
boolean orderChanged = false;
|
||||
ArrayList<ExpandableNotificationRow> orderedRows = new ArrayList<>();
|
||||
for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
|
||||
View view = mListContainer.getContainerChildAt(i);
|
||||
if (!(view instanceof ExpandableNotificationRow)) {
|
||||
@@ -283,11 +288,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
|
||||
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
|
||||
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
|
||||
List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
|
||||
List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
|
||||
|
||||
for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
|
||||
childIndex++) {
|
||||
ExpandableNotificationRow childView = orderedChildren.get(childIndex);
|
||||
ExpandableNotificationRow childView = orderedChildren.get(childIndex).getRow();
|
||||
if (children == null || !children.contains(childView)) {
|
||||
if (childView.getParent() != null) {
|
||||
Log.wtf(TAG, "trying to add a notification child that already has " +
|
||||
@@ -300,11 +305,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
parent.addChildNotification(childView, childIndex);
|
||||
mListContainer.notifyGroupChildAdded(childView);
|
||||
}
|
||||
orderedRows.add(childView);
|
||||
}
|
||||
|
||||
// Finally after removing and adding has been performed we can apply the order.
|
||||
orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager,
|
||||
orderChanged |= parent.applyChildOrder(orderedRows, mVisualStabilityManager,
|
||||
mEntryManager);
|
||||
orderedRows.clear();
|
||||
}
|
||||
if (orderChanged) {
|
||||
mListContainer.generateChildOrderChangedEvent();
|
||||
@@ -323,13 +330,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
||||
|
||||
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
|
||||
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
|
||||
List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
|
||||
List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
|
||||
|
||||
if (children != null) {
|
||||
toRemove.clear();
|
||||
for (ExpandableNotificationRow childRow : children) {
|
||||
if ((orderedChildren == null
|
||||
|| !orderedChildren.contains(childRow))
|
||||
|| !orderedChildren.contains(childRow.getEntry()))
|
||||
&& !childRow.keepInParent()) {
|
||||
toRemove.add(childRow);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
||||
import com.android.systemui.statusbar.SmartReplyController;
|
||||
import com.android.systemui.statusbar.notification.DynamicChildBindController;
|
||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
@@ -138,7 +139,8 @@ public interface StatusBarDependenciesModule {
|
||||
KeyguardBypassController bypassController,
|
||||
BubbleController bubbleController,
|
||||
DynamicPrivacyController privacyController,
|
||||
ForegroundServiceSectionController fgsSectionController) {
|
||||
ForegroundServiceSectionController fgsSectionController,
|
||||
DynamicChildBindController dynamicChildBindController) {
|
||||
return new NotificationViewHierarchyManager(
|
||||
context,
|
||||
mainHandler,
|
||||
@@ -150,7 +152,8 @@ public interface StatusBarDependenciesModule {
|
||||
bypassController,
|
||||
bubbleController,
|
||||
privacyController,
|
||||
fgsSectionController);
|
||||
fgsSectionController,
|
||||
dynamicChildBindController);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.notification;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
|
||||
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
|
||||
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Controller that binds/unbinds views content views on notification group children.
|
||||
*
|
||||
* We currently only show a limited number of notification children even if more exist, so we
|
||||
* can save memory by freeing content views when they're not visible and binding them again when
|
||||
* they get close to being visible.
|
||||
*
|
||||
* Eventually, when {@link NotifPipeline} takes over as the new notification pipeline, we'll have
|
||||
* more control over which notifications even make it to inflation in the first place and be able
|
||||
* to enforce this at an earlier stage at the level of the {@link ExpandableNotificationRow}, but
|
||||
* for now, we're just doing it at the level of content views.
|
||||
*/
|
||||
public class DynamicChildBindController {
|
||||
private final RowContentBindStage mStage;
|
||||
private final int mChildBindCutoff;
|
||||
|
||||
@Inject
|
||||
public DynamicChildBindController(RowContentBindStage stage) {
|
||||
this(stage, CHILD_BIND_CUTOFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param childBindCutoff the cutoff where we no longer bother having content views bound
|
||||
*/
|
||||
DynamicChildBindController(
|
||||
RowContentBindStage stage,
|
||||
int childBindCutoff) {
|
||||
mStage = stage;
|
||||
mChildBindCutoff = childBindCutoff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the child content views, unbinding content views on children that won't be visible
|
||||
* and binding content views on children that will be visible eventually.
|
||||
*
|
||||
* @param groupNotifs map of notification summaries to their children
|
||||
*/
|
||||
public void updateChildContentViews(
|
||||
Map<NotificationEntry, List<NotificationEntry>> groupNotifs) {
|
||||
for (NotificationEntry entry : groupNotifs.keySet()) {
|
||||
List<NotificationEntry> children = groupNotifs.get(entry);
|
||||
for (int j = 0; j < children.size(); j++) {
|
||||
NotificationEntry childEntry = children.get(j);
|
||||
if (j >= mChildBindCutoff) {
|
||||
if (hasChildContent(childEntry)) {
|
||||
freeChildContent(childEntry);
|
||||
}
|
||||
} else {
|
||||
if (!hasChildContent(childEntry)) {
|
||||
bindChildContent(childEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasChildContent(NotificationEntry entry) {
|
||||
ExpandableNotificationRow row = entry.getRow();
|
||||
return row.getPrivateLayout().getContractedChild() != null
|
||||
|| row.getPrivateLayout().getExpandedChild() != null;
|
||||
}
|
||||
|
||||
private void freeChildContent(NotificationEntry entry) {
|
||||
RowContentBindParams params = mStage.getStageParams(entry);
|
||||
params.freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
params.freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
|
||||
mStage.requestRebind(entry, null);
|
||||
}
|
||||
|
||||
private void bindChildContent(NotificationEntry entry) {
|
||||
RowContentBindParams params = mStage.getStageParams(entry);
|
||||
params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
|
||||
mStage.requestRebind(entry, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* How big the buffer of extra views we keep around to be ready to show when we do need to
|
||||
* dynamically inflate.
|
||||
*/
|
||||
private static final int EXTRA_VIEW_BUFFER_COUNT = 1;
|
||||
|
||||
private static final int CHILD_BIND_CUTOFF =
|
||||
NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED + EXTRA_VIEW_BUFFER_COUNT;
|
||||
}
|
||||
@@ -18,7 +18,10 @@ package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
|
||||
|
||||
@@ -467,6 +470,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
}
|
||||
};
|
||||
switch (inflationFlag) {
|
||||
case FLAG_CONTENT_VIEW_CONTRACTED:
|
||||
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
|
||||
freeViewRunnable);
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_EXPANDED:
|
||||
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_EXPANDED,
|
||||
freeViewRunnable);
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_HEADS_UP:
|
||||
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
|
||||
freeViewRunnable);
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -191,6 +192,18 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
|
||||
@InflationFlag int inflateFlag) {
|
||||
switch (inflateFlag) {
|
||||
case FLAG_CONTENT_VIEW_CONTRACTED:
|
||||
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
|
||||
row.getPrivateLayout().setContractedChild(null);
|
||||
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
}
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_EXPANDED:
|
||||
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_EXPANDED)) {
|
||||
row.getPrivateLayout().setExpandedChild(null);
|
||||
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
|
||||
}
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_HEADS_UP:
|
||||
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
|
||||
row.getPrivateLayout().setHeadsUpChild(null);
|
||||
@@ -204,8 +217,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
|
||||
}
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_CONTRACTED:
|
||||
case FLAG_CONTENT_VIEW_EXPANDED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,13 @@ public class NotificationContentView extends FrameLayout {
|
||||
public static final int VISIBLE_TYPE_EXPANDED = 1;
|
||||
public static final int VISIBLE_TYPE_HEADSUP = 2;
|
||||
private static final int VISIBLE_TYPE_SINGLELINE = 3;
|
||||
public static final int UNDEFINED = -1;
|
||||
/**
|
||||
* Used when there is no content on the view such as when we're a public layout but don't
|
||||
* need to show.
|
||||
*/
|
||||
private static final int VISIBLE_TYPE_NONE = -1;
|
||||
|
||||
private static final int UNDEFINED = -1;
|
||||
|
||||
private final Rect mClipBounds = new Rect();
|
||||
|
||||
@@ -99,7 +105,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
private HybridGroupManager mHybridGroupManager;
|
||||
private int mClipTopAmount;
|
||||
private int mContentHeight;
|
||||
private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
|
||||
private int mVisibleType = VISIBLE_TYPE_NONE;
|
||||
private boolean mAnimate;
|
||||
private boolean mIsHeadsUp;
|
||||
private boolean mLegacy;
|
||||
@@ -141,7 +147,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
/** The visible type at the start of a touch driven transformation */
|
||||
private int mTransformationStartVisibleType;
|
||||
/** The visible type at the start of an animation driven transformation */
|
||||
private int mAnimationStartVisibleType = UNDEFINED;
|
||||
private int mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
private boolean mUserExpanding;
|
||||
private int mSingleLineWidthIndention;
|
||||
private boolean mForceSelectNextLayout = true;
|
||||
@@ -386,7 +392,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
mContractedChild = null;
|
||||
mContractedWrapper = null;
|
||||
if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
|
||||
mTransformationStartVisibleType = UNDEFINED;
|
||||
mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -434,7 +440,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
mExpandedChild = null;
|
||||
mExpandedWrapper = null;
|
||||
if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
|
||||
mTransformationStartVisibleType = UNDEFINED;
|
||||
mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
|
||||
selectLayout(false /* animate */, true /* force */);
|
||||
@@ -472,7 +478,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
mHeadsUpChild = null;
|
||||
mHeadsUpWrapper = null;
|
||||
if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
|
||||
mTransformationStartVisibleType = UNDEFINED;
|
||||
mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
|
||||
selectLayout(false /* animate */, true /* force */);
|
||||
@@ -597,7 +603,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
}
|
||||
|
||||
// Size change of the expanded version
|
||||
if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
|
||||
if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart != UNDEFINED
|
||||
&& mExpandedChild != null) {
|
||||
return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
|
||||
}
|
||||
@@ -607,10 +613,12 @@ public class NotificationContentView extends FrameLayout {
|
||||
hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
|
||||
} else if (mExpandedChild != null) {
|
||||
hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
|
||||
} else {
|
||||
} else if (mContractedChild != null) {
|
||||
hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
|
||||
+ mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_action_list_height);
|
||||
} else {
|
||||
hint = getMinHeight();
|
||||
}
|
||||
|
||||
if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
|
||||
@@ -646,7 +654,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
if (mForceSelectNextLayout) {
|
||||
forceUpdateVisibilities();
|
||||
}
|
||||
if (mTransformationStartVisibleType != UNDEFINED
|
||||
if (mTransformationStartVisibleType != VISIBLE_TYPE_NONE
|
||||
&& mVisibleType != mTransformationStartVisibleType
|
||||
&& getViewForVisibleType(mTransformationStartVisibleType) != null) {
|
||||
final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
|
||||
@@ -823,7 +831,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
fireExpandedVisibleListenerIfVisible();
|
||||
// forceUpdateVisibilities cancels outstanding animations without updating the
|
||||
// mAnimationStartVisibleType. Do so here instead.
|
||||
mAnimationStartVisibleType = UNDEFINED;
|
||||
mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
|
||||
private void fireExpandedVisibleListenerIfVisible() {
|
||||
@@ -898,7 +906,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
fireExpandedVisibleListenerIfVisible();
|
||||
// updateViewVisibilities cancels outstanding animations without updating the
|
||||
// mAnimationStartVisibleType. Do so here instead.
|
||||
mAnimationStartVisibleType = UNDEFINED;
|
||||
mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
|
||||
private void updateViewVisibility(int visibleType, int type, View view,
|
||||
@@ -924,7 +932,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
|
||||
hiddenView.setVisible(false);
|
||||
}
|
||||
mAnimationStartVisibleType = UNDEFINED;
|
||||
mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
}
|
||||
});
|
||||
fireExpandedVisibleListenerIfVisible();
|
||||
@@ -1041,8 +1049,10 @@ public class NotificationContentView extends FrameLayout {
|
||||
&& (!mIsChildInGroup || isGroupExpanded()
|
||||
|| !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
|
||||
return VISIBLE_TYPE_CONTRACTED;
|
||||
} else {
|
||||
} else if (!noExpandedChild) {
|
||||
return VISIBLE_TYPE_EXPANDED;
|
||||
} else {
|
||||
return VISIBLE_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1423,7 +1433,8 @@ public class NotificationContentView extends FrameLayout {
|
||||
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
|
||||
if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
|
||||
|| mHeadsUpChild == null || !mContainingNotification.canShowHeadsUp()) {
|
||||
if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
|
||||
if (mContractedChild == null
|
||||
|| mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
|
||||
expandable = false;
|
||||
}
|
||||
} else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
|
||||
@@ -1514,7 +1525,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
if (userExpanding) {
|
||||
mTransformationStartVisibleType = mVisibleType;
|
||||
} else {
|
||||
mTransformationStartVisibleType = UNDEFINED;
|
||||
mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
|
||||
mVisibleType = calculateVisibleType();
|
||||
updateViewVisibilities(mVisibleType);
|
||||
updateBackgroundColor(false);
|
||||
@@ -1558,6 +1569,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
}
|
||||
|
||||
public void setContentHeightAnimating(boolean animating) {
|
||||
//TODO: It's odd that this does nothing when animating is true
|
||||
if (!animating) {
|
||||
mContentHeightAtAnimationStart = UNDEFINED;
|
||||
}
|
||||
@@ -1565,7 +1577,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isAnimatingVisibleType() {
|
||||
return mAnimationStartVisibleType != UNDEFINED;
|
||||
return mAnimationStartVisibleType != VISIBLE_TYPE_NONE;
|
||||
}
|
||||
|
||||
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
|
||||
@@ -1758,17 +1770,25 @@ public class NotificationContentView extends FrameLayout {
|
||||
}
|
||||
|
||||
public int getExpandHeight() {
|
||||
int viewType = VISIBLE_TYPE_EXPANDED;
|
||||
if (mExpandedChild == null) {
|
||||
int viewType;
|
||||
if (mExpandedChild != null) {
|
||||
viewType = VISIBLE_TYPE_EXPANDED;
|
||||
} else if (mContractedChild != null) {
|
||||
viewType = VISIBLE_TYPE_CONTRACTED;
|
||||
} else {
|
||||
return getMinHeight();
|
||||
}
|
||||
return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
|
||||
}
|
||||
|
||||
public int getHeadsUpHeight(boolean forceNoHeader) {
|
||||
int viewType = VISIBLE_TYPE_HEADSUP;
|
||||
if (mHeadsUpChild == null) {
|
||||
int viewType;
|
||||
if (mHeadsUpChild != null) {
|
||||
viewType = VISIBLE_TYPE_HEADSUP;
|
||||
} else if (mContractedChild != null) {
|
||||
viewType = VISIBLE_TYPE_CONTRACTED;
|
||||
} else {
|
||||
return getMinHeight();
|
||||
}
|
||||
// The headsUp remote input quickly switches to the expanded one, so lets also include that
|
||||
// one
|
||||
|
||||
@@ -53,8 +53,7 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
|
||||
@VisibleForTesting
|
||||
static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
|
||||
@VisibleForTesting
|
||||
static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
|
||||
public static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
|
||||
private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
|
||||
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.bubbles.BubbleController;
|
||||
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
|
||||
import com.android.systemui.statusbar.notification.DynamicChildBindController;
|
||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableView;
|
||||
@@ -106,17 +106,14 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
||||
mock(KeyguardBypassController.class),
|
||||
mock(BubbleController.class),
|
||||
mock(DynamicPrivacyController.class),
|
||||
mock(ForegroundServiceSectionController.class));
|
||||
mock(ForegroundServiceSectionController.class),
|
||||
mock(DynamicChildBindController.class));
|
||||
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
|
||||
}
|
||||
|
||||
private NotificationEntry createEntry() throws Exception {
|
||||
ExpandableNotificationRow row = mHelper.createRow();
|
||||
NotificationEntry entry = new NotificationEntryBuilder()
|
||||
.setSbn(row.getEntry().getSbn())
|
||||
.build();
|
||||
entry.setRow(row);
|
||||
return entry;
|
||||
return row.getEntry();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.notification;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
|
||||
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class DynamicChildBindControllerTest extends SysuiTestCase {
|
||||
|
||||
private DynamicChildBindController mDynamicChildBindController;
|
||||
private Map<NotificationEntry, List<NotificationEntry>> mGroupNotifs = new ArrayMap<>();
|
||||
private static final int TEST_CHILD_BIND_CUTOFF = 5;
|
||||
|
||||
@Mock private RowContentBindStage mBindStage;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
allowTestableLooperAsMainThread();
|
||||
when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
|
||||
mDynamicChildBindController =
|
||||
new DynamicChildBindController(mBindStage, TEST_CHILD_BIND_CUTOFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentViewsOfChildrenBeyondCutoffAreFreed() {
|
||||
// GIVEN a group notification with one view beyond the cutoff with content bound
|
||||
NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF + 1);
|
||||
NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF);
|
||||
|
||||
RowContentBindParams bindParams = mock(RowContentBindParams.class);
|
||||
when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
|
||||
|
||||
// WHEN the controller gets the list
|
||||
mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
|
||||
|
||||
// THEN we free content views
|
||||
verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
|
||||
verify(mBindStage).requestRebind(eq(lastChild), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentViewsBeforeCutoffAreBound() {
|
||||
// GIVEN a group notification with one view before the cutoff with content unbound
|
||||
NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF);
|
||||
NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF - 1);
|
||||
|
||||
lastChild.getRow().getPrivateLayout().setContractedChild(null);
|
||||
lastChild.getRow().getPrivateLayout().setExpandedChild(null);
|
||||
|
||||
RowContentBindParams bindParams = mock(RowContentBindParams.class);
|
||||
when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
|
||||
|
||||
// WHEN the controller gets the list
|
||||
mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
|
||||
|
||||
// THEN we bind content views
|
||||
verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
|
||||
verify(mBindStage).requestRebind(eq(lastChild), any());
|
||||
}
|
||||
|
||||
private NotificationEntry addGroup(int size) {
|
||||
NotificationEntry summary = new NotificationEntryBuilder().build();
|
||||
summary.setRow(createRow());
|
||||
ArrayList<NotificationEntry> children = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
NotificationEntry child = new NotificationEntryBuilder().build();
|
||||
child.setRow(createRow());
|
||||
children.add(child);
|
||||
}
|
||||
mGroupNotifs.put(summary, children);
|
||||
return summary;
|
||||
}
|
||||
|
||||
private ExpandableNotificationRow createRow() {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null);
|
||||
row.getPrivateLayout().setContractedChild(new View(mContext));
|
||||
row.getPrivateLayout().setExpandedChild(new View(mContext));
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user