Convert NotificationContentInflater to singleton
Convert NotificationContentInflater into a singleton since it has almost no state, making it easier to inject and re-use. Move the only per-row state it has, cached remote views, to a separate class NotifRemoteViewCacheImpl that manages the lifetime of the cached remote views based off the notification's lifecycle. This has the added benefit of being re-usable for content recycling later on. Bug: 145749521 Test: atest SystemUITests Test: atest NotificationContentInflaterTest w/o @Suppress locally Test: smoke test (add, remove, cancel, expand, groups, HUN) Change-Id: I7c05b19a0e12bced02bfdaa21e0243f35d64029f
This commit is contained in:
@@ -584,7 +584,15 @@ public class NotificationRemoteInputManager implements Dumpable {
|
||||
|
||||
public void bindRow(ExpandableNotificationRow row) {
|
||||
row.setRemoteInputController(mRemoteInputController);
|
||||
row.setRemoteViewClickHandler(mOnClickHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return on-click handler for notification remote views
|
||||
*
|
||||
* @return on-click handler
|
||||
*/
|
||||
public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() {
|
||||
return mOnClickHandler;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
@@ -26,7 +28,7 @@ import dagger.Provides;
|
||||
/**
|
||||
* Dagger Module providing common dependencies of StatusBar.
|
||||
*/
|
||||
@Module
|
||||
@Module(includes = {NotificationRowModule.class})
|
||||
public class StatusBarDependenciesModule {
|
||||
/**
|
||||
* Provides our instance of CommandQueue which is considered optional.
|
||||
|
||||
@@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.NotificationClicker;
|
||||
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
|
||||
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
|
||||
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||
@@ -67,6 +67,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
|
||||
|
||||
private final Context mContext;
|
||||
private final NotificationRowContentBinder mRowContentBinder;
|
||||
private final NotificationMessagingUtil mMessagingUtil;
|
||||
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
|
||||
this::logNotificationExpansion;
|
||||
@@ -79,7 +80,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
private NotificationPresenter mPresenter;
|
||||
private NotificationListContainer mListContainer;
|
||||
private HeadsUpManager mHeadsUpManager;
|
||||
private NotificationContentInflater.InflationCallback mInflationCallback;
|
||||
private NotificationRowContentBinder.InflationCallback mInflationCallback;
|
||||
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
|
||||
private BindRowCallback mBindRowCallback;
|
||||
private NotificationClicker mNotificationClicker;
|
||||
@@ -90,6 +91,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
Context context,
|
||||
NotificationRemoteInputManager notificationRemoteInputManager,
|
||||
NotificationLockscreenUserManager notificationLockscreenUserManager,
|
||||
NotificationRowContentBinder rowContentBinder,
|
||||
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
|
||||
KeyguardBypassController keyguardBypassController,
|
||||
StatusBarStateController statusBarStateController,
|
||||
@@ -98,6 +100,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
|
||||
NotificationLogger logger) {
|
||||
mContext = context;
|
||||
mRowContentBinder = rowContentBinder;
|
||||
mMessagingUtil = new NotificationMessagingUtil(context);
|
||||
mNotificationRemoteInputManager = notificationRemoteInputManager;
|
||||
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
|
||||
@@ -124,7 +127,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
mOnAppOpsClickListener = mGutsManager::openGuts;
|
||||
}
|
||||
|
||||
public void setInflationCallback(NotificationContentInflater.InflationCallback callback) {
|
||||
public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
|
||||
mInflationCallback = callback;
|
||||
}
|
||||
|
||||
@@ -169,6 +172,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
row.setGroupManager(mGroupManager);
|
||||
row.setHeadsUpManager(mHeadsUpManager);
|
||||
row.setOnExpandClickListener(mPresenter);
|
||||
row.setContentBinder(mRowContentBinder);
|
||||
row.setInflationCallback(mInflationCallback);
|
||||
if (mAllowLongPress) {
|
||||
row.setLongPressListener(mGutsManager::openGuts);
|
||||
|
||||
@@ -65,7 +65,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import android.widget.Chronometer;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
@@ -150,7 +149,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
private StatusBarStateController mStatusbarStateController;
|
||||
private KeyguardBypassController mBypassController;
|
||||
private LayoutListener mLayoutListener;
|
||||
private final NotificationContentInflater mNotificationInflater;
|
||||
private NotificationRowContentBinder mNotificationContentBinder;
|
||||
private int mIconTransformContentShift;
|
||||
private int mIconTransformContentShiftNoIcon;
|
||||
private int mMaxHeadsUpHeightBeforeN;
|
||||
@@ -464,7 +463,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
* Inflate views based off the inflation flags set. Inflation happens asynchronously.
|
||||
*/
|
||||
public void inflateViews() {
|
||||
mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
|
||||
mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
|
||||
false /* forceInflate */, mInflationCallback);
|
||||
}
|
||||
|
||||
@@ -478,7 +477,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
// View should not be reinflated in the future
|
||||
clearInflationFlags(inflationFlag);
|
||||
Runnable freeViewRunnable =
|
||||
() -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag);
|
||||
() -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag);
|
||||
switch (inflationFlag) {
|
||||
case FLAG_CONTENT_VIEW_HEADS_UP:
|
||||
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
|
||||
@@ -852,7 +851,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
mIsChildInGroup = isChildInGroup;
|
||||
if (mIsLowPriority) {
|
||||
int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
|
||||
mNotificationInflater.bindContent(mEntry, this, flags, mBindParams,
|
||||
mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams,
|
||||
false /* forceInflate */, mInflationCallback);
|
||||
}
|
||||
}
|
||||
@@ -1259,7 +1258,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
l.reInflateViews();
|
||||
}
|
||||
mEntry.getSbn().clearPackageContext();
|
||||
mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
|
||||
mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
|
||||
true /* forceInflate */, mInflationCallback);
|
||||
}
|
||||
|
||||
@@ -1634,10 +1633,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
mBindParams.usesIncreasedHeadsUpHeight = use;
|
||||
}
|
||||
|
||||
public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
|
||||
mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set callback for notification content inflation
|
||||
*
|
||||
@@ -1652,7 +1647,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
mNeedsRedaction = needsRedaction;
|
||||
if (needsRedaction) {
|
||||
setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
|
||||
mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
|
||||
mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
|
||||
mBindParams, false /* forceInflate */, mInflationCallback);
|
||||
} else {
|
||||
clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
|
||||
@@ -1661,9 +1656,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public NotificationContentInflater getNotificationInflater() {
|
||||
return mNotificationInflater;
|
||||
/**
|
||||
* Set the binder implementation for notification content views.
|
||||
*
|
||||
* @param contentBinder content binder
|
||||
*/
|
||||
public void setContentBinder(NotificationRowContentBinder contentBinder) {
|
||||
mNotificationContentBinder = contentBinder;
|
||||
}
|
||||
|
||||
public interface ExpansionLogger {
|
||||
@@ -1672,7 +1671,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
|
||||
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mNotificationInflater = new NotificationContentInflater();
|
||||
mMenuRow = new NotificationMenuRow(mContext);
|
||||
mImageResolver = new NotificationInlineImageResolver(context,
|
||||
new NotificationInlineImageCache());
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.row;
|
||||
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
||||
|
||||
/**
|
||||
* Caches {@link RemoteViews} for a notification's content views.
|
||||
*/
|
||||
public interface NotifRemoteViewCache {
|
||||
|
||||
/**
|
||||
* Whether the notification has the remote view cached
|
||||
*
|
||||
* @param entry notification
|
||||
* @param flag inflation flag for content view
|
||||
* @return true if the remote view is cached
|
||||
*/
|
||||
boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag);
|
||||
|
||||
/**
|
||||
* Get the remote view for the content flag specified.
|
||||
*
|
||||
* @param entry notification
|
||||
* @param flag inflation flag for the content view
|
||||
* @return the remote view if it is cached, null otherwise
|
||||
*/
|
||||
@Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag);
|
||||
|
||||
/**
|
||||
* Cache a remote view for a given content flag on a notification.
|
||||
*
|
||||
* @param entry notification
|
||||
* @param flag inflation flag for the content view
|
||||
* @param remoteView remote view to store
|
||||
*/
|
||||
void putCachedView(
|
||||
NotificationEntry entry,
|
||||
@InflationFlag int flag,
|
||||
RemoteViews remoteView);
|
||||
|
||||
/**
|
||||
* Remove a cached remote view for a given content flag on a notification.
|
||||
*
|
||||
* @param entry notification
|
||||
* @param flag inflation flag for the content view
|
||||
*/
|
||||
void removeCachedView(NotificationEntry entry, @InflationFlag int flag);
|
||||
|
||||
/**
|
||||
* Clear a notification's remote view cache.
|
||||
*
|
||||
* @param entry notification
|
||||
*/
|
||||
void clearCache(NotificationEntry entry);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.row;
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Implementation of remote view cache that keeps remote views cached for all active notifications.
|
||||
*/
|
||||
public class NotifRemoteViewCacheImpl implements NotifRemoteViewCache {
|
||||
private final Map<NotificationEntry, SparseArray<RemoteViews>> mNotifCachedContentViews =
|
||||
new ArrayMap<>();
|
||||
|
||||
@Inject
|
||||
NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) {
|
||||
entryManager.addNotificationEntryListener(mEntryListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag) {
|
||||
return getCachedView(entry, flag) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag) {
|
||||
SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
|
||||
if (contentViews == null) {
|
||||
return null;
|
||||
}
|
||||
return contentViews.get(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putCachedView(
|
||||
NotificationEntry entry,
|
||||
@InflationFlag int flag,
|
||||
RemoteViews remoteView) {
|
||||
/**
|
||||
* TODO: We should be more strict here in the future (i.e. throw an exception) if the
|
||||
* content views aren't created. We don't do that right now because we have edge cases
|
||||
* where we may bind/unbind content after a notification is removed.
|
||||
*/
|
||||
SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
|
||||
if (contentViews == null) {
|
||||
return;
|
||||
}
|
||||
contentViews.put(flag, remoteView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) {
|
||||
SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
|
||||
if (contentViews == null) {
|
||||
return;
|
||||
}
|
||||
contentViews.remove(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache(NotificationEntry entry) {
|
||||
SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
|
||||
if (contentViews == null) {
|
||||
return;
|
||||
}
|
||||
contentViews.clear();
|
||||
}
|
||||
|
||||
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
|
||||
@Override
|
||||
public void onPendingEntryAdded(NotificationEntry entry) {
|
||||
mNotifCachedContentViews.put(entry, new SparseArray<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntryRemoved(
|
||||
NotificationEntry entry,
|
||||
@Nullable NotificationVisibility visibility,
|
||||
boolean removedByUser) {
|
||||
mNotifCachedContentViews.remove(entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
|
||||
|
||||
@@ -26,7 +27,6 @@ import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.CancellationSignal;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
@@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.ImageMessageConsumer;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.statusbar.InflationTask;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.SmartReplyController;
|
||||
import com.android.systemui.statusbar.notification.InflationException;
|
||||
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
|
||||
@@ -49,17 +50,30 @@ import com.android.systemui.util.Assert;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
|
||||
* asynchronously building the content's {@link RemoteViews} and applying it to the row.
|
||||
*/
|
||||
@Singleton
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public class NotificationContentInflater implements NotificationRowContentBinder {
|
||||
|
||||
public static final String TAG = "NotifContentInflater";
|
||||
|
||||
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
|
||||
private boolean mInflateSynchronously = false;
|
||||
private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
|
||||
private final NotificationRemoteInputManager mRemoteInputManager;
|
||||
private final NotifRemoteViewCache mRemoteViewCache;
|
||||
|
||||
@Inject
|
||||
public NotificationContentInflater(
|
||||
NotifRemoteViewCache remoteViewCache,
|
||||
NotificationRemoteInputManager remoteInputManager) {
|
||||
mRemoteViewCache = remoteViewCache;
|
||||
mRemoteInputManager = remoteInputManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindContent(
|
||||
@@ -76,27 +90,27 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return;
|
||||
}
|
||||
|
||||
StatusBarNotification sbn = row.getEntry().getSbn();
|
||||
StatusBarNotification sbn = entry.getSbn();
|
||||
|
||||
// To check if the notification has inline image and preload inline image if necessary.
|
||||
row.getImageResolver().preloadImages(sbn.getNotification());
|
||||
|
||||
if (forceInflate) {
|
||||
mCachedContentViews.clear();
|
||||
mRemoteViewCache.clearCache(entry);
|
||||
}
|
||||
|
||||
AsyncInflationTask task = new AsyncInflationTask(
|
||||
sbn,
|
||||
mInflateSynchronously,
|
||||
contentToBind,
|
||||
mCachedContentViews,
|
||||
mRemoteViewCache,
|
||||
entry,
|
||||
row,
|
||||
bindParams.isLowPriority,
|
||||
bindParams.isChildInGroup,
|
||||
bindParams.usesIncreasedHeight,
|
||||
bindParams.usesIncreasedHeadsUpHeight,
|
||||
callback,
|
||||
mRemoteViewClickHandler);
|
||||
mRemoteInputManager.getRemoteViewsOnClickHandler());
|
||||
if (mInflateSynchronously) {
|
||||
task.onPostExecute(task.doInBackground());
|
||||
} else {
|
||||
@@ -123,13 +137,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
result = inflateSmartReplyViews(result, reInflateFlags, entry,
|
||||
row.getContext(), packageContext, row.getHeadsUpManager(),
|
||||
row.getExistingSmartRepliesAndActions());
|
||||
|
||||
apply(
|
||||
inflateSynchronously,
|
||||
result,
|
||||
reInflateFlags,
|
||||
mCachedContentViews,
|
||||
mRemoteViewCache,
|
||||
entry,
|
||||
row,
|
||||
mRemoteViewClickHandler,
|
||||
mRemoteInputManager.getRemoteViewsOnClickHandler(),
|
||||
null);
|
||||
return result;
|
||||
}
|
||||
@@ -149,42 +165,33 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
int curFlag = 1;
|
||||
while (contentToUnbind != 0) {
|
||||
if ((contentToUnbind & curFlag) != 0) {
|
||||
freeNotificationView(row, curFlag);
|
||||
freeNotificationView(entry, row, curFlag);
|
||||
}
|
||||
contentToUnbind &= ~curFlag;
|
||||
curFlag = curFlag << 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set click handler for notification remote views
|
||||
*
|
||||
* @param remoteViewClickHandler click handler for remote views
|
||||
*/
|
||||
public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
|
||||
mRemoteViewClickHandler = remoteViewClickHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the content view associated with the inflation flag. Will only succeed if the
|
||||
* view is safe to remove.
|
||||
*
|
||||
* @param inflateFlag the flag corresponding to the content view which should be freed
|
||||
*/
|
||||
private void freeNotificationView(ExpandableNotificationRow row,
|
||||
private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
|
||||
@InflationFlag int inflateFlag) {
|
||||
switch (inflateFlag) {
|
||||
case FLAG_CONTENT_VIEW_HEADS_UP:
|
||||
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
|
||||
row.getPrivateLayout().setHeadsUpChild(null);
|
||||
mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
|
||||
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
|
||||
row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
|
||||
}
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_PUBLIC:
|
||||
if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
|
||||
row.getPublicLayout().setContractedChild(null);
|
||||
mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
|
||||
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
|
||||
}
|
||||
break;
|
||||
case FLAG_CONTENT_VIEW_CONTRACTED:
|
||||
@@ -245,11 +252,12 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return result;
|
||||
}
|
||||
|
||||
public static CancellationSignal apply(
|
||||
private static CancellationSignal apply(
|
||||
boolean inflateSynchronously,
|
||||
InflationProgress result,
|
||||
@InflationFlag int reInflateFlags,
|
||||
ArrayMap<Integer, RemoteViews> cachedContentViews,
|
||||
NotifRemoteViewCache remoteViewCache,
|
||||
NotificationEntry entry,
|
||||
ExpandableNotificationRow row,
|
||||
RemoteViews.OnClickHandler remoteViewClickHandler,
|
||||
@Nullable InflationCallback callback) {
|
||||
@@ -261,7 +269,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if ((reInflateFlags & flag) != 0) {
|
||||
boolean isNewView =
|
||||
!canReapplyRemoteView(result.newContentView,
|
||||
cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
ApplyCallback applyCallback = new ApplyCallback() {
|
||||
@Override
|
||||
public void setResultView(View v) {
|
||||
@@ -273,8 +281,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return result.newContentView;
|
||||
}
|
||||
};
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
|
||||
row, isNewView, remoteViewClickHandler, callback, privateLayout,
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
|
||||
entry, row, isNewView, remoteViewClickHandler, callback, privateLayout,
|
||||
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
|
||||
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
|
||||
runningInflations, applyCallback);
|
||||
@@ -285,7 +293,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if (result.newExpandedView != null) {
|
||||
boolean isNewView =
|
||||
!canReapplyRemoteView(result.newExpandedView,
|
||||
cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
|
||||
remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED));
|
||||
ApplyCallback applyCallback = new ApplyCallback() {
|
||||
@Override
|
||||
public void setResultView(View v) {
|
||||
@@ -297,8 +305,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return result.newExpandedView;
|
||||
}
|
||||
};
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
|
||||
cachedContentViews, row, isNewView, remoteViewClickHandler,
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
|
||||
entry, row, isNewView, remoteViewClickHandler,
|
||||
callback, privateLayout, privateLayout.getExpandedChild(),
|
||||
privateLayout.getVisibleWrapper(
|
||||
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
|
||||
@@ -311,7 +319,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if (result.newHeadsUpView != null) {
|
||||
boolean isNewView =
|
||||
!canReapplyRemoteView(result.newHeadsUpView,
|
||||
cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
|
||||
remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP));
|
||||
ApplyCallback applyCallback = new ApplyCallback() {
|
||||
@Override
|
||||
public void setResultView(View v) {
|
||||
@@ -323,8 +331,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return result.newHeadsUpView;
|
||||
}
|
||||
};
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
|
||||
cachedContentViews, row, isNewView, remoteViewClickHandler,
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
|
||||
entry, row, isNewView, remoteViewClickHandler,
|
||||
callback, privateLayout, privateLayout.getHeadsUpChild(),
|
||||
privateLayout.getVisibleWrapper(
|
||||
VISIBLE_TYPE_HEADSUP), runningInflations,
|
||||
@@ -336,7 +344,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if ((reInflateFlags & flag) != 0) {
|
||||
boolean isNewView =
|
||||
!canReapplyRemoteView(result.newPublicView,
|
||||
cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
|
||||
remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC));
|
||||
ApplyCallback applyCallback = new ApplyCallback() {
|
||||
@Override
|
||||
public void setResultView(View v) {
|
||||
@@ -348,15 +356,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
return result.newPublicView;
|
||||
}
|
||||
};
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
|
||||
row, isNewView, remoteViewClickHandler, callback,
|
||||
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
|
||||
entry, row, isNewView, remoteViewClickHandler, callback,
|
||||
publicLayout, publicLayout.getContractedChild(),
|
||||
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
|
||||
runningInflations, applyCallback);
|
||||
}
|
||||
|
||||
// Let's try to finish, maybe nobody is even inflating anything
|
||||
finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
|
||||
finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry,
|
||||
row);
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
cancellationSignal.setOnCancelListener(
|
||||
() -> runningInflations.values().forEach(CancellationSignal::cancel));
|
||||
@@ -369,7 +378,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
final InflationProgress result,
|
||||
final @InflationFlag int reInflateFlags,
|
||||
@InflationFlag int inflationId,
|
||||
final ArrayMap<Integer, RemoteViews> cachedContentViews,
|
||||
final NotifRemoteViewCache remoteViewCache,
|
||||
final NotificationEntry entry,
|
||||
final ExpandableNotificationRow row,
|
||||
boolean isNewView,
|
||||
RemoteViews.OnClickHandler remoteViewClickHandler,
|
||||
@@ -422,8 +432,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
existingWrapper.onReinflated();
|
||||
}
|
||||
runningInflations.remove(inflationId);
|
||||
finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
|
||||
callback, row);
|
||||
finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
|
||||
callback, entry, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -488,11 +498,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
* @return true if the inflation was finished
|
||||
*/
|
||||
private static boolean finishIfDone(InflationProgress result,
|
||||
@InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
|
||||
@InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
|
||||
HashMap<Integer, CancellationSignal> runningInflations,
|
||||
@Nullable InflationCallback endListener, ExpandableNotificationRow row) {
|
||||
@Nullable InflationCallback endListener, NotificationEntry entry,
|
||||
ExpandableNotificationRow row) {
|
||||
Assert.isMainThread();
|
||||
NotificationEntry entry = row.getEntry();
|
||||
NotificationContentView privateLayout = row.getPrivateLayout();
|
||||
NotificationContentView publicLayout = row.getPublicLayout();
|
||||
if (runningInflations.isEmpty()) {
|
||||
@@ -500,23 +510,27 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if (result.inflatedContentView != null) {
|
||||
// New view case
|
||||
privateLayout.setContractedChild(result.inflatedContentView);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
|
||||
} else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
|
||||
result.newContentView);
|
||||
} else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
|
||||
// Reinflation case. Only update if it's still cached (i.e. view has not been
|
||||
// freed while inflating).
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
|
||||
result.newContentView);
|
||||
}
|
||||
}
|
||||
|
||||
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
|
||||
if (result.inflatedExpandedView != null) {
|
||||
privateLayout.setExpandedChild(result.inflatedExpandedView);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
|
||||
result.newExpandedView);
|
||||
} else if (result.newExpandedView == null) {
|
||||
privateLayout.setExpandedChild(null);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
|
||||
} else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
|
||||
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
|
||||
} else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
|
||||
result.newExpandedView);
|
||||
}
|
||||
if (result.newExpandedView != null) {
|
||||
privateLayout.setExpandedInflatedSmartReplies(
|
||||
@@ -530,12 +544,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
|
||||
if (result.inflatedHeadsUpView != null) {
|
||||
privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
|
||||
result.newHeadsUpView);
|
||||
} else if (result.newHeadsUpView == null) {
|
||||
privateLayout.setHeadsUpChild(null);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
|
||||
} else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
|
||||
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
|
||||
} else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
|
||||
result.newHeadsUpView);
|
||||
}
|
||||
if (result.newHeadsUpView != null) {
|
||||
privateLayout.setHeadsUpInflatedSmartReplies(
|
||||
@@ -548,16 +564,18 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
|
||||
if (result.inflatedPublicView != null) {
|
||||
publicLayout.setContractedChild(result.inflatedPublicView);
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
|
||||
} else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
|
||||
cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
|
||||
result.newPublicView);
|
||||
} else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
|
||||
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
|
||||
result.newPublicView);
|
||||
}
|
||||
}
|
||||
|
||||
entry.headsUpStatusBarText = result.headsUpStatusBarText;
|
||||
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
|
||||
if (endListener != null) {
|
||||
endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
|
||||
endListener.onAsyncInflationFinished(entry, reInflateFlags);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -615,7 +633,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
|
||||
implements InflationCallback, InflationTask {
|
||||
|
||||
private final StatusBarNotification mSbn;
|
||||
private final NotificationEntry mEntry;
|
||||
private final Context mContext;
|
||||
private final boolean mInflateSynchronously;
|
||||
private final boolean mIsLowPriority;
|
||||
@@ -624,17 +642,17 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
private final InflationCallback mCallback;
|
||||
private final boolean mUsesIncreasedHeadsUpHeight;
|
||||
private @InflationFlag int mReInflateFlags;
|
||||
private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
|
||||
private final NotifRemoteViewCache mRemoteViewCache;
|
||||
private ExpandableNotificationRow mRow;
|
||||
private Exception mError;
|
||||
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
|
||||
private CancellationSignal mCancellationSignal;
|
||||
|
||||
private AsyncInflationTask(
|
||||
StatusBarNotification notification,
|
||||
boolean inflateSynchronously,
|
||||
@InflationFlag int reInflateFlags,
|
||||
ArrayMap<Integer, RemoteViews> cachedContentViews,
|
||||
NotifRemoteViewCache cache,
|
||||
NotificationEntry entry,
|
||||
ExpandableNotificationRow row,
|
||||
boolean isLowPriority,
|
||||
boolean isChildInGroup,
|
||||
@@ -642,11 +660,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
boolean usesIncreasedHeadsUpHeight,
|
||||
InflationCallback callback,
|
||||
RemoteViews.OnClickHandler remoteViewClickHandler) {
|
||||
mEntry = entry;
|
||||
mRow = row;
|
||||
mSbn = notification;
|
||||
mInflateSynchronously = inflateSynchronously;
|
||||
mReInflateFlags = reInflateFlags;
|
||||
mCachedContentViews = cachedContentViews;
|
||||
mRemoteViewCache = cache;
|
||||
mContext = mRow.getContext();
|
||||
mIsLowPriority = isLowPriority;
|
||||
mIsChildInGroup = isChildInGroup;
|
||||
@@ -654,7 +672,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
|
||||
mRemoteViewClickHandler = remoteViewClickHandler;
|
||||
mCallback = callback;
|
||||
NotificationEntry entry = row.getEntry();
|
||||
entry.setInflationTask(this);
|
||||
}
|
||||
|
||||
@@ -667,12 +684,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
@Override
|
||||
protected InflationProgress doInBackground(Void... params) {
|
||||
try {
|
||||
final StatusBarNotification sbn = mEntry.getSbn();
|
||||
final Notification.Builder recoveredBuilder
|
||||
= Notification.Builder.recoverBuilder(mContext,
|
||||
mSbn.getNotification());
|
||||
sbn.getNotification());
|
||||
|
||||
Context packageContext = mSbn.getPackageContext(mContext);
|
||||
Notification notification = mSbn.getNotification();
|
||||
Context packageContext = sbn.getPackageContext(mContext);
|
||||
Notification notification = sbn.getNotification();
|
||||
if (notification.isMediaNotification()) {
|
||||
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
|
||||
packageContext);
|
||||
@@ -681,7 +699,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
|
||||
recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
|
||||
mUsesIncreasedHeadsUpHeight, packageContext);
|
||||
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
|
||||
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
|
||||
mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
|
||||
mRow.getExistingSmartRepliesAndActions());
|
||||
} catch (Exception e) {
|
||||
@@ -694,15 +712,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
protected void onPostExecute(InflationProgress result) {
|
||||
if (mError == null) {
|
||||
mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
|
||||
mCachedContentViews, mRow, mRemoteViewClickHandler, this);
|
||||
mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this);
|
||||
} else {
|
||||
handleError(mError);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleError(Exception e) {
|
||||
mRow.getEntry().onInflationTaskFinished();
|
||||
StatusBarNotification sbn = mRow.getEntry().getSbn();
|
||||
mEntry.onInflationTaskFinished();
|
||||
StatusBarNotification sbn = mEntry.getSbn();
|
||||
final String ident = sbn.getPackageName() + "/0x"
|
||||
+ Integer.toHexString(sbn.getId());
|
||||
Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
|
||||
@@ -736,10 +754,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
@Override
|
||||
public void onAsyncInflationFinished(NotificationEntry entry,
|
||||
@InflationFlag int inflatedFlags) {
|
||||
mRow.getEntry().onInflationTaskFinished();
|
||||
mEntry.onInflationTaskFinished();
|
||||
mRow.onNotificationUpdated();
|
||||
if (mCallback != null) {
|
||||
mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
|
||||
mCallback.onAsyncInflationFinished(mEntry, inflatedFlags);
|
||||
}
|
||||
|
||||
// Notify the resolver that the inflation task has finished,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.row;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
|
||||
/**
|
||||
* Dagger Module containing notification row and view inflation implementations.
|
||||
*/
|
||||
@Module
|
||||
public abstract class NotificationRowModule {
|
||||
/**
|
||||
* Provides notification row content binder instance.
|
||||
*/
|
||||
@Binds
|
||||
@Singleton
|
||||
public abstract NotificationRowContentBinder provideNotificationRowContentBinder(
|
||||
NotificationContentInflater contentBinderImpl);
|
||||
|
||||
/**
|
||||
* Provides notification remote view cache instance.
|
||||
*/
|
||||
@Binds
|
||||
@Singleton
|
||||
public abstract NotifRemoteViewCache provideNotifRemoteViewCache(
|
||||
NotifRemoteViewCacheImpl cacheImpl);
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import com.android.systemui.statusbar.InflationTask;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
|
||||
@@ -428,7 +427,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
|
||||
* The notification is still pending inflation but we've decided that we no longer need
|
||||
* the content view (e.g. suppression might have changed and we decided we need to transfer
|
||||
* back). However, there is no way to abort just this inflation if other inflation requests
|
||||
* have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead
|
||||
* have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead
|
||||
* we just flag it as aborted and free when it's inflated.
|
||||
*/
|
||||
boolean mAbortOnInflation;
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
||||
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
|
||||
@@ -329,6 +330,11 @@ public class NotificationTestHelper {
|
||||
entry.setRow(row);
|
||||
entry.createIcons(mContext, entry.getSbn());
|
||||
row.setEntry(entry);
|
||||
NotificationContentInflater contentBinder = new NotificationContentInflater(
|
||||
mock(NotifRemoteViewCache.class),
|
||||
mock(NotificationRemoteInputManager.class));
|
||||
contentBinder.setInflateSynchronously(true);
|
||||
row.setContentBinder(contentBinder);
|
||||
row.setInflationFlags(extraInflationFlags);
|
||||
inflateAndWait(row);
|
||||
|
||||
@@ -341,7 +347,6 @@ public class NotificationTestHelper {
|
||||
|
||||
private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
row.getNotificationInflater().setInflateSynchronously(true);
|
||||
NotificationContentInflater.InflationCallback callback =
|
||||
new NotificationContentInflater.InflationCallback() {
|
||||
@Override
|
||||
|
||||
@@ -84,7 +84,10 @@ import com.android.systemui.statusbar.notification.logging.NotifLog;
|
||||
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
|
||||
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||
@@ -200,9 +203,15 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
|
||||
mEntry.expandedIcon = mock(StatusBarIconView.class);
|
||||
|
||||
NotificationRowContentBinder contentBinder = new NotificationContentInflater(
|
||||
mock(NotifRemoteViewCache.class),
|
||||
mRemoteInputManager);
|
||||
|
||||
NotificationRowBinderImpl notificationRowBinder =
|
||||
new NotificationRowBinderImpl(mContext,
|
||||
mRemoteInputManager, mLockscreenUserManager,
|
||||
mRemoteInputManager,
|
||||
mLockscreenUserManager,
|
||||
contentBinder,
|
||||
true, /* allowLongPress */
|
||||
mock(KeyguardBypassController.class),
|
||||
mock(StatusBarStateController.class),
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.row;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class NotifRemoteViewCacheImplTest extends SysuiTestCase {
|
||||
|
||||
private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
|
||||
private NotificationEntry mEntry;
|
||||
private NotificationEntryListener mEntryListener;
|
||||
@Mock private RemoteViews mRemoteViews;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mEntry = new NotificationEntryBuilder().build();
|
||||
|
||||
NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
|
||||
mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager);
|
||||
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
|
||||
ArgumentCaptor.forClass(NotificationEntryListener.class);
|
||||
verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
|
||||
mEntryListener = entryListenerCaptor.getValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutCachedView() {
|
||||
// GIVEN an initialized cache for an entry.
|
||||
mEntryListener.onPendingEntryAdded(mEntry);
|
||||
|
||||
// WHEN a notification's cached remote views is put in.
|
||||
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
|
||||
|
||||
// THEN the remote view is cached.
|
||||
assertTrue(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
assertEquals(
|
||||
"Cached remote view is not the one we put in.",
|
||||
mRemoteViews,
|
||||
mNotifRemoteViewCache.getCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveCachedView() {
|
||||
// GIVEN a cache with a cached view.
|
||||
mEntryListener.onPendingEntryAdded(mEntry);
|
||||
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
|
||||
|
||||
// WHEN we remove the cached view.
|
||||
mNotifRemoteViewCache.removeCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
|
||||
// THEN the remote view is not cached.
|
||||
assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearCache() {
|
||||
// GIVEN a non-empty cache.
|
||||
mEntryListener.onPendingEntryAdded(mEntry);
|
||||
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
|
||||
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews);
|
||||
|
||||
// WHEN we clear the cache.
|
||||
mNotifRemoteViewCache.clearCache(mEntry);
|
||||
|
||||
// THEN the cache is empty.
|
||||
assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
|
||||
assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED));
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,16 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
@@ -35,16 +40,17 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.filters.Suppress;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.InflationTask;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.NotificationTestHelper;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
|
||||
@@ -57,6 +63,8 @@ import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -73,8 +81,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
|
||||
private Notification.Builder mBuilder;
|
||||
private ExpandableNotificationRow mRow;
|
||||
|
||||
@Mock private NotifRemoteViewCache mCache;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mBuilder = new Notification.Builder(mContext).setSmallIcon(
|
||||
R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
@@ -83,7 +94,9 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
|
||||
ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
|
||||
mBuilder.build());
|
||||
mRow = spy(row);
|
||||
mNotificationInflater = new NotificationContentInflater();
|
||||
mNotificationInflater = new NotificationContentInflater(
|
||||
mCache,
|
||||
mock(NotificationRemoteInputManager.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,7 +187,9 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
|
||||
result,
|
||||
FLAG_CONTENT_VIEW_EXPANDED,
|
||||
0,
|
||||
new ArrayMap() /* cachedContentViews */, mRow,
|
||||
mock(NotifRemoteViewCache.class),
|
||||
mRow.getEntry(),
|
||||
mRow,
|
||||
true /* isNewView */, (v, p, r) -> true,
|
||||
new InflationCallback() {
|
||||
@Override
|
||||
@@ -244,6 +259,71 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
|
||||
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception {
|
||||
// GIVEN a cached view.
|
||||
RemoteViews contractedRemoteView = mBuilder.createContentView();
|
||||
when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
|
||||
.thenReturn(true);
|
||||
when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
|
||||
.thenReturn(contractedRemoteView);
|
||||
|
||||
// GIVEN existing bound view with same layout id.
|
||||
View view = contractedRemoteView.apply(mContext, null /* parent */);
|
||||
mRow.getPrivateLayout().setContractedChild(view);
|
||||
|
||||
// WHEN inflater inflates
|
||||
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
|
||||
|
||||
// THEN the view should be re-used
|
||||
assertEquals("Binder inflated a new view even though the old one was cached and usable.",
|
||||
view, mRow.getPrivateLayout().getContractedChild());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInflatesNewViewWhenCachedNotPossibleToReuse() throws Exception {
|
||||
// GIVEN a cached remote view.
|
||||
RemoteViews contractedRemoteView = mBuilder.createHeadsUpContentView();
|
||||
when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
|
||||
.thenReturn(true);
|
||||
when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
|
||||
.thenReturn(contractedRemoteView);
|
||||
|
||||
// GIVEN existing bound view with different layout id.
|
||||
View view = new TextView(mContext);
|
||||
mRow.getPrivateLayout().setContractedChild(view);
|
||||
|
||||
// WHEN inflater inflates
|
||||
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
|
||||
|
||||
// THEN the view should be a new view
|
||||
assertNotEquals("Binder (somehow) used the same view when inflating.",
|
||||
view, mRow.getPrivateLayout().getContractedChild());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInflationCachesCreatedRemoteView() throws Exception {
|
||||
// WHEN inflater inflates
|
||||
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
|
||||
|
||||
// THEN inflater informs cache of the new remote view
|
||||
verify(mCache).putCachedView(
|
||||
eq(mRow.getEntry()),
|
||||
eq(FLAG_CONTENT_VIEW_CONTRACTED),
|
||||
any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnbindRemovesCachedRemoteView() {
|
||||
// WHEN inflated unbinds content
|
||||
mNotificationInflater.unbindContent(mRow.getEntry(), mRow, FLAG_CONTENT_VIEW_HEADS_UP);
|
||||
|
||||
// THEN inflated informs cache to remove remote view
|
||||
verify(mCache).removeCachedView(
|
||||
eq(mRow.getEntry()),
|
||||
eq(FLAG_CONTENT_VIEW_HEADS_UP));
|
||||
}
|
||||
|
||||
private static void inflateAndWait(NotificationContentInflater inflater,
|
||||
@InflationFlag int contentToInflate,
|
||||
ExpandableNotificationRow row)
|
||||
|
||||
Reference in New Issue
Block a user