diff options
15 files changed, 545 insertions, 39 deletions
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 264b53c6ee40..d8210742e331 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -174,6 +174,23 @@ public class StatusBarNotification implements Parcelable { return sbnKey; } + /** + * @return Whether the Entry is a group child by the app or system + * @hide + */ + public boolean isAppOrSystemGroupChild() { + return isGroup() && !getNotification().isGroupSummary(); + } + + + /** + * @return Whether the Entry is a group summary by the app or system + * @hide + */ + public boolean isAppOrSystemGroupSummary() { + return isGroup() && getNotification().isGroupSummary(); + } + private String groupKey() { if (overrideGroupKey != null) { return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 143fc324f8c7..3cf61e211e42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -34,6 +34,8 @@ import com.android.internal.widget.ConversationLayout; import com.android.internal.widget.ImageFloatingTextView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentView; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; +import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import java.util.ArrayList; import java.util.HashSet; @@ -253,7 +255,8 @@ public class NotificationGroupingUtil { } public void init() { - View header = mParentRow.getNotificationViewWrapper().getNotificationHeader(); + NotificationViewWrapper wrapper = mParentRow.getNotificationViewWrapper(); + View header = wrapper == null ? null : wrapper.getNotificationHeader(); mParentView = header == null ? null : header.findViewById(mId); mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow); mApply = !mComparator.isEmpty(mParentView); @@ -326,6 +329,9 @@ public class NotificationGroupingUtil { @Override public boolean isEmpty(View view) { + if (AsyncGroupHeaderViewInflation.isEnabled() && view == null) { + return true; + } if (view instanceof ImageView) { return ((ImageView) view).getDrawable() == null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 639e23ae0765..deaf1d1bc764 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -363,10 +363,11 @@ public class PreparationCoordinator implements Coordinator { NotifInflater.Params getInflaterParams(NotifUiAdjustment adjustment, String reason) { return new NotifInflater.Params( - adjustment.isMinimized(), - reason, - adjustment.isSnoozeEnabled(), - adjustment.isChildInGroup() + /* isLowPriority = */ adjustment.isMinimized(), + /* reason = */ reason, + /* showSnooze = */ adjustment.isSnoozeEnabled(), + /* isChildInGroup = */ adjustment.isChildInGroup(), + /* isGroupSummary = */ adjustment.isGroupSummary() ); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt index c0b187be42f3..18460c3e3766 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt @@ -60,5 +60,6 @@ interface NotifInflater { val reason: String, val showSnooze: Boolean, val isChildInGroup: Boolean = false, + val isGroupSummary: Boolean = false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt index e1d2cdc65d5a..bab94b50018e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt @@ -20,6 +20,7 @@ import android.app.Notification import android.app.RemoteInput import android.graphics.drawable.Icon import android.text.TextUtils +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation /** @@ -36,6 +37,7 @@ class NotifUiAdjustment internal constructor( val isMinimized: Boolean, val needsRedaction: Boolean, val isChildInGroup: Boolean, + val isGroupSummary: Boolean, ) { companion object { @JvmStatic @@ -55,6 +57,8 @@ class NotifUiAdjustment internal constructor( // !oldAdjustment.isChildInGroup && newAdjustment.isChildInGroup -> true AsyncHybridViewInflation.isEnabled && oldAdjustment.isChildInGroup != newAdjustment.isChildInGroup -> true + AsyncGroupHeaderViewInflation.isEnabled && + !oldAdjustment.isGroupSummary && newAdjustment.isGroupSummary -> true else -> false } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt index 6f44c13a3e71..0b9d19df3a75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt @@ -123,6 +123,7 @@ class NotifUiAdjustmentProvider @Inject constructor( isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled, isMinimized = isEntryMinimized(entry), needsRedaction = lockscreenUserManager.needsRedaction(entry), - isChildInGroup = groupMembershipManager.isChildInGroup(entry), + isChildInGroup = entry.sbn.isAppOrSystemGroupChild, + isGroupSummary = entry.sbn.isAppOrSystemGroupSummary, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 954e80505cbe..c5b55c7b1d9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -21,6 +21,8 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon 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_PUBLIC; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER; import static java.util.Objects.requireNonNull; @@ -50,6 +52,7 @@ import com.android.systemui.statusbar.notification.row.RowContentBindParams; import com.android.systemui.statusbar.notification.row.RowContentBindStage; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -271,6 +274,17 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } } + if (AsyncGroupHeaderViewInflation.isEnabled()) { + if (inflaterParams.isGroupSummary()) { + params.requireContentViews(FLAG_GROUP_SUMMARY_HEADER); + if (isLowPriority) { + params.requireContentViews(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER); + } + } else { + params.markContentViewsFreeable(FLAG_GROUP_SUMMARY_HEADER); + params.markContentViewsFreeable(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER); + } + } params.rebindAllContentViews(); mLogger.logRequestingRebind(entry, inflaterParams); mRowContentBindStage.requestRebind(entry, en -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index decb244947ff..d987a278fc71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -100,6 +100,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -586,7 +587,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mMenuRow.setAppName(mAppName); } if (mIsSummaryWithChildren) { - mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation()); + if (!AsyncGroupHeaderViewInflation.isEnabled()) { + // We create the header from the background thread instead + mChildrenContainer.recreateNotificationHeader(mExpandClickListener, + isConversation()); + } mChildrenContainer.onNotificationUpdated(); } if (mAnimationRunning) { @@ -668,7 +673,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public int getOriginalIconColor() { if (mIsSummaryWithChildren && !shouldShowPublic()) { - return mChildrenContainer.getVisibleWrapper().getOriginalIconColor(); + if (!AsyncGroupHeaderViewInflation.isEnabled()) { + return mChildrenContainer.getVisibleWrapper().getOriginalIconColor(); + } } int color = getShowingLayout().getOriginalIconColor(); if (color != Notification.COLOR_INVALID) { @@ -1513,6 +1520,40 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mChildrenContainer; } + /** + * @return An non-null instance of mChildrenContainer, inflate it if not yet. + */ + public @NonNull NotificationChildrenContainer getChildrenContainerNonNull() { + if (mChildrenContainer == null) { + mChildrenContainerStub.inflate(); + } + return mChildrenContainer; + } + + /** + * Set the group notification header view + * @param headerView header view to set + */ + public void setGroupHeader(NotificationHeaderView headerView) { + NotificationChildrenContainer childrenContainer = getChildrenContainerNonNull(); + childrenContainer.setGroupHeader( + /* headerView= */ headerView, + /* onClickListener= */ mExpandClickListener + ); + } + + /** + * Set the low-priority group notification header view + * @param headerView header view to set + */ + public void setLowPriorityGroupHeader(NotificationHeaderView headerView) { + NotificationChildrenContainer childrenContainer = getChildrenContainerNonNull(); + childrenContainer.setLowPriorityGroupHeader( + /* headerViewLowPriority= */ headerView, + /* onClickListener= */ mExpandClickListener + ); + } + public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { boolean wasAboveShelf = isAboveShelf(); boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning; @@ -1565,7 +1606,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public View getShelfTransformationTarget() { if (mIsSummaryWithChildren && !shouldShowPublic()) { - return mChildrenContainer.getVisibleWrapper().getShelfTransformationTarget(); + NotificationViewWrapper viewWrapper = mChildrenContainer.getVisibleWrapper(); + if (AsyncGroupHeaderViewInflation.isEnabled() && viewWrapper == null) { + return null; + } + return viewWrapper.getShelfTransformationTarget(); } return getShowingLayout().getShelfTransformationTarget(); } @@ -2710,10 +2755,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView && mChildrenContainer.getNotificationChildCount() > 0; if (mIsSummaryWithChildren) { Trace.beginSection("ExpNotRow#onChildCountChanged (summary)"); - NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper(); - if (wrapper == null || wrapper.getNotificationHeader() == null) { - mChildrenContainer.recreateNotificationHeader(mExpandClickListener, - isConversation()); + if (!AsyncGroupHeaderViewInflation.isEnabled()) { + NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper(); + if (wrapper == null || wrapper.getNotificationHeader() == null) { + mChildrenContainer.recreateNotificationHeader(mExpandClickListener, + isConversation()); + } } } if (!mIsSummaryWithChildren && wasSummary) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index e288e857bf4a..d308fa583f71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -37,7 +37,9 @@ import android.os.Trace; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.view.NotificationHeaderView; import android.view.View; +import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; @@ -51,11 +53,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; @@ -387,6 +391,21 @@ public class NotificationContentInflater implements NotificationRowContentBinder logger.logAsyncTaskProgress(entryForLogging, "creating public remote view"); result.newPublicView = builder.makePublicContentView(isLowPriority); } + + if (AsyncGroupHeaderViewInflation.isEnabled()) { + if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) { + logger.logAsyncTaskProgress(entryForLogging, + "creating group summary remote view"); + result.mNewGroupHeaderView = builder.makeNotificationGroupHeader(); + } + + if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) { + logger.logAsyncTaskProgress(entryForLogging, + "creating low-priority group summary remote view"); + result.mNewLowPriorityGroupHeaderView = + builder.makeLowPriorityContentView(true /* useRegularSubtext */); + } + } setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider); result.packageContext = packageContext; result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */); @@ -534,6 +553,67 @@ public class NotificationContentInflater implements NotificationRowContentBinder runningInflations, applyCallback, logger); } + if (AsyncGroupHeaderViewInflation.isEnabled()) { + NotificationChildrenContainer childrenContainer = row.getChildrenContainerNonNull(); + if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) { + boolean isNewView = + !canReapplyRemoteView( + /* newView = */ result.mNewGroupHeaderView, + /* oldView = */ remoteViewCache + .getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)); + ApplyCallback applyCallback = new ApplyCallback() { + @Override + public void setResultView(View v) { + logger.logAsyncTaskProgress(entry, "group header view applied"); + result.mInflatedGroupHeaderView = (NotificationHeaderView) v; + } + + @Override + public RemoteViews getRemoteView() { + return result.mNewGroupHeaderView; + } + }; + logger.logAsyncTaskProgress(entry, "applying group header view"); + applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, + /* inflationId = */ FLAG_GROUP_SUMMARY_HEADER, + remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback, + /* parentLayout = */ childrenContainer, + /* existingView = */ childrenContainer.getNotificationHeader(), + /* existingWrapper = */ childrenContainer.getNotificationHeaderWrapper(), + runningInflations, applyCallback, logger); + } + + if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) { + boolean isNewView = + !canReapplyRemoteView( + /* newView = */ result.mNewLowPriorityGroupHeaderView, + /* oldView = */ remoteViewCache.getCachedView( + entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)); + ApplyCallback applyCallback = new ApplyCallback() { + @Override + public void setResultView(View v) { + logger.logAsyncTaskProgress(entry, + "low-priority group header view applied"); + result.mInflatedLowPriorityGroupHeaderView = (NotificationHeaderView) v; + } + + @Override + public RemoteViews getRemoteView() { + return result.mNewLowPriorityGroupHeaderView; + } + }; + logger.logAsyncTaskProgress(entry, "applying low priority group header view"); + applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, + /* inflationId = */ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback, + /* parentLayout = */ childrenContainer, + /* existingView = */ childrenContainer.getNotificationHeaderLowPriority(), + /* existingWrapper = */ childrenContainer + .getLowPriorityViewWrapper(), + runningInflations, applyCallback, logger); + } + } + // Let's try to finish, maybe nobody is even inflating anything finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry, row, logger); @@ -560,7 +640,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder boolean isNewView, RemoteViews.InteractionHandler remoteViewClickHandler, @Nullable final InflationCallback callback, - NotificationContentView parentLayout, + ViewGroup parentLayout, View existingView, NotificationViewWrapper existingWrapper, final HashMap<Integer, CancellationSignal> runningInflations, @@ -702,6 +782,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result; } + /** + * Notifications with undecorated custom views need to satisfy a minimum height to avoid visual + * issues. + */ private static boolean requiresHeightCheck(NotificationEntry entry) { // Undecorated custom views are disallowed from S onwards if (entry.targetSdk >= Build.VERSION_CODES.S) { @@ -845,6 +929,39 @@ public class NotificationContentInflater implements NotificationRowContentBinder } } + if (AsyncGroupHeaderViewInflation.isEnabled()) { + if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) { + if (result.mInflatedGroupHeaderView != null) { + row.setIsLowPriority(false); + row.setGroupHeader(/* headerView= */ result.mInflatedGroupHeaderView); + remoteViewCache.putCachedView(entry, FLAG_GROUP_SUMMARY_HEADER, + result.mNewGroupHeaderView); + } else if (remoteViewCache.hasCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)) { + // Re-inflation case. Only update if it's still cached (i.e. view has not + // been freed while inflating). + remoteViewCache.putCachedView(entry, FLAG_GROUP_SUMMARY_HEADER, + result.mNewGroupHeaderView); + } + } + + if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) { + if (result.mInflatedLowPriorityGroupHeaderView != null) { + // New view case, set row to low priority + row.setIsLowPriority(true); + row.setLowPriorityGroupHeader( + /* headerView= */ result.mInflatedLowPriorityGroupHeaderView); + remoteViewCache.putCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + result.mNewLowPriorityGroupHeaderView); + } else if (remoteViewCache.hasCachedView(entry, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)) { + // Re-inflation case. Only update if it's still cached (i.e. view has not + // been freed while inflating). + remoteViewCache.putCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + result.mNewGroupHeaderView); + } + } + } + entry.headsUpStatusBarText = result.headsUpStatusBarText; entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic; if (endListener != null) { @@ -1147,6 +1264,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder private RemoteViews newHeadsUpView; private RemoteViews newExpandedView; private RemoteViews newPublicView; + private RemoteViews mNewGroupHeaderView; + private RemoteViews mNewLowPriorityGroupHeaderView; @VisibleForTesting Context packageContext; @@ -1155,6 +1274,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder private View inflatedHeadsUpView; private View inflatedExpandedView; private View inflatedPublicView; + private NotificationHeaderView mInflatedGroupHeaderView; + private NotificationHeaderView mInflatedLowPriorityGroupHeaderView; private CharSequence headsUpStatusBarText; private CharSequence headsUpStatusBarTextPublic; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt index ee9462c60674..15c705579bf7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt @@ -27,6 +27,8 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import javax.inject.Inject @@ -145,6 +147,12 @@ constructor(@NotifInflationLog private val buffer: LogBuffer) { if (flag and FLAG_CONTENT_VIEW_SINGLE_LINE != 0) { l.add("SINGLE_LINE") } + if (flag and FLAG_GROUP_SUMMARY_HEADER != 0) { + l.add("GROUP_SUMMARY_HEADER") + } + if (flag and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) { + l.add("LOW_PRIORITY_GROUP_SUMMARY_HEADER") + } return l.joinToString("|") } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 374248252d1f..50bc3d31aa42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -238,7 +238,10 @@ public class NotificationContentView extends FrameLayout implements Notification mMinContractedHeight = getResources().getDimensionPixelSize( R.dimen.min_notification_layout_height); if (AsyncHybridViewInflation.isEnabled()) { - //TODO: set the height with a more reasonable min single-line height + //TODO (b/217799515): single-line view height is the greater of two heights: text view + // height and icon height (when there's an icon). icon height is fixed to be + // conversation_single_line_face_pile_size (24dp), the text view's height is 16sp, + // its pixel height changes with the system's font scaling factor. mMinSingleLineHeight = getResources().getDimensionPixelSize( R.dimen.conversation_single_line_face_pile_size); } @@ -843,7 +846,7 @@ public class NotificationContentView extends FrameLayout implements Notification if (mSingleLineView != null) { return getViewHeight(VISIBLE_TYPE_SINGLELINE); } else { - Log.wtf(TAG, "getMinHeight: mSingleLineView == null"); + //TODO(b/217799515): investigate the impact of min-height value return mMinSingleLineHeight; } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java index 736140c44dfd..b0fd47587782 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java @@ -81,6 +81,8 @@ public interface NotificationRowContentBinder { FLAG_CONTENT_VIEW_HEADS_UP, FLAG_CONTENT_VIEW_PUBLIC, FLAG_CONTENT_VIEW_SINGLE_LINE, + FLAG_GROUP_SUMMARY_HEADER, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, FLAG_CONTENT_VIEW_ALL}) @interface InflationFlag {} /** @@ -108,7 +110,17 @@ public interface NotificationRowContentBinder { */ int FLAG_CONTENT_VIEW_SINGLE_LINE = 1 << 4; - int FLAG_CONTENT_VIEW_ALL = (1 << 5) - 1; + /** + * The notification group summary header view + */ + int FLAG_GROUP_SUMMARY_HEADER = 1 << 5; + + /** + * The notification low-priority group summary header view + */ + int FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER = 1 << 6; + + int FLAG_CONTENT_VIEW_ALL = (1 << 7) - 1; /** * Parameters for content view binding @@ -129,6 +141,11 @@ public interface NotificationRowContentBinder { * Use increased height when binding heads up views. */ public boolean usesIncreasedHeadsUpHeight; + + /** + * Is group summary notification + */ + public boolean mIsGroupSummary; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index abf6c27c68ac..fa973001cec7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -55,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.HybridGroupManager; import com.android.systemui.statusbar.notification.row.HybridNotificationView; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; @@ -131,6 +132,7 @@ public class NotificationChildrenContainer extends ViewGroup private int mUntruncatedChildCount; private boolean mContainingNotificationIsFaded = false; private RoundableState mRoundableState; + private int mMinSingleLineHeight; private NotificationChildrenContainerLogger mLogger; @@ -183,6 +185,8 @@ public class NotificationChildrenContainer extends ViewGroup com.android.internal.R.dimen.notification_content_margin) - mNotificationHeaderMargin; mHybridGroupManager.initDimens(); + mMinSingleLineHeight = getResources().getDimensionPixelSize( + R.dimen.conversation_single_line_face_pile_size); } @NonNull @@ -385,16 +389,31 @@ public class NotificationChildrenContainer extends ViewGroup return mAttachedChildren.size(); } - public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) { + /** + * Re-create the Notification header view + * @param listener OnClickListener of the header view + * @param isConversation if the notification group is a conversation group + */ + public void recreateNotificationHeader( + OnClickListener listener, + boolean isConversation + ) { + // We don't want to inflate headers from the main thread when async inflation enabled + AsyncGroupHeaderViewInflation.assertInLegacyMode(); + // TODO(b/217799515): remove traces from this function in a follow-up change Trace.beginSection("NotifChildCont#recreateHeader"); mHeaderClickListener = listener; mIsConversation = isConversation; StatusBarNotification notification = mContainingNotification.getEntry().getSbn(); final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), notification.getNotification()); + Trace.beginSection("recreateHeader#makeNotificationGroupHeader"); RemoteViews header = builder.makeNotificationGroupHeader(); + Trace.endSection(); if (mNotificationHeader == null) { + Trace.beginSection("recreateHeader#apply"); mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this); + Trace.endSection(); mNotificationHeader.findViewById(com.android.internal.R.id.expand_button) .setVisibility(VISIBLE); mNotificationHeader.setOnClickListener(mHeaderClickListener); @@ -407,7 +426,9 @@ public class NotificationChildrenContainer extends ViewGroup addView(mNotificationHeader, 0); invalidate(); } else { + Trace.beginSection("recreateHeader#reapply"); header.reapply(getContext(), mNotificationHeader); + Trace.endSection(); } mNotificationHeaderWrapper.setExpanded(mChildrenExpanded); mNotificationHeaderWrapper.onContentUpdated(mContainingNotification); @@ -417,12 +438,105 @@ public class NotificationChildrenContainer extends ViewGroup Trace.endSection(); } + private void removeGroupHeader() { + if (mNotificationHeader == null) { + return; + } + removeView(mNotificationHeader); + mNotificationHeader = null; + mNotificationHeaderWrapper = null; + } + + private void removeLowPriorityGroupHeader() { + if (mNotificationHeaderLowPriority == null) { + return; + } + removeView(mNotificationHeaderLowPriority); + mNotificationHeaderLowPriority = null; + mNotificationHeaderWrapperLowPriority = null; + } + + /** + * Set the group header view + * @param headerView view to set + * @param onClickListener OnClickListener of the header view + */ + public void setGroupHeader( + NotificationHeaderView headerView, + OnClickListener onClickListener + ) { + if (AsyncGroupHeaderViewInflation.isUnexpectedlyInLegacyMode()) return; + mHeaderClickListener = onClickListener; + + removeGroupHeader(); + + if (headerView == null) { + return; + } + + mNotificationHeader = headerView; + mNotificationHeader.findViewById(com.android.internal.R.id.expand_button) + .setVisibility(VISIBLE); + mNotificationHeader.setOnClickListener(mHeaderClickListener); + mNotificationHeaderWrapper = + (NotificationHeaderViewWrapper) NotificationViewWrapper.wrap( + getContext(), + mNotificationHeader, + mContainingNotification); + mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate); + addView(mNotificationHeader, 0); + invalidate(); + + mNotificationHeaderWrapper.setExpanded(mChildrenExpanded); + mNotificationHeaderWrapper.onContentUpdated(mContainingNotification); + + updateHeaderVisibility(false /* animate */); + updateChildrenAppearance(); + + Trace.endSection(); + } + + /** + * Set the low-priority group header view + * @param headerViewLowPriority header view to set + * @param onClickListener OnClickListener of the header view + */ + public void setLowPriorityGroupHeader( + NotificationHeaderView headerViewLowPriority, + OnClickListener onClickListener + ) { + if (AsyncGroupHeaderViewInflation.isUnexpectedlyInLegacyMode()) return; + removeLowPriorityGroupHeader(); + if (headerViewLowPriority == null) { + return; + } + + mNotificationHeaderLowPriority = headerViewLowPriority; + mNotificationHeaderLowPriority.findViewById(com.android.internal.R.id.expand_button) + .setVisibility(VISIBLE); + mNotificationHeaderLowPriority.setOnClickListener(onClickListener); + mNotificationHeaderWrapperLowPriority = + (NotificationHeaderViewWrapper) NotificationViewWrapper.wrap( + getContext(), + mNotificationHeaderLowPriority, + mContainingNotification); + mNotificationHeaderWrapperLowPriority.setOnRoundnessChangedListener(this::invalidate); + addView(mNotificationHeaderLowPriority, 0); + invalidate(); + + mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification); + updateHeaderVisibility(false /* animate */); + updateChildrenAppearance(); + } + /** * Recreate the low-priority header. * * @param builder a builder to reuse. Otherwise the builder will be recovered. */ - private void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) { + @VisibleForTesting + void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) { + AsyncGroupHeaderViewInflation.assertInLegacyMode(); RemoteViews header; StatusBarNotification notification = mContainingNotification.getEntry().getSbn(); if (mIsLowPriority) { @@ -527,8 +641,10 @@ public class NotificationChildrenContainer extends ViewGroup * @param alpha alpha value to apply to the content */ public void setContentAlpha(float alpha) { - for (int i = 0; i < mNotificationHeader.getChildCount(); i++) { - mNotificationHeader.getChildAt(i).setAlpha(alpha); + if (mNotificationHeader != null) { + for (int i = 0; i < mNotificationHeader.getChildCount(); i++) { + mNotificationHeader.getChildAt(i).setAlpha(alpha); + } } for (ExpandableNotificationRow child : getAttachedChildren()) { child.setContentAlpha(alpha); @@ -564,7 +680,11 @@ public class NotificationChildrenContainer extends ViewGroup */ private int getIntrinsicHeight(float maxAllowedVisibleChildren) { if (showingAsLowPriority()) { - return mNotificationHeaderLowPriority.getHeight(); + if (AsyncGroupHeaderViewInflation.isEnabled()) { + return mHeaderHeight; + } else { + return mNotificationHeaderLowPriority.getHeight(); + } } int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation; int visibleChildren = 0; @@ -1023,6 +1143,14 @@ public class NotificationChildrenContainer extends ViewGroup return mCurrentHeader; } + public NotificationHeaderView getNotificationHeader() { + return mNotificationHeader; + } + + public NotificationHeaderView getNotificationHeaderLowPriority() { + return mNotificationHeaderLowPriority; + } + private void updateHeaderVisibility(boolean animate) { ViewGroup desiredHeader; ViewGroup currentHeader = mCurrentHeader; @@ -1032,6 +1160,10 @@ public class NotificationChildrenContainer extends ViewGroup return; } + if (AsyncGroupHeaderViewInflation.isEnabled() && desiredHeader == null) { + return; + } + if (animate) { if (desiredHeader != null && currentHeader != null) { currentHeader.setVisibility(VISIBLE); @@ -1271,6 +1403,9 @@ public class NotificationChildrenContainer extends ViewGroup boolean likeHighPriority, int headerTranslation) { if (!likeHighPriority && showingAsLowPriority()) { + if (AsyncGroupHeaderViewInflation.isEnabled()) { + return mHeaderHeight; + } if (mNotificationHeaderLowPriority == null) { Log.e(TAG, "getMinHeight: low priority header is null", new Exception()); return 0; @@ -1295,8 +1430,12 @@ public class NotificationChildrenContainer extends ViewGroup if (singleLineView != null) { minExpandHeight += singleLineView.getHeight(); } else { - Log.e(TAG, "getMinHeight: child " + child.getEntry().getKey() - + " single line view is null", new Exception()); + if (AsyncGroupHeaderViewInflation.isEnabled()) { + minExpandHeight += mMinSingleLineHeight; + } else { + Log.e(TAG, "getMinHeight: child " + child.getEntry().getKey() + + " single line view is null", new Exception()); + } } visibleChildren++; } @@ -1309,15 +1448,19 @@ public class NotificationChildrenContainer extends ViewGroup } public void reInflateViews(OnClickListener listener, StatusBarNotification notification) { - if (mNotificationHeader != null) { - removeView(mNotificationHeader); - mNotificationHeader = null; - } - if (mNotificationHeaderLowPriority != null) { - removeView(mNotificationHeaderLowPriority); - mNotificationHeaderLowPriority = null; + if (!AsyncGroupHeaderViewInflation.isEnabled()) { + // When Async header inflation is enabled, we do not reinflate headers because they are + // inflated from the background thread + if (mNotificationHeader != null) { + removeView(mNotificationHeader); + mNotificationHeader = null; + } + if (mNotificationHeaderLowPriority != null) { + removeView(mNotificationHeaderLowPriority); + mNotificationHeaderLowPriority = null; + } + recreateNotificationHeader(listener, mIsConversation); } - recreateNotificationHeader(listener, mIsConversation); initDimens(); for (int i = 0; i < mDividers.size(); i++) { View prevDivider = mDividers.get(i); @@ -1395,7 +1538,9 @@ public class NotificationChildrenContainer extends ViewGroup public void setIsLowPriority(boolean isLowPriority) { mIsLowPriority = isLowPriority; if (mContainingNotification != null) { /* we're not yet set up yet otherwise */ - recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation); + if (!AsyncGroupHeaderViewInflation.isEnabled()) { + recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation); + } updateHeaderVisibility(false /* animate */); } if (mUserLocked) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt index 73c49c023dd5..115a0d367e87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -46,6 +47,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.inOrder +import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @@ -141,12 +143,14 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() { fun changeIsChildInGroup_asyncHybirdFlagEnabled_needReInflation() { // Given: an Entry that is not child in group // AsyncHybridViewInflation flag is enabled - whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(false) + val spySbn = spy(entry.sbn) + entry.sbn = spySbn + whenever(spySbn.isAppOrSystemGroupChild).thenReturn(false) val oldAdjustment = adjustmentProvider.calculateAdjustment(entry) assertThat(oldAdjustment.isChildInGroup).isFalse() // When: the Entry becomes a group child - whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(true) + whenever(spySbn.isAppOrSystemGroupChild).thenReturn(true) val newAdjustment = adjustmentProvider.calculateAdjustment(entry) assertThat(newAdjustment.isChildInGroup).isTrue() assertThat(newAdjustment).isNotEqualTo(oldAdjustment) @@ -160,12 +164,14 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() { fun changeIsChildInGroup_asyncHybirdFlagDisabled_noNeedForReInflation() { // Given: an Entry that is not child in group // AsyncHybridViewInflation flag is disabled - whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(false) + val spySbn = spy(entry.sbn) + entry.sbn = spySbn + whenever(spySbn.isAppOrSystemGroupChild).thenReturn(false) val oldAdjustment = adjustmentProvider.calculateAdjustment(entry) assertThat(oldAdjustment.isChildInGroup).isFalse() // When: the Entry becomes a group child - whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(true) + whenever(spySbn.isAppOrSystemGroupChild).thenReturn(true) val newAdjustment = adjustmentProvider.calculateAdjustment(entry) assertThat(newAdjustment.isChildInGroup).isTrue() assertThat(newAdjustment).isNotEqualTo(oldAdjustment) @@ -173,4 +179,24 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() { // Then: need no re-inflation assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) } + + @Test + @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) + fun changeIsGroupSummary_needReInflation() { + // Given: an Entry that is not a group summary + val spySbn = spy(entry.sbn) + entry.sbn = spySbn + whenever(spySbn.isAppOrSystemGroupSummary).thenReturn(false) + val oldAdjustment = adjustmentProvider.calculateAdjustment(entry) + assertThat(oldAdjustment.isGroupSummary).isFalse() + + // When: the Entry becomes a group summary + whenever(spySbn.isAppOrSystemGroupSummary).thenReturn(true) + val newAdjustment = adjustmentProvider.calculateAdjustment(entry) + assertThat(newAdjustment.isGroupSummary).isTrue() + assertThat(newAdjustment).isNotEqualTo(oldAdjustment) + + // Then: Need re-inflation + assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index be976a1c9eaf..1f38a73020b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -16,11 +16,17 @@ package com.android.systemui.statusbar.notification.stack; +import static org.junit.Assert.assertNull; + +import android.app.Notification; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.NotificationHeaderView; import android.view.View; +import android.widget.RemoteViews; import androidx.test.filters.SmallTest; @@ -28,6 +34,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper; import org.junit.Assert; @@ -40,6 +47,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper +//@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public class NotificationChildrenContainerTest extends SysuiTestCase { private ExpandableNotificationRow mGroup; @@ -138,6 +146,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { } @Test + @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void testLowPriorityHeaderCleared() { mGroup.setIsLowPriority(true); NotificationHeaderView lowPriorityHeaderView = @@ -145,11 +154,12 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility()); Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent()); mGroup.setIsLowPriority(false); - Assert.assertNull(lowPriorityHeaderView.getParent()); - Assert.assertNull(mChildrenContainer.getLowPriorityViewWrapper()); + assertNull(lowPriorityHeaderView.getParent()); + assertNull(mChildrenContainer.getLowPriorityViewWrapper()); } @Test + @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void testRecreateNotificationHeader_hasHeader() { mChildrenContainer.recreateNotificationHeader(null, false); Assert.assertNotNull("Children container must have a header after recreation", @@ -157,6 +167,76 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { } @Test + @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) + public void testSetLowPriorityWithAsyncInflation_noHeaderReInflation() { + mChildrenContainer.setIsLowPriority(true); + assertNull("We don't inflate header from the main thread with Async " + + "Inflation enabled", mChildrenContainer.getCurrentHeaderView()); + } + + @Test + @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) + public void setLowPriorityBeforeLowPriorityHeaderSet() { + + //Given: the children container does not have a low-priority header, and is not low-priority + assertNull(mChildrenContainer.getLowPriorityViewWrapper()); + mGroup.setIsLowPriority(false); + + //When: set the children container to be low-priority and set the low-priority header + mGroup.setIsLowPriority(true); + mGroup.setLowPriorityGroupHeader(createHeaderView(/* lowPriorityHeader= */ true)); + + //Then: the low-priority group header should be visible + NotificationHeaderView lowPriorityHeaderView = + mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader(); + Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility()); + Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent()); + + //When: set the children container to be not low-priority and set the normal header + mGroup.setIsLowPriority(false); + mGroup.setGroupHeader(createHeaderView(/* lowPriorityHeader= */ false)); + + //Then: the low-priority group header should not be visible , normal header should be + // visible + Assert.assertEquals(View.INVISIBLE, lowPriorityHeaderView.getVisibility()); + Assert.assertEquals( + View.VISIBLE, + mChildrenContainer.getNotificationHeaderWrapper().getNotificationHeader() + .getVisibility() + ); + } + + @Test + @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) + public void changeLowPriorityAfterHeaderSet() { + + //Given: the children container does not have headers, and is not low-priority + assertNull(mChildrenContainer.getLowPriorityViewWrapper()); + assertNull(mChildrenContainer.getNotificationHeaderWrapper()); + mGroup.setIsLowPriority(false); + + //When: set the set the normal header + mGroup.setGroupHeader(createHeaderView(/* lowPriorityHeader= */ false)); + + //Then: the group header should be visible + NotificationHeaderView headerView = + mChildrenContainer.getNotificationHeaderWrapper().getNotificationHeader(); + Assert.assertEquals(View.VISIBLE, headerView.getVisibility()); + Assert.assertSame(mChildrenContainer, headerView.getParent()); + + //When: set the set the row to be low priority, and set the low-priority header + mGroup.setIsLowPriority(true); + mGroup.setLowPriorityGroupHeader(createHeaderView(/* lowPriorityHeader= */ true)); + + //Then: the header view should not be visible, the low-priority group header should be + // visible + Assert.assertEquals(View.INVISIBLE, headerView.getVisibility()); + NotificationHeaderView lowPriorityHeaderView = + mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader(); + Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility()); + } + + @Test public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child() { List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren(); ExpandableNotificationRow notificationRow = children.get(children.size() - 1); @@ -170,6 +250,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { } @Test + @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_header() { NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper(); Assert.assertEquals(0f, header.getTopRoundness(), 0.001f); @@ -180,6 +261,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { } @Test + @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_headerLowPriority() { mChildrenContainer.setIsLowPriority(true); @@ -190,4 +272,17 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { Assert.assertEquals(1f, header.getTopRoundness(), 0.001f); } + + private NotificationHeaderView createHeaderView(boolean lowPriority) { + Notification notification = mNotificationTestHelper.createNotification(); + final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), + notification); + RemoteViews headerRemoteViews; + if (lowPriority) { + headerRemoteViews = builder.makeLowPriorityContentView(true); + } else { + headerRemoteViews = builder.makeNotificationGroupHeader(); + } + return (NotificationHeaderView) headerRemoteViews.apply(getContext(), mChildrenContainer); + } } |