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
This commit is contained in:
Ned Burns
2020-03-13 20:52:43 -04:00
parent 88aeaf7dc8
commit d8b5154b25
17 changed files with 619 additions and 322 deletions

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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="

View File

@@ -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<OnSensitivityChangedListener> 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<StatusBarIconView> 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<ShortcutInfo> 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<Message> 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 */

View File

@@ -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<RowInflaterTask> 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<RowInflaterTask> 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<RowInflaterTask> 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);

View File

@@ -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)
}
}

View File

@@ -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<StatusBarIcon, StatusBarIcon> {
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<Person>(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"

View File

@@ -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;
}
}

View File

@@ -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)
}
}
}

View File

@@ -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<ExpandableNotificationRow> 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

View File

@@ -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);

View File

@@ -269,7 +269,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
: newEntry.icon, animateIsolation);
: newEntry.getIcons().getStatusBarIcon(), animateIsolation);
}
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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);