From d8b5154b25eabb9cdddbe91c8fcec0d5526a7747 Mon Sep 17 00:00:00 2001 From: Ned Burns Date: Fri, 13 Mar 2020 20:52:43 -0400 Subject: [PATCH] Move icon logic out of NotificationEntry It's been on the list of things to remove for a while and now the conversations feature has some extra needs that can't be addressed by the existing system. Moves all logic into IconManager. Icons are still stored on the NotificationEntry (boo) but they're all encapsulated in a getIcons() object. All of this logic should eventually move into the content inflation pipeline (probably?) but for now at least it's self-contained. Bug: 112656837 Test: atest SystemUITests Change-Id: Iecbd8999c2c866c7217ad9d0e090fb1db7ddd690 --- .../statusbar/HeadsUpStatusBarView.java | 23 +- .../statusbar/NotificationMediaManager.java | 4 +- .../systemui/statusbar/NotificationShelf.java | 6 +- .../NotificationEntryManager.java | 2 +- .../collection/NotificationEntry.java | 289 ++------------- .../inflation/NotificationRowBinderImpl.java | 41 +-- .../notification/icon/IconBuilder.kt | 41 +++ .../notification/icon/IconManager.kt | 338 ++++++++++++++++++ .../statusbar/notification/icon/IconPack.java | 134 +++++++ .../statusbar/notification/row/DungeonRow.kt | 2 +- .../row/ExpandableNotificationRow.java | 10 +- .../stack/StackStateAnimator.java | 7 +- .../phone/HeadsUpAppearanceController.java | 2 +- .../phone/NotificationIconAreaController.java | 14 +- .../row/ExpandableNotificationRowTest.java | 4 +- ...NotificationEntryManagerInflationTest.java | 14 +- .../row/NotificationTestHelper.java | 10 +- 17 files changed, 619 insertions(+), 322 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index 4597b16568849..b33424c563a38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -33,6 +33,7 @@ import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener; import java.util.List; @@ -159,20 +160,30 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { } public void setEntry(NotificationEntry entry) { - if (entry != null) { - mShowingEntry = entry; + if (mShowingEntry != null) { + mShowingEntry.removeOnSensitivityChangedListener(mOnSensitivityChangedListener); + } + mShowingEntry = entry; + + if (mShowingEntry != null) { CharSequence text = entry.headsUpStatusBarText; if (entry.isSensitive()) { text = entry.headsUpStatusBarTextPublic; } mTextView.setText(text); - mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry)); - } else if (mShowingEntry != null){ - mShowingEntry.setOnSensitiveChangedListener(null); - mShowingEntry = null; + mShowingEntry.addOnSensitivityChangedListener(mOnSensitivityChangedListener); } } + private final OnSensitivityChangedListener mOnSensitivityChangedListener = entry -> { + if (entry != mShowingEntry) { + throw new IllegalStateException("Got a sensitivity change for " + entry + + " but mShowingEntry is " + mShowingEntry); + } + // Update the text + setEntry(entry); + }; + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 8632969264912..d8fdf928f6add 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -263,11 +263,11 @@ public class NotificationMediaManager implements Dumpable { synchronized (mEntryManager) { NotificationEntry entry = mEntryManager .getActiveNotificationUnfiltered(mMediaNotificationKey); - if (entry == null || entry.expandedIcon == null) { + if (entry == null || entry.getIcons().getShelfIcon() == null) { return null; } - return entry.expandedIcon.getSourceIcon(); + return entry.getIcons().getShelfIcon().getSourceIcon(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 1a8454cfa1138..d7f2ae43cf9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -329,7 +329,7 @@ public class NotificationShelf extends ActivatableNotificationView implements expandableRow.setAboveShelf(false); } if (notGoneIndex == 0) { - StatusBarIconView icon = expandableRow.getEntry().expandedIcon; + StatusBarIconView icon = expandableRow.getEntry().getIcons().getShelfIcon(); NotificationIconContainer.IconState iconState = getIconState(icon); // The icon state might be null in rare cases where the notification is actually // added to the layout, but not to the shelf. An example are replied messages, @@ -432,7 +432,7 @@ public class NotificationShelf extends ActivatableNotificationView implements // if the shelf is clipped, lets make sure we also clip the icon maxTop = Math.max(maxTop, getTranslationY() + getClipTopAmount()); } - StatusBarIconView icon = row.getEntry().expandedIcon; + StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon(); float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY(); if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) { int top = (int) (maxTop - shelfIconPosition); @@ -444,7 +444,7 @@ public class NotificationShelf extends ActivatableNotificationView implements } private void updateContinuousClipping(final ExpandableNotificationRow row) { - StatusBarIconView icon = row.getEntry().expandedIcon; + StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon(); boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing(); boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null; if (needsContinuousClipping && !isContinuousClipping) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 0450e02818756..c6d84ff79bde7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -841,7 +841,7 @@ public class NotificationEntryManager implements private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) { pw.print(indent); - pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon); + pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon()); StatusBarNotification n = e.getSbn(); pw.print(indent); pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 3e9d8a436f38b..7019b5b42cf45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -29,8 +29,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; @@ -44,10 +42,7 @@ import android.app.NotificationManager.Policy; import android.app.Person; import android.app.RemoteInputHistoryItem; import android.content.Context; -import android.content.pm.LauncherApps; -import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.ShortcutInfo; -import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -55,25 +50,20 @@ import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.util.ArraySet; -import android.util.Log; -import android.view.View; -import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.statusbar.InflationTask; -import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; +import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotificationGuts; @@ -81,8 +71,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -102,15 +90,11 @@ import java.util.Objects; * clean this up in the future. */ public final class NotificationEntry extends ListEntry { - private static final String TAG = "NotificationEntry"; private final String mKey; private StatusBarNotification mSbn; private Ranking mRanking; - private StatusBarIcon mSmallIcon; - private StatusBarIcon mPeopleAvatar; - /* * Bookkeeping members */ @@ -142,10 +126,7 @@ public final class NotificationEntry extends ListEntry { * TODO: Remove every member beneath this line if possible */ - public StatusBarIconView icon; - public StatusBarIconView expandedIcon; - public StatusBarIconView centeredIcon; - public StatusBarIconView aodIcon; + private IconPack mIcons = IconPack.buildEmptyPack(null); private boolean interruption; public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; @@ -191,7 +172,8 @@ public final class NotificationEntry extends ListEntry { private boolean hasSentReply; private boolean mSensitive = true; - private Runnable mOnSensitiveChangedListener; + private List mOnSensitivityChangedListeners = new ArrayList<>(); + private boolean mAutoHeadsUp; private boolean mPulseSupressed; private boolean mAllowFgsDismissal; @@ -347,6 +329,15 @@ public final class NotificationEntry extends ListEntry { * TODO: Remove as many of these as possible */ + @NonNull + public IconPack getIcons() { + return mIcons; + } + + public void setIcons(@NonNull IconPack icons) { + mIcons = icons; + } + public void setInterruption() { interruption = true; } @@ -464,239 +455,6 @@ public final class NotificationEntry extends ListEntry { || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY; } - /** - * Create the icons for a notification - * @param context the context to create the icons with - * @param sbn the notification - * @throws InflationException Exception if required icons are not valid or specified - */ - public void createIcons(Context context, StatusBarNotification sbn) - throws InflationException { - StatusBarIcon ic = getIcon(context, sbn, false /* redact */); - - // Construct the icon. - icon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - // Construct the expanded icon. - expandedIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - // Construct the expanded icon. - aodIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - aodIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - aodIcon.setIncreasedSize(true); - - try { - setIcons(ic, Collections.singletonList(icon)); - if (isSensitive()) { - ic = getIcon(context, sbn, true /* redact */); - } - setIcons(ic, Arrays.asList(expandedIcon, aodIcon)); - } catch (InflationException e) { - icon = null; - expandedIcon = null; - centeredIcon = null; - aodIcon = null; - throw e; - } - - expandedIcon.setVisibility(View.INVISIBLE); - expandedIcon.setOnVisibilityChangedListener( - newVisibility -> { - if (row != null) { - row.setIconsVisible(newVisibility != View.VISIBLE); - } - }); - - // Construct the centered icon - if (mSbn.getNotification().isMediaNotification()) { - centeredIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - try { - setIcons(ic, Collections.singletonList(centeredIcon)); - } catch (InflationException e) { - centeredIcon = null; - throw e; - } - } - } - - /** - * Determines if this icon should be tinted based on the sensitivity of the icon, its context - * and the user's indicated sensitivity preference. - * - * @param ic The icon that should/should not be tinted. - * @return - */ - private boolean shouldTintIcon(StatusBarIconView ic) { - boolean usedInSensitiveContext = (ic == expandedIcon || ic == aodIcon); - return !isImportantConversation() || (usedInSensitiveContext && isSensitive()); - } - - - private void setIcons(StatusBarIcon ic, List icons) - throws InflationException { - for (StatusBarIconView icon: icons) { - if (icon == null) { - continue; - } - icon.setTintIcons(shouldTintIcon(icon)); - if (!icon.set(ic)) { - throw new InflationException("Couldn't create icon" + ic); - } - } - } - - private StatusBarIcon getIcon(Context context, StatusBarNotification sbn, boolean redact) - throws InflationException { - Notification n = sbn.getNotification(); - final boolean showPeopleAvatar = isImportantConversation() && !redact; - - // If cached, return corresponding cached values - if (showPeopleAvatar && mPeopleAvatar != null) { - return mPeopleAvatar; - } else if (!showPeopleAvatar && mSmallIcon != null) { - return mSmallIcon; - } - - Icon icon = showPeopleAvatar ? createPeopleAvatar(context) : n.getSmallIcon(); - if (icon == null) { - throw new InflationException("No icon in notification from " + sbn.getPackageName()); - } - - StatusBarIcon ic = new StatusBarIcon( - sbn.getUser(), - sbn.getPackageName(), - icon, - n.iconLevel, - n.number, - StatusBarIconView.contentDescForNotification(context, n)); - - // Cache if important conversation. - if (isImportantConversation()) { - if (showPeopleAvatar) { - mPeopleAvatar = ic; - } else { - mSmallIcon = ic; - } - } - return ic; - } - - private Icon createPeopleAvatar(Context context) throws InflationException { - // Attempt to extract form shortcut. - String conversationId = getChannel().getConversationId(); - ShortcutQuery query = new ShortcutQuery() - .setPackage(mSbn.getPackageName()) - .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED) - .setShortcutIds(Collections.singletonList(conversationId)); - List shortcuts = context.getSystemService(LauncherApps.class) - .getShortcuts(query, mSbn.getUser()); - Icon ic = null; - if (shortcuts != null && !shortcuts.isEmpty()) { - ic = shortcuts.get(0).getIcon(); - } - - // Fall back to notification large icon if available - if (ic == null) { - ic = mSbn.getNotification().getLargeIcon(); - } - - // Fall back to extract from message - if (ic == null) { - Bundle extras = mSbn.getNotification().extras; - List messages = Message.getMessagesFromBundleArray( - extras.getParcelableArray(Notification.EXTRA_MESSAGES)); - Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON); - - for (int i = messages.size() - 1; i >= 0; i--) { - Message message = messages.get(i); - Person sender = message.getSenderPerson(); - if (sender != null && sender != user) { - ic = message.getSenderPerson().getIcon(); - break; - } - } - } - - // Revert to small icon if still not available - if (ic == null) { - ic = mSbn.getNotification().getSmallIcon(); - } - if (ic == null) { - throw new InflationException("No icon in notification from " + mSbn.getPackageName()); - } - return ic; - } - - private void updateSensitiveIconState() { - try { - StatusBarIcon ic = getIcon(getRow().getContext(), mSbn, isSensitive()); - setIcons(ic, Arrays.asList(expandedIcon, aodIcon)); - } catch (InflationException e) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Unable to update icon", e); - } - } - } - - public void setIconTag(int key, Object tag) { - if (icon != null) { - icon.setTag(key, tag); - expandedIcon.setTag(key, tag); - } - - if (centeredIcon != null) { - centeredIcon.setTag(key, tag); - } - - if (aodIcon != null) { - aodIcon.setTag(key, tag); - } - } - - /** - * Update the notification icons. - * - * @param context the context to create the icons with. - * @param sbn the notification to read the icon from. - * @throws InflationException Exception if required icons are not valid or specified - */ - public void updateIcons(Context context, StatusBarNotification sbn) - throws InflationException { - if (icon != null) { - // Update the icon - mSmallIcon = null; - mPeopleAvatar = null; - - StatusBarIcon ic = getIcon(context, sbn, false /* redact */); - - icon.setNotification(sbn); - expandedIcon.setNotification(sbn); - aodIcon.setNotification(sbn); - setIcons(ic, Arrays.asList(icon, expandedIcon)); - - if (isSensitive()) { - ic = getIcon(context, sbn, true /* redact */); - } - setIcons(ic, Collections.singletonList(aodIcon)); - - if (centeredIcon != null) { - centeredIcon.setNotification(sbn); - setIcons(ic, Collections.singletonList(centeredIcon)); - } - } - } - - private boolean isImportantConversation() { - return getChannel() != null && getChannel().isImportantConversation(); - } - public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) { int rawColor = isLowPriority ? Notification.COLOR_DEFAULT : @@ -1125,9 +883,8 @@ public final class NotificationEntry extends ListEntry { getRow().setSensitive(sensitive, deviceSensitive); if (sensitive != mSensitive) { mSensitive = sensitive; - updateSensitiveIconState(); - if (mOnSensitiveChangedListener != null) { - mOnSensitiveChangedListener.run(); + for (int i = 0; i < mOnSensitivityChangedListeners.size(); i++) { + mOnSensitivityChangedListeners.get(i).onSensitivityChanged(this); } } } @@ -1136,8 +893,14 @@ public final class NotificationEntry extends ListEntry { return mSensitive; } - public void setOnSensitiveChangedListener(Runnable listener) { - mOnSensitiveChangedListener = listener; + /** Add a listener to be notified when the entry's sensitivity changes. */ + public void addOnSensitivityChangedListener(OnSensitivityChangedListener listener) { + mOnSensitivityChangedListeners.add(listener); + } + + /** Remove a listener that was registered above. */ + public void removeOnSensitivityChangedListener(OnSensitivityChangedListener listener) { + mOnSensitivityChangedListeners.remove(listener); } public boolean isPulseSuppressed() { @@ -1167,6 +930,12 @@ public final class NotificationEntry extends ListEntry { } } + /** Listener interface for {@link #addOnSensitivityChangedListener} */ + public interface OnSensitivityChangedListener { + /** Called when the sensitivity changes */ + void onSensitivityChanged(@NonNull NotificationEntry entry); + } + /** @see #getDismissState() */ public enum DismissState { /** User has not dismissed this notif or its parent */ 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 4beeedecfdf50..7237284888261 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 @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.collection.inflation; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; import android.annotation.Nullable; @@ -29,8 +28,6 @@ import android.util.Log; import android.view.ViewGroup; import com.android.internal.util.NotificationMessagingUtil; -import com.android.systemui.R; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -38,25 +35,22 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotifBindPipeline; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; 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.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; @@ -66,23 +60,23 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; - private final Context mContext; - private final NotifBindPipeline mNotifBindPipeline; - private final RowContentBindStage mRowContentBindStage; private final NotificationMessagingUtil mMessagingUtil; private final NotificationRemoteInputManager mNotificationRemoteInputManager; private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; + private final NotifBindPipeline mNotifBindPipeline; + private final RowContentBindStage mRowContentBindStage; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final Provider mRowInflaterTaskProvider; + private final ExpandableNotificationRowComponent.Builder + mExpandableNotificationRowComponentBuilder; + private final IconManager mIconManager; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; private NotificationRowContentBinder.InflationCallback mInflationCallback; private BindRowCallback mBindRowCallback; private NotificationClicker mNotificationClicker; - private final Provider mRowInflaterTaskProvider; - private final ExpandableNotificationRowComponent.Builder - mExpandableNotificationRowComponentBuilder; @Inject public NotificationRowBinderImpl( @@ -92,14 +86,10 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { NotificationLockscreenUserManager notificationLockscreenUserManager, NotifBindPipeline notifBindPipeline, RowContentBindStage rowContentBindStage, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, - KeyguardBypassController keyguardBypassController, - StatusBarStateController statusBarStateController, - NotificationGroupManager notificationGroupManager, - NotificationGutsManager notificationGutsManager, NotificationInterruptStateProvider notificationInterruptionStateProvider, Provider rowInflaterTaskProvider, - ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) { + ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, + IconManager iconManager) { mContext = context; mNotifBindPipeline = notifBindPipeline; mRowContentBindStage = rowContentBindStage; @@ -109,6 +99,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mNotificationInterruptStateProvider = notificationInterruptionStateProvider; mRowInflaterTaskProvider = rowInflaterTaskProvider; mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; + mIconManager = iconManager; } /** @@ -120,6 +111,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mPresenter = presenter; mListContainer = listContainer; mBindRowCallback = bindRowCallback; + + mIconManager.attach(); } public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) { @@ -142,12 +135,12 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { final StatusBarNotification sbn = entry.getSbn(); if (entry.rowExists()) { - entry.updateIcons(mContext, sbn); + mIconManager.updateIcons(entry); entry.reset(); updateNotification(entry, pmUser, sbn, entry.getRow()); entry.getRowController().setOnDismissRunnable(onDismissRunnable); } else { - entry.createIcons(mContext, sbn); + mIconManager.createIcons(entry); mRowInflaterTaskProvider.get().inflate(mContext, parent, entry, row -> { // Setup the controller for the view. @@ -227,8 +220,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); - // TODO: should updates to the entry be happening somewhere else? - entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); + // TODO: should this be happening somewhere else? + mIconManager.updateIconTags(entry, entry.targetSdk); row.setOnActivatedListener(mPresenter); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt new file mode 100644 index 0000000000000..afc123faba798 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt @@ -0,0 +1,41 @@ +/* + * 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.icon + +import android.app.Notification +import android.content.Context +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import javax.inject.Inject + +/** + * Testable wrapper around Context. + */ +class IconBuilder @Inject constructor( + private val context: Context +) { + fun createIconView(entry: NotificationEntry): StatusBarIconView { + return StatusBarIconView( + context, + "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}", + entry.sbn) + } + + fun getIconContentDescription(n: Notification): CharSequence { + return StatusBarIconView.contentDescForNotification(context, n) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt new file mode 100644 index 0000000000000..bb0fcafdb354b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt @@ -0,0 +1,338 @@ +/* + * 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.icon + +import android.app.Notification +import android.app.Person +import android.content.pm.LauncherApps +import android.graphics.drawable.Icon +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.ImageView +import com.android.internal.statusbar.StatusBarIcon +import com.android.systemui.R +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.InflationException +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import javax.inject.Inject + +/** + * Inflates and updates icons associated with notifications + * + * Notifications are represented by icons in a few different places -- in the status bar, in the + * notification shelf, in AOD, etc. This class is in charge of inflating the views that hold these + * icons and keeping the icon assets themselves up to date as notifications change. + * + * TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry. + * Long-term, it should probably live somewhere in the content inflation pipeline. + */ +class IconManager @Inject constructor( + private val notifCollection: CommonNotifCollection, + private val launcherApps: LauncherApps, + private val iconBuilder: IconBuilder +) { + fun attach() { + notifCollection.addCollectionListener(entryListener) + } + + private val entryListener = object : NotifCollectionListener { + override fun onEntryInit(entry: NotificationEntry) { + entry.addOnSensitivityChangedListener(sensitivityListener) + } + + override fun onEntryCleanUp(entry: NotificationEntry) { + entry.removeOnSensitivityChangedListener(sensitivityListener) + } + + override fun onRankingApplied() { + // When the sensitivity changes OR when the isImportantConversation status changes, + // we need to update the icons + for (entry in notifCollection.allNotifs) { + val isImportant = isImportantConversation(entry) + if (entry.icons.areIconsAvailable && + isImportant != entry.icons.isImportantConversation) { + updateIconsSafe(entry) + } + entry.icons.isImportantConversation = isImportant + } + } + } + + private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener { + entry -> updateIconsSafe(entry) + } + + /** + * Inflate icon views for each icon variant and assign appropriate icons to them. Stores the + * result in [NotificationEntry.getIcons]. + * + * @throws InflationException Exception if required icons are not valid or specified + */ + @Throws(InflationException::class) + fun createIcons(entry: NotificationEntry) { + // Construct the status bar icon view. + val sbIcon = iconBuilder.createIconView(entry) + sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + + // Construct the shelf icon view. + val shelfIcon = iconBuilder.createIconView(entry) + shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + + shelfIcon.visibility = View.INVISIBLE + // TODO: This doesn't belong here + shelfIcon.setOnVisibilityChangedListener { newVisibility: Int -> + if (entry.row != null) { + entry.row.setIconsVisible(newVisibility != View.VISIBLE) + } + } + + // Construct the aod icon view. + val aodIcon = iconBuilder.createIconView(entry) + aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + aodIcon.setIncreasedSize(true) + + // Construct the centered icon view. + val centeredIcon = if (entry.sbn.notification.isMediaNotification) { + iconBuilder.createIconView(entry).apply { + scaleType = ImageView.ScaleType.CENTER_INSIDE + } + } else { + null + } + + // Set the icon views' icons + val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry) + + try { + setIcon(entry, normalIconDescriptor, sbIcon) + setIcon(entry, sensitiveIconDescriptor, shelfIcon) + setIcon(entry, sensitiveIconDescriptor, aodIcon) + if (centeredIcon != null) { + setIcon(entry, normalIconDescriptor, centeredIcon) + } + entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, centeredIcon, entry.icons) + } catch (e: InflationException) { + entry.icons = IconPack.buildEmptyPack(entry.icons) + throw e + } + } + + /** + * Update the notification icons. + * + * @param entry the notification to read the icon from. + * @throws InflationException Exception if required icons are not valid or specified + */ + @Throws(InflationException::class) + fun updateIcons(entry: NotificationEntry) { + if (!entry.icons.areIconsAvailable) { + return + } + entry.icons.smallIconDescriptor = null + entry.icons.peopleAvatarDescriptor = null + + val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry) + + entry.icons.statusBarIcon?.let { + it.notification = entry.sbn + setIcon(entry, normalIconDescriptor, it) + } + + entry.icons.shelfIcon?.let { + it.notification = entry.sbn + setIcon(entry, normalIconDescriptor, it) + } + + entry.icons.aodIcon?.let { + it.notification = entry.sbn + setIcon(entry, sensitiveIconDescriptor, it) + } + + entry.icons.centeredIcon?.let { + it.notification = entry.sbn + setIcon(entry, sensitiveIconDescriptor, it) + } + } + + /** + * Updates tags on the icon views to match the posting app's target SDK level + * + * Note that this method MUST be called after both [createIcons] and [updateIcons]. + */ + fun updateIconTags(entry: NotificationEntry, targetSdk: Int) { + setTagOnIconViews( + entry.icons, + R.id.icon_is_pre_L, + targetSdk < Build.VERSION_CODES.LOLLIPOP) + } + + private fun updateIconsSafe(entry: NotificationEntry) { + try { + updateIcons(entry) + } catch (e: InflationException) { + // TODO This should mark the entire row as involved in an inflation error + Log.e(TAG, "Unable to update icon", e) + } + } + + @Throws(InflationException::class) + private fun getIconDescriptors( + entry: NotificationEntry + ): Pair { + val iconDescriptor = getIconDescriptor(entry, false /* redact */) + val sensitiveDescriptor = if (entry.isSensitive) { + getIconDescriptor(entry, true /* redact */) + } else { + iconDescriptor + } + return Pair(iconDescriptor, sensitiveDescriptor) + } + + @Throws(InflationException::class) + private fun getIconDescriptor( + entry: NotificationEntry, + redact: Boolean + ): StatusBarIcon { + val n = entry.sbn.notification + val showPeopleAvatar = isImportantConversation(entry) && !redact + + val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor + val smallIconDescriptor = entry.icons.smallIconDescriptor + + // If cached, return corresponding cached values + if (showPeopleAvatar && peopleAvatarDescriptor != null) { + return peopleAvatarDescriptor + } else if (!showPeopleAvatar && smallIconDescriptor != null) { + return smallIconDescriptor + } + + val icon = + (if (showPeopleAvatar) { + createPeopleAvatar(entry) + } else { + n.smallIcon + }) ?: throw InflationException( + "No icon in notification from " + entry.sbn.packageName) + + val ic = StatusBarIcon( + entry.sbn.user, + entry.sbn.packageName, + icon, + n.iconLevel, + n.number, + iconBuilder.getIconContentDescription(n)) + + // Cache if important conversation. + if (isImportantConversation(entry)) { + if (showPeopleAvatar) { + entry.icons.peopleAvatarDescriptor = ic + } else { + entry.icons.smallIconDescriptor = ic + } + } + + return ic + } + + @Throws(InflationException::class) + private fun setIcon( + entry: NotificationEntry, + iconDescriptor: StatusBarIcon, + iconView: StatusBarIconView + ) { + iconView.setTintIcons(shouldTintIconView(entry, iconView)) + if (!iconView.set(iconDescriptor)) { + throw InflationException("Couldn't create icon $iconDescriptor") + } + } + + @Throws(InflationException::class) + private fun createPeopleAvatar(entry: NotificationEntry): Icon? { + // Attempt to extract form shortcut. + val conversationId = entry.ranking.channel.conversationId + val query = LauncherApps.ShortcutQuery() + .setPackage(entry.sbn.packageName) + .setQueryFlags( + LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC + or LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED) + .setShortcutIds(listOf(conversationId)) + val shortcuts = launcherApps.getShortcuts(query, entry.sbn.user) + var ic: Icon? = null + if (shortcuts != null && shortcuts.isNotEmpty()) { + ic = shortcuts[0].icon + } + + // Fall back to notification large icon if available + if (ic == null) { + ic = entry.sbn.notification.getLargeIcon() + } + + // Fall back to extract from message + if (ic == null) { + val extras: Bundle = entry.sbn.notification.extras + val messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray( + extras.getParcelableArray(Notification.EXTRA_MESSAGES)) + val user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON) + for (i in messages.indices.reversed()) { + val message = messages[i] + val sender = message.senderPerson + if (sender != null && sender !== user) { + ic = message.senderPerson!!.icon + break + } + } + } + + // Revert to small icon if still not available + if (ic == null) { + ic = entry.sbn.notification.smallIcon + } + if (ic == null) { + throw InflationException("No icon in notification from " + entry.sbn.packageName) + } + return ic + } + + /** + * Determines if this icon should be tinted based on the sensitivity of the icon, its context + * and the user's indicated sensitivity preference. + * + * @param iconView The icon that should/should not be tinted. + */ + private fun shouldTintIconView(entry: NotificationEntry, iconView: StatusBarIconView): Boolean { + val usedInSensitiveContext = + iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon + return !isImportantConversation(entry) || usedInSensitiveContext && entry.isSensitive + } + + private fun isImportantConversation(entry: NotificationEntry): Boolean { + return entry.ranking.channel != null && entry.ranking.channel.isImportantConversation + } + + private fun setTagOnIconViews(icons: IconPack, key: Int, tag: Any) { + icons.statusBarIcon?.setTag(key, tag) + icons.shelfIcon?.setTag(key, tag) + icons.aodIcon?.setTag(key, tag) + icons.centeredIcon?.setTag(key, tag) + } +} + +private const val TAG = "IconManager" \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java new file mode 100644 index 0000000000000..054e381f096a7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java @@ -0,0 +1,134 @@ +/* + * 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.icon; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.StatusBarIconView; + +/** + * Data class for storing icons associated with a notification + */ +public final class IconPack { + + private final boolean mAreIconsAvailable; + @Nullable private final StatusBarIconView mStatusBarIcon; + @Nullable private final StatusBarIconView mShelfIcon; + @Nullable private final StatusBarIconView mAodIcon; + @Nullable private final StatusBarIconView mCenteredIcon; + + @Nullable private StatusBarIcon mSmallIconDescriptor; + @Nullable private StatusBarIcon mPeopleAvatarDescriptor; + + private boolean mIsImportantConversation; + + /** + * Builds an empty instance of IconPack that doesn't have any icons (because either they + * haven't been inflated yet or there was an error while inflating them). + */ + public static IconPack buildEmptyPack(@Nullable IconPack fromSource) { + return new IconPack(false, null, null, null, null, fromSource); + } + + /** + * Builds an instance of an IconPack that contains successfully-inflated icons + */ + public static IconPack buildPack( + @NonNull StatusBarIconView statusBarIcon, + @NonNull StatusBarIconView shelfIcon, + @NonNull StatusBarIconView aodIcon, + @Nullable StatusBarIconView centeredIcon, + @Nullable IconPack source) { + return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, centeredIcon, source); + } + + private IconPack( + boolean areIconsAvailable, + @Nullable StatusBarIconView statusBarIcon, + @Nullable StatusBarIconView shelfIcon, + @Nullable StatusBarIconView aodIcon, + @Nullable StatusBarIconView centeredIcon, + @Nullable IconPack source) { + mAreIconsAvailable = areIconsAvailable; + mStatusBarIcon = statusBarIcon; + mShelfIcon = shelfIcon; + mCenteredIcon = centeredIcon; + mAodIcon = aodIcon; + if (source != null) { + mIsImportantConversation = source.mIsImportantConversation; + } + } + + /** The version of the notification icon that appears in the status bar. */ + @Nullable + public StatusBarIconView getStatusBarIcon() { + return mStatusBarIcon; + } + + /** + * The version of the icon that appears in the "shelf" at the bottom of the notification shade. + * In general, this icon also appears somewhere on the notification and is "sucked" into the + * shelf as the scrolls beyond it. + */ + @Nullable + public StatusBarIconView getShelfIcon() { + return mShelfIcon; + } + + @Nullable + public StatusBarIconView getCenteredIcon() { + return mCenteredIcon; + } + + /** The version of the icon that's shown when pulsing (in AOD). */ + @Nullable + public StatusBarIconView getAodIcon() { + return mAodIcon; + } + + @Nullable + StatusBarIcon getSmallIconDescriptor() { + return mSmallIconDescriptor; + } + + void setSmallIconDescriptor(@Nullable StatusBarIcon smallIconDescriptor) { + mSmallIconDescriptor = smallIconDescriptor; + } + + @Nullable + StatusBarIcon getPeopleAvatarDescriptor() { + return mPeopleAvatarDescriptor; + } + + void setPeopleAvatarDescriptor(@Nullable StatusBarIcon peopleAvatarDescriptor) { + mPeopleAvatarDescriptor = peopleAvatarDescriptor; + } + + boolean isImportantConversation() { + return mIsImportantConversation; + } + + void setImportantConversation(boolean importantConversation) { + mIsImportantConversation = importantConversation; + } + + public boolean getAreIconsAvailable() { + return mAreIconsAvailable; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt index 373457d4e3366..dbfa27f1f68ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt @@ -37,7 +37,7 @@ class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, } (findViewById(R.id.icon) as StatusBarIconView).apply { - set(entry?.icon?.statusBarIcon) + set(entry?.icons?.statusBarIcon?.statusBarIcon) } } } 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 f61fe98309397..b1db5b5b4312f 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 @@ -579,7 +579,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @VisibleForTesting void updateShelfIconColor() { - StatusBarIconView expandedIcon = mEntry.expandedIcon; + StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon(); boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L)); boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon, ContrastColorUtil.getInstance(mContext)); @@ -1290,7 +1290,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setLongPressListener(null); mGroupParentWhenDismissed = mNotificationParent; mChildAfterViewWhenDismissed = null; - mEntry.icon.setDismissed(); + mEntry.getIcons().getStatusBarIcon().setDismissed(); if (isChildInGroup()) { List notificationChildren = mNotificationParent.getNotificationChildren(); @@ -1832,7 +1832,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mTranslateableViews.get(i).setTranslationX(0); } invalidateOutline(); - getEntry().expandedIcon.setScrollX(0); + getEntry().getIcons().getShelfIcon().setScrollX(0); } if (mMenuRow != null) { @@ -1912,7 +1912,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // In order to keep the shelf in sync with this swiping, we're simply translating // it's icon by the same amount. The translation is already being used for the normal // positioning, so we can use the scrollX instead. - getEntry().expandedIcon.setScrollX((int) -translationX); + getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX); } if (mMenuRow != null && mMenuRow.getMenuView() != null) { @@ -2111,7 +2111,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public StatusBarIconView getShelfIcon() { - return getEntry().expandedIcon; + return getEntry().getIcons().getShelfIcon(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 0996ff27e9a33..14442e346db49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -453,9 +453,10 @@ public class StackStateAnimator { needsAnimation = false; } NotificationEntry entry = row.getEntry(); - StatusBarIconView icon = entry.icon; - if (entry.centeredIcon != null && entry.centeredIcon.getParent() != null) { - icon = entry.centeredIcon; + StatusBarIconView icon = entry.getIcons().getStatusBarIcon(); + final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon(); + if (centeredIcon != null && centeredIcon.getParent() != null) { + icon = centeredIcon; } if (icon.getParent() != null) { icon.getLocationOnScreen(mTmpLocation); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index c39ee3a902308..51c02c9f93ab2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -269,7 +269,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } updateIsolatedIconLocation(false /* requireUpdate */); mNotificationIconAreaController.showIconIsolated(newEntry == null ? null - : newEntry.icon, animateIsolation); + : newEntry.getIcons().getStatusBarIcon(), animateIsolation); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index b09ccffdc4dec..f58cce58af74d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -253,8 +253,8 @@ public class NotificationIconAreaController implements DarkReceiver, boolean hidePulsing, boolean onlyShowCenteredIcon) { final boolean isCenteredNotificationIcon = mCenteredIconView != null - && entry.centeredIcon != null - && Objects.equals(entry.centeredIcon, mCenteredIconView); + && entry.getIcons().getCenteredIcon() != null + && Objects.equals(entry.getIcons().getCenteredIcon(), mCenteredIconView); if (onlyShowCenteredIcon) { return isCenteredNotificationIcon; } @@ -307,7 +307,7 @@ public class NotificationIconAreaController implements DarkReceiver, } private void updateShelfIcons() { - updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons, + updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons, true /* showAmbient */, true /* showLowPriority */, false /* hideDismissed */, @@ -319,7 +319,7 @@ public class NotificationIconAreaController implements DarkReceiver, } public void updateStatusBarIcons() { - updateIconsForLayout(entry -> entry.icon, mNotificationIcons, + updateIconsForLayout(entry -> entry.getIcons().getStatusBarIcon(), mNotificationIcons, false /* showAmbient */, mShowLowPriority, true /* hideDismissed */, @@ -331,7 +331,7 @@ public class NotificationIconAreaController implements DarkReceiver, } private void updateCenterIcon() { - updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon, + updateIconsForLayout(entry -> entry.getIcons().getCenteredIcon(), mCenteredIcon, false /* showAmbient */, true /* showLowPriority */, false /* hideDismissed */, @@ -343,7 +343,7 @@ public class NotificationIconAreaController implements DarkReceiver, } public void updateAodNotificationIcons() { - updateIconsForLayout(entry -> entry.aodIcon, mAodIcons, + updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons, false /* showAmbient */, true /* showLowPriority */, true /* hideDismissed */, @@ -517,7 +517,7 @@ public class NotificationIconAreaController implements DarkReceiver, * Shows the icon view given in the center. */ public void showIconCentered(NotificationEntry entry) { - StatusBarIconView icon = entry == null ? null : entry.centeredIcon; + StatusBarIconView icon = entry == null ? null : entry.getIcons().getCenteredIcon(); if (!Objects.equals(mCenteredIconView, icon)) { mCenteredIconView = icon; updateNotificationIcons(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index e960185ebe3a2..c356e0d16512d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -329,10 +329,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testIconScrollXAfterTranslationAndReset() throws Exception { mGroupRow.setTranslation(50); - assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX()); + assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX()); mGroupRow.resetTranslation(); - assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX()); + assertEquals(0, mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index a21a047d9a70a..cb44703478266 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.content.pm.LauncherApps; import android.os.Handler; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; @@ -61,6 +62,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.icon.IconBuilder; +import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -245,14 +248,13 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mLockscreenUserManager, pipeline, stage, - true, /* allowLongPress */ - mock(KeyguardBypassController.class), - mock(StatusBarStateController.class), - mGroupManager, - mGutsManager, mNotificationInterruptionStateProvider, RowInflaterTask::new, - mExpandableNotificationRowComponentBuilder); + mExpandableNotificationRowComponentBuilder, + new IconManager( + mEntryManager, + mock(LauncherApps.class), + new IconBuilder(mContext))); mEntryManager.setUpWithPresenter(mPresenter); mEntryManager.addNotificationEntryListener(mEntryListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 5a89fc44f970d..dc3374bd511be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -34,6 +34,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherApps; import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -53,6 +54,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.icon.IconBuilder; +import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -93,6 +96,7 @@ public class NotificationTestHelper { private final NotifBindPipeline mBindPipeline; private final NotifCollectionListener mBindPipelineEntryListener; private final RowContentBindStage mBindStage; + private final IconManager mIconManager; private StatusBarStateController mStatusBarStateController; public NotificationTestHelper(Context context, TestableDependency dependency) { @@ -106,6 +110,10 @@ public class NotificationTestHelper { mock(KeyguardBypassController.class), mock(NotificationGroupManager.class), mock(ConfigurationControllerImpl.class)); mGroupManager.setHeadsUpManager(mHeadsUpManager); + mIconManager = new IconManager( + mock(CommonNotifCollection.class), + mock(LauncherApps.class), + new IconBuilder(mContext)); NotificationContentInflater contentBinder = new NotificationContentInflater( mock(NotifRemoteViewCache.class), @@ -377,7 +385,7 @@ public class NotificationTestHelper { .build(); entry.setRow(row); - entry.createIcons(mContext, entry.getSbn()); + mIconManager.createIcons(entry); row.setEntry(entry); mBindPipelineEntryListener.onEntryInit(entry);