blob: 48a82957bf1ef61c33ba86da164858bb56151078 [file] [log] [blame]
Selim Cinek1a48bab2017-02-17 19:38:40 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Selim Cinek1a48bab2017-02-17 19:38:40 -080018
Kevin38ce6fa2018-10-17 16:00:14 -070019import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
Kevind5022f92018-10-08 18:30:26 -070020import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
21
Kevind4660b22018-09-27 10:57:35 -070022import android.annotation.IntDef;
Selim Cinek01d3da62017-04-28 15:03:48 -070023import android.annotation.Nullable;
Selim Cinek1a48bab2017-02-17 19:38:40 -080024import android.app.Notification;
25import android.content.Context;
Selim Cinek2630dc72017-04-20 15:16:10 -070026import android.os.AsyncTask;
Selim Cinek01d3da62017-04-28 15:03:48 -070027import android.os.CancellationSignal;
Selim Cinek1a48bab2017-02-17 19:38:40 -080028import android.service.notification.StatusBarNotification;
Kevind4660b22018-09-27 10:57:35 -070029import android.util.ArrayMap;
Selim Cinek1a48bab2017-02-17 19:38:40 -080030import android.util.Log;
31import android.view.View;
32import android.widget.RemoteViews;
33
Selim Cinek10790672017-03-08 16:33:05 -080034import com.android.internal.annotations.VisibleForTesting;
Ahan Wude396fa2018-05-08 20:42:24 +080035import com.android.internal.widget.ImageMessageConsumer;
Gustav Sennton5759f872019-02-13 17:25:26 +000036import com.android.systemui.Dependency;
Tony Mak628cb932018-06-19 18:30:41 +010037import com.android.systemui.statusbar.InflationTask;
Gustav Sennton5759f872019-02-13 17:25:26 +000038import com.android.systemui.statusbar.SmartReplyController;
Rohan Shah20790b82018-07-02 17:21:04 -070039import com.android.systemui.statusbar.notification.InflationException;
40import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
Ned Burnsf81c4c42019-01-07 14:10:43 -050041import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Kevin38ce6fa2018-10-17 16:00:14 -070042import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
Selim Cinek1a48bab2017-02-17 19:38:40 -080043import com.android.systemui.statusbar.phone.StatusBar;
Gustav Sennton5759f872019-02-13 17:25:26 +000044import com.android.systemui.statusbar.policy.HeadsUpManager;
45import com.android.systemui.statusbar.policy.InflatedSmartReplies;
Gustav Sennton8a52dc32019-04-15 12:48:23 +010046import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
Gustav Sennton5759f872019-02-13 17:25:26 +000047import com.android.systemui.statusbar.policy.SmartReplyConstants;
Selim Cinek01d3da62017-04-28 15:03:48 -070048import com.android.systemui.util.Assert;
49
Kevind4660b22018-09-27 10:57:35 -070050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
Selim Cinek01d3da62017-04-28 15:03:48 -070052import java.util.HashMap;
Selim Cinek1a48bab2017-02-17 19:38:40 -080053
Selim Cinek1a48bab2017-02-17 19:38:40 -080054/**
55 * A utility that inflates the right kind of contentView based on the state
56 */
Ned Burns1a5e22f2019-02-14 15:11:52 -050057public class NotificationContentInflater {
Selim Cinek1a48bab2017-02-17 19:38:40 -080058
Ned Burns1a5e22f2019-02-14 15:11:52 -050059 public static final String TAG = "NotifContentInflater";
Kevind4660b22018-09-27 10:57:35 -070060
61 @Retention(RetentionPolicy.SOURCE)
62 @IntDef(flag = true,
Kevind5022f92018-10-08 18:30:26 -070063 prefix = {"FLAG_CONTENT_VIEW_"},
Kevind4660b22018-09-27 10:57:35 -070064 value = {
Kevind5022f92018-10-08 18:30:26 -070065 FLAG_CONTENT_VIEW_CONTRACTED,
66 FLAG_CONTENT_VIEW_EXPANDED,
67 FLAG_CONTENT_VIEW_HEADS_UP,
Kevind5022f92018-10-08 18:30:26 -070068 FLAG_CONTENT_VIEW_PUBLIC,
69 FLAG_CONTENT_VIEW_ALL})
Kevind4660b22018-09-27 10:57:35 -070070 public @interface InflationFlag {}
71 /**
72 * The default, contracted view. Seen when the shade is pulled down and in the lock screen
73 * if there is no worry about content sensitivity.
74 */
Kevind5022f92018-10-08 18:30:26 -070075 public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1;
Kevind4660b22018-09-27 10:57:35 -070076
77 /**
78 * The expanded view. Seen when the user expands a notification.
79 */
Kevind5022f92018-10-08 18:30:26 -070080 public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
Kevind4660b22018-09-27 10:57:35 -070081
82 /**
83 * The heads up view. Seen when a high priority notification peeks in from the top.
84 */
Kevind5022f92018-10-08 18:30:26 -070085 public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
Kevind4660b22018-09-27 10:57:35 -070086
87 /**
Kevind4660b22018-09-27 10:57:35 -070088 * The public view. This is a version of the contracted view that hides sensitive
89 * information and is used on the lock screen if we determine that the notification's
90 * content should be hidden.
91 */
Selim Cinekc3fec682019-06-06 18:11:07 -070092 public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 3;
Kevind4660b22018-09-27 10:57:35 -070093
Kevind5022f92018-10-08 18:30:26 -070094 public static final int FLAG_CONTENT_VIEW_ALL = ~0;
Kevind4660b22018-09-27 10:57:35 -070095
96 /**
97 * Content views that must be inflated at all times.
98 */
99 @InflationFlag
100 private static final int REQUIRED_INFLATION_FLAGS =
Kevind5022f92018-10-08 18:30:26 -0700101 FLAG_CONTENT_VIEW_CONTRACTED
Kevin01a53cb2018-11-09 18:19:54 -0800102 | FLAG_CONTENT_VIEW_EXPANDED;
Kevind4660b22018-09-27 10:57:35 -0700103
104 /**
105 * The set of content views to inflate.
106 */
107 @InflationFlag
108 private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
109
Selim Cinek1a48bab2017-02-17 19:38:40 -0800110 private final ExpandableNotificationRow mRow;
111 private boolean mIsLowPriority;
112 private boolean mUsesIncreasedHeight;
113 private boolean mUsesIncreasedHeadsUpHeight;
114 private RemoteViews.OnClickHandler mRemoteViewClickHandler;
Selim Cinekc478f902017-02-22 20:55:44 -0800115 private boolean mIsChildInGroup;
Selim Cinek2630dc72017-04-20 15:16:10 -0700116 private InflationCallback mCallback;
Ned Burns342c3a02019-02-15 18:08:39 -0500117 private boolean mInflateSynchronously = false;
Kevind4660b22018-09-27 10:57:35 -0700118 private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
Selim Cinek1a48bab2017-02-17 19:38:40 -0800119
Ned Burns1a5e22f2019-02-14 15:11:52 -0500120 public NotificationContentInflater(ExpandableNotificationRow row) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800121 mRow = row;
122 }
123
124 public void setIsLowPriority(boolean isLowPriority) {
125 mIsLowPriority = isLowPriority;
126 }
127
Selim Cinekc478f902017-02-22 20:55:44 -0800128 /**
129 * Set whether the notification is a child in a group
130 *
131 * @return whether the view was re-inflated
132 */
Selim Cinek2630dc72017-04-20 15:16:10 -0700133 public void setIsChildInGroup(boolean childInGroup) {
Selim Cinekc478f902017-02-22 20:55:44 -0800134 if (childInGroup != mIsChildInGroup) {
135 mIsChildInGroup = childInGroup;
136 if (mIsLowPriority) {
Kevind5022f92018-10-08 18:30:26 -0700137 int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
Selim Cinek2630dc72017-04-20 15:16:10 -0700138 inflateNotificationViews(flags);
Selim Cinekc478f902017-02-22 20:55:44 -0800139 }
Kevind4660b22018-09-27 10:57:35 -0700140 }
Selim Cinekc478f902017-02-22 20:55:44 -0800141 }
142
Selim Cinek1a48bab2017-02-17 19:38:40 -0800143 public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
144 mUsesIncreasedHeight = usesIncreasedHeight;
145 }
146
147 public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
148 mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
149 }
150
151 public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
152 mRemoteViewClickHandler = remoteViewClickHandler;
153 }
154
Kevin38ce6fa2018-10-17 16:00:14 -0700155 /**
156 * Update whether or not the notification is redacted on the lock screen. If the notification
Selim Cinekc3fec682019-06-06 18:11:07 -0700157 * is now redacted, we should inflate the public contracted view to now show on the lock screen.
Kevin38ce6fa2018-10-17 16:00:14 -0700158 *
159 * @param needsRedaction true if the notification should now be redacted on the lock screen
160 */
161 public void updateNeedsRedaction(boolean needsRedaction) {
Kevin38ce6fa2018-10-17 16:00:14 -0700162 if (mRow.getEntry() == null) {
163 return;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700164 }
Kevin38ce6fa2018-10-17 16:00:14 -0700165 if (needsRedaction) {
Selim Cinekc3fec682019-06-06 18:11:07 -0700166 int flags = FLAG_CONTENT_VIEW_PUBLIC;
167 inflateNotificationViews(flags);
Kevin38ce6fa2018-10-17 16:00:14 -0700168 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700169 }
170
Selim Cinek2630dc72017-04-20 15:16:10 -0700171 /**
Kevind4660b22018-09-27 10:57:35 -0700172 * Set whether or not a particular content view is needed and whether or not it should be
173 * inflated. These flags will be used when we inflate or reinflate.
174 *
175 * @param flag the {@link InflationFlag} corresponding to the view that should/should not be
176 * inflated
177 * @param shouldInflate true if the view should be inflated, false otherwise
178 */
179 public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
180 if (shouldInflate) {
181 mInflationFlags |= flag;
182 } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) {
183 mInflationFlags &= ~flag;
184 }
185 }
186
187 /**
Kevin01a53cb2018-11-09 18:19:54 -0800188 * Convenience method for setting multiple flags at once.
Kevind4660b22018-09-27 10:57:35 -0700189 *
190 * @param flags a set of {@link InflationFlag} corresponding to content views that should be
191 * inflated
192 */
Kevin01a53cb2018-11-09 18:19:54 -0800193 @VisibleForTesting
Kevind4660b22018-09-27 10:57:35 -0700194 public void addInflationFlags(@InflationFlag int flags) {
195 mInflationFlags |= flags;
196 }
197
198 /**
Kevin38ce6fa2018-10-17 16:00:14 -0700199 * Whether or not the view corresponding to the flag is set to be inflated currently.
200 *
201 * @param flag the {@link InflationFlag} corresponding to the view
202 * @return true if the flag is set and view will be inflated, false o/w
203 */
Kevin38ce6fa2018-10-17 16:00:14 -0700204 public boolean isInflationFlagSet(@InflationFlag int flag) {
205 return ((mInflationFlags & flag) != 0);
206 }
207
208 /**
Kevin01a53cb2018-11-09 18:19:54 -0800209 * Inflate views for set flags on a background thread. This is asynchronous and will
Selim Cinek2630dc72017-04-20 15:16:10 -0700210 * notify the callback once it's finished.
211 */
212 public void inflateNotificationViews() {
Kevind4660b22018-09-27 10:57:35 -0700213 inflateNotificationViews(mInflationFlags);
Selim Cinekc478f902017-02-22 20:55:44 -0800214 }
215
216 /**
Kevind4660b22018-09-27 10:57:35 -0700217 * Inflate all views for the specified flags on a background thread. This is asynchronous and
218 * will notify the callback once it's finished. If the content view is already inflated, this
219 * will reinflate it.
Selim Cinek2630dc72017-04-20 15:16:10 -0700220 *
Kevin01a53cb2018-11-09 18:19:54 -0800221 * @param reInflateFlags flags which views should be inflated. Should be a subset of
Ned Burns1a5e22f2019-02-14 15:11:52 -0500222 * {@link #mInflationFlags} as only those will be inflated/reinflated.
Selim Cinekc478f902017-02-22 20:55:44 -0800223 */
Kevind4660b22018-09-27 10:57:35 -0700224 private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
Selim Cinek67ff2482017-05-25 10:27:28 -0700225 if (mRow.isRemoved()) {
226 // We don't want to reinflate anything for removed notifications. Otherwise views might
227 // be readded to the stack, leading to leaks. This may happen with low-priority groups
228 // where the removal of already removed children can lead to a reinflation.
229 return;
230 }
Kevind4660b22018-09-27 10:57:35 -0700231 // Only inflate the ones that are set.
Kevin38ce6fa2018-10-17 16:00:14 -0700232 reInflateFlags &= mInflationFlags;
Selim Cinek10790672017-03-08 16:33:05 -0800233 StatusBarNotification sbn = mRow.getEntry().notification;
Ahan Wude396fa2018-05-08 20:42:24 +0800234
235 // To check if the notification has inline image and preload inline image if necessary.
236 mRow.getImageResolver().preloadImages(sbn.getNotification());
237
Ned Burns342c3a02019-02-15 18:08:39 -0500238 AsyncInflationTask task = new AsyncInflationTask(
239 sbn,
240 mInflateSynchronously,
241 reInflateFlags,
242 mCachedContentViews,
243 mRow,
244 mIsLowPriority,
245 mIsChildInGroup,
246 mUsesIncreasedHeight,
247 mUsesIncreasedHeadsUpHeight,
Ned Burns342c3a02019-02-15 18:08:39 -0500248 mCallback,
249 mRemoteViewClickHandler);
250 if (mInflateSynchronously) {
Jason Monk6dceace2018-05-15 20:24:07 -0400251 task.onPostExecute(task.doInBackground());
252 } else {
253 task.execute();
254 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800255 }
256
Selim Cinek10790672017-03-08 16:33:05 -0800257 @VisibleForTesting
Ned Burns342c3a02019-02-15 18:08:39 -0500258 InflationProgress inflateNotificationViews(
259 boolean inflateSynchronously,
260 @InflationFlag int reInflateFlags,
261 Notification.Builder builder,
262 Context packageContext) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700263 InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
264 mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
Selim Cinekc3fec682019-06-06 18:11:07 -0700265 packageContext);
Gustav Sennton5759f872019-02-13 17:25:26 +0000266 result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(),
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100267 mRow.getContext(), mRow.getHeadsUpManager(),
268 mRow.getExistingSmartRepliesAndActions());
Ned Burns342c3a02019-02-15 18:08:39 -0500269 apply(
270 inflateSynchronously,
271 result,
272 reInflateFlags,
273 mCachedContentViews,
274 mRow,
Ned Burns342c3a02019-02-15 18:08:39 -0500275 mRemoteViewClickHandler,
276 null);
Selim Cinek01d3da62017-04-28 15:03:48 -0700277 return result;
278 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700279
Kevind4660b22018-09-27 10:57:35 -0700280 /**
Kevind5022f92018-10-08 18:30:26 -0700281 * Frees the content view associated with the inflation flag. Will only succeed if the
282 * view is safe to remove.
Kevind4660b22018-09-27 10:57:35 -0700283 *
284 * @param inflateFlag the flag corresponding to the content view which should be freed
285 */
286 public void freeNotificationView(@InflationFlag int inflateFlag) {
Kevind5022f92018-10-08 18:30:26 -0700287 if ((mInflationFlags & inflateFlag) != 0) {
288 // The view should still be inflated.
289 return;
290 }
Kevind4660b22018-09-27 10:57:35 -0700291 switch (inflateFlag) {
Kevind5022f92018-10-08 18:30:26 -0700292 case FLAG_CONTENT_VIEW_HEADS_UP:
293 if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
294 mRow.getPrivateLayout().setHeadsUpChild(null);
295 mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
Gustav Sennton5759f872019-02-13 17:25:26 +0000296 mRow.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
Kevind5022f92018-10-08 18:30:26 -0700297 }
Kevind4660b22018-09-27 10:57:35 -0700298 break;
Kevin38ce6fa2018-10-17 16:00:14 -0700299 case FLAG_CONTENT_VIEW_PUBLIC:
300 if (mRow.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
301 mRow.getPublicLayout().setContractedChild(null);
302 mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
303 }
304 break;
Kevind5022f92018-10-08 18:30:26 -0700305 case FLAG_CONTENT_VIEW_CONTRACTED:
306 case FLAG_CONTENT_VIEW_EXPANDED:
Kevind4660b22018-09-27 10:57:35 -0700307 default:
308 break;
309 }
310 }
311
Gustav Sennton5759f872019-02-13 17:25:26 +0000312 private static InflationProgress inflateSmartReplyViews(InflationProgress result,
313 @InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100314 HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000315 SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
316 SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
317 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
318 result.expandedInflatedSmartReplies =
319 InflatedSmartReplies.inflate(
320 context, entry, smartReplyConstants, smartReplyController,
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100321 headsUpManager, previousSmartRepliesAndActions);
Gustav Sennton5759f872019-02-13 17:25:26 +0000322 }
323 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
324 result.headsUpInflatedSmartReplies =
325 InflatedSmartReplies.inflate(
326 context, entry, smartReplyConstants, smartReplyController,
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100327 headsUpManager, previousSmartRepliesAndActions);
Gustav Sennton5759f872019-02-13 17:25:26 +0000328 }
329 return result;
330 }
331
Kevind4660b22018-09-27 10:57:35 -0700332 private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Selim Cinek01d3da62017-04-28 15:03:48 -0700333 Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
Selim Cinekc3fec682019-06-06 18:11:07 -0700334 boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight,
Selim Cinek01d3da62017-04-28 15:03:48 -0700335 Context packageContext) {
336 InflationProgress result = new InflationProgress();
337 isLowPriority = isLowPriority && !isChildInGroup;
Gustav Sennton5759f872019-02-13 17:25:26 +0000338
Kevind5022f92018-10-08 18:30:26 -0700339 if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700340 result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
Selim Cinek10790672017-03-08 16:33:05 -0800341 }
342
Kevind5022f92018-10-08 18:30:26 -0700343 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700344 result.newExpandedView = createExpandedView(builder, isLowPriority);
Selim Cinek10790672017-03-08 16:33:05 -0800345 }
346
Kevind5022f92018-10-08 18:30:26 -0700347 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700348 result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
Selim Cinek10790672017-03-08 16:33:05 -0800349 }
350
Kevind5022f92018-10-08 18:30:26 -0700351 if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
Selim Cinekabcc2012019-07-23 18:44:07 -0700352 result.newPublicView = builder.makePublicContentView(isLowPriority);
Selim Cinek10790672017-03-08 16:33:05 -0800353 }
354
Selim Cinek01d3da62017-04-28 15:03:48 -0700355 result.packageContext = packageContext;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800356 result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
357 result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
358 true /* showingPublic */);
Selim Cinek01d3da62017-04-28 15:03:48 -0700359 return result;
360 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700361
Ned Burns342c3a02019-02-15 18:08:39 -0500362 public static CancellationSignal apply(
363 boolean inflateSynchronously,
364 InflationProgress result,
365 @InflationFlag int reInflateFlags,
366 ArrayMap<Integer, RemoteViews> cachedContentViews,
367 ExpandableNotificationRow row,
Selim Cinek01d3da62017-04-28 15:03:48 -0700368 RemoteViews.OnClickHandler remoteViewClickHandler,
369 @Nullable InflationCallback callback) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700370 NotificationContentView privateLayout = row.getPrivateLayout();
371 NotificationContentView publicLayout = row.getPublicLayout();
372 final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
373
Kevind5022f92018-10-08 18:30:26 -0700374 int flag = FLAG_CONTENT_VIEW_CONTRACTED;
Selim Cinek01d3da62017-04-28 15:03:48 -0700375 if ((reInflateFlags & flag) != 0) {
Kevind4660b22018-09-27 10:57:35 -0700376 boolean isNewView =
377 !canReapplyRemoteView(result.newContentView,
Kevind5022f92018-10-08 18:30:26 -0700378 cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
Selim Cinek01d3da62017-04-28 15:03:48 -0700379 ApplyCallback applyCallback = new ApplyCallback() {
380 @Override
381 public void setResultView(View v) {
382 result.inflatedContentView = v;
383 }
384
385 @Override
386 public RemoteViews getRemoteView() {
387 return result.newContentView;
388 }
389 };
Ned Burns342c3a02019-02-15 18:08:39 -0500390 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
Selim Cinekc3fec682019-06-06 18:11:07 -0700391 row, isNewView, remoteViewClickHandler, callback, privateLayout,
Selim Cinek131f1a42017-06-05 17:50:19 -0700392 privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
393 NotificationContentView.VISIBLE_TYPE_CONTRACTED),
Selim Cinek01d3da62017-04-28 15:03:48 -0700394 runningInflations, applyCallback);
395 }
396
Kevind5022f92018-10-08 18:30:26 -0700397 flag = FLAG_CONTENT_VIEW_EXPANDED;
Selim Cinek01d3da62017-04-28 15:03:48 -0700398 if ((reInflateFlags & flag) != 0) {
399 if (result.newExpandedView != null) {
Kevind4660b22018-09-27 10:57:35 -0700400 boolean isNewView =
401 !canReapplyRemoteView(result.newExpandedView,
Kevind5022f92018-10-08 18:30:26 -0700402 cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
Selim Cinek01d3da62017-04-28 15:03:48 -0700403 ApplyCallback applyCallback = new ApplyCallback() {
404 @Override
405 public void setResultView(View v) {
406 result.inflatedExpandedView = v;
407 }
408
409 @Override
410 public RemoteViews getRemoteView() {
411 return result.newExpandedView;
412 }
413 };
Ned Burns342c3a02019-02-15 18:08:39 -0500414 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
Selim Cinekc3fec682019-06-06 18:11:07 -0700415 cachedContentViews, row, isNewView, remoteViewClickHandler,
Ned Burns342c3a02019-02-15 18:08:39 -0500416 callback, privateLayout, privateLayout.getExpandedChild(),
Selim Cinek131f1a42017-06-05 17:50:19 -0700417 privateLayout.getVisibleWrapper(
418 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
Selim Cinek01d3da62017-04-28 15:03:48 -0700419 applyCallback);
Selim Cinek10790672017-03-08 16:33:05 -0800420 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700421 }
422
Kevind5022f92018-10-08 18:30:26 -0700423 flag = FLAG_CONTENT_VIEW_HEADS_UP;
Selim Cinek01d3da62017-04-28 15:03:48 -0700424 if ((reInflateFlags & flag) != 0) {
425 if (result.newHeadsUpView != null) {
Kevind4660b22018-09-27 10:57:35 -0700426 boolean isNewView =
427 !canReapplyRemoteView(result.newHeadsUpView,
Kevind5022f92018-10-08 18:30:26 -0700428 cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
Selim Cinek01d3da62017-04-28 15:03:48 -0700429 ApplyCallback applyCallback = new ApplyCallback() {
430 @Override
431 public void setResultView(View v) {
432 result.inflatedHeadsUpView = v;
433 }
434
435 @Override
436 public RemoteViews getRemoteView() {
437 return result.newHeadsUpView;
438 }
439 };
Ned Burns342c3a02019-02-15 18:08:39 -0500440 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
Selim Cinekc3fec682019-06-06 18:11:07 -0700441 cachedContentViews, row, isNewView, remoteViewClickHandler,
Ned Burns342c3a02019-02-15 18:08:39 -0500442 callback, privateLayout, privateLayout.getHeadsUpChild(),
Selim Cinek131f1a42017-06-05 17:50:19 -0700443 privateLayout.getVisibleWrapper(
Kevind5022f92018-10-08 18:30:26 -0700444 VISIBLE_TYPE_HEADSUP), runningInflations,
Selim Cinek01d3da62017-04-28 15:03:48 -0700445 applyCallback);
446 }
447 }
448
Kevind5022f92018-10-08 18:30:26 -0700449 flag = FLAG_CONTENT_VIEW_PUBLIC;
Selim Cinek01d3da62017-04-28 15:03:48 -0700450 if ((reInflateFlags & flag) != 0) {
Kevind4660b22018-09-27 10:57:35 -0700451 boolean isNewView =
452 !canReapplyRemoteView(result.newPublicView,
Kevind5022f92018-10-08 18:30:26 -0700453 cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
Selim Cinek01d3da62017-04-28 15:03:48 -0700454 ApplyCallback applyCallback = new ApplyCallback() {
455 @Override
456 public void setResultView(View v) {
457 result.inflatedPublicView = v;
458 }
459
460 @Override
461 public RemoteViews getRemoteView() {
462 return result.newPublicView;
463 }
464 };
Ned Burns342c3a02019-02-15 18:08:39 -0500465 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
Selim Cinekc3fec682019-06-06 18:11:07 -0700466 row, isNewView, remoteViewClickHandler, callback,
Selim Cinek131f1a42017-06-05 17:50:19 -0700467 publicLayout, publicLayout.getContractedChild(),
468 publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
469 runningInflations, applyCallback);
Selim Cinek01d3da62017-04-28 15:03:48 -0700470 }
471
Selim Cinek01d3da62017-04-28 15:03:48 -0700472 // Let's try to finish, maybe nobody is even inflating anything
Selim Cinekc3fec682019-06-06 18:11:07 -0700473 finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
Selim Cinek01d3da62017-04-28 15:03:48 -0700474 CancellationSignal cancellationSignal = new CancellationSignal();
475 cancellationSignal.setOnCancelListener(
476 () -> runningInflations.values().forEach(CancellationSignal::cancel));
477 return cancellationSignal;
478 }
479
Selim Cinekd246bed2017-06-19 16:58:35 -0700480 @VisibleForTesting
Ned Burns342c3a02019-02-15 18:08:39 -0500481 static void applyRemoteView(
482 boolean inflateSynchronously,
483 final InflationProgress result,
484 final @InflationFlag int reInflateFlags,
485 @InflationFlag int inflationId,
Kevind4660b22018-09-27 10:57:35 -0700486 final ArrayMap<Integer, RemoteViews> cachedContentViews,
Ned Burns342c3a02019-02-15 18:08:39 -0500487 final ExpandableNotificationRow row,
Ned Burns342c3a02019-02-15 18:08:39 -0500488 boolean isNewView,
Selim Cinek01d3da62017-04-28 15:03:48 -0700489 RemoteViews.OnClickHandler remoteViewClickHandler,
Kevind4660b22018-09-27 10:57:35 -0700490 @Nullable final InflationCallback callback,
Ned Burns342c3a02019-02-15 18:08:39 -0500491 NotificationContentView parentLayout,
492 View existingView,
Selim Cinek131f1a42017-06-05 17:50:19 -0700493 NotificationViewWrapper existingWrapper,
Selim Cinek01d3da62017-04-28 15:03:48 -0700494 final HashMap<Integer, CancellationSignal> runningInflations,
495 ApplyCallback applyCallback) {
Selim Cinekd246bed2017-06-19 16:58:35 -0700496 RemoteViews newContentView = applyCallback.getRemoteView();
Ned Burns342c3a02019-02-15 18:08:39 -0500497 if (inflateSynchronously) {
Jason Monk6dceace2018-05-15 20:24:07 -0400498 try {
499 if (isNewView) {
500 View v = newContentView.apply(
501 result.packageContext,
502 parentLayout,
503 remoteViewClickHandler);
504 v.setIsRootNamespace(true);
505 applyCallback.setResultView(v);
506 } else {
507 newContentView.reapply(
508 result.packageContext,
509 existingView,
510 remoteViewClickHandler);
511 existingWrapper.onReinflated();
512 }
513 } catch (Exception e) {
Kevind4660b22018-09-27 10:57:35 -0700514 handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
Jason Monk6dceace2018-05-15 20:24:07 -0400515 // Add a running inflation to make sure we don't trigger callbacks.
516 // Safe to do because only happens in tests.
517 runningInflations.put(inflationId, new CancellationSignal());
518 }
519 return;
520 }
Ahan Wude396fa2018-05-08 20:42:24 +0800521 RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
522
523 @Override
524 public void onViewInflated(View v) {
525 if (v instanceof ImageMessageConsumer) {
526 ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver());
527 }
528 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700529
530 @Override
531 public void onViewApplied(View v) {
532 if (isNewView) {
533 v.setIsRootNamespace(true);
534 applyCallback.setResultView(v);
Selim Cinek131f1a42017-06-05 17:50:19 -0700535 } else if (existingWrapper != null) {
536 existingWrapper.onReinflated();
Selim Cinek01d3da62017-04-28 15:03:48 -0700537 }
538 runningInflations.remove(inflationId);
Kevind4660b22018-09-27 10:57:35 -0700539 finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
Selim Cinekc3fec682019-06-06 18:11:07 -0700540 callback, row);
Selim Cinek01d3da62017-04-28 15:03:48 -0700541 }
542
543 @Override
544 public void onError(Exception e) {
Selim Cinekd246bed2017-06-19 16:58:35 -0700545 // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
546 // actually also be a system issue, so let's try on the UI thread again to be safe.
547 try {
548 View newView = existingView;
549 if (isNewView) {
550 newView = newContentView.apply(
551 result.packageContext,
552 parentLayout,
553 remoteViewClickHandler);
554 } else {
555 newContentView.reapply(
556 result.packageContext,
557 existingView,
558 remoteViewClickHandler);
559 }
560 Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
561 e);
562 onViewApplied(newView);
563 } catch (Exception anotherException) {
564 runningInflations.remove(inflationId);
Kevind4660b22018-09-27 10:57:35 -0700565 handleInflationError(runningInflations, e, row.getStatusBarNotification(),
566 callback);
Selim Cinekd246bed2017-06-19 16:58:35 -0700567 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700568 }
569 };
570 CancellationSignal cancellationSignal;
Selim Cinek01d3da62017-04-28 15:03:48 -0700571 if (isNewView) {
572 cancellationSignal = newContentView.applyAsync(
573 result.packageContext,
574 parentLayout,
Selim Cinek9c7b3072019-04-12 19:03:41 -0700575 null,
Selim Cinek01d3da62017-04-28 15:03:48 -0700576 listener,
577 remoteViewClickHandler);
578 } else {
579 cancellationSignal = newContentView.reapplyAsync(
580 result.packageContext,
581 existingView,
Selim Cinek9c7b3072019-04-12 19:03:41 -0700582 null,
Selim Cinek01d3da62017-04-28 15:03:48 -0700583 listener,
584 remoteViewClickHandler);
585 }
586 runningInflations.put(inflationId, cancellationSignal);
587 }
588
Kevind4660b22018-09-27 10:57:35 -0700589 private static void handleInflationError(
590 HashMap<Integer, CancellationSignal> runningInflations, Exception e,
591 StatusBarNotification notification, @Nullable InflationCallback callback) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700592 Assert.isMainThread();
593 runningInflations.values().forEach(CancellationSignal::cancel);
594 if (callback != null) {
595 callback.handleInflationException(notification, e);
Selim Cinek10790672017-03-08 16:33:05 -0800596 }
597 }
598
Selim Cinek01d3da62017-04-28 15:03:48 -0700599 /**
600 * Finish the inflation of the views
601 *
602 * @return true if the inflation was finished
603 */
Kevind4660b22018-09-27 10:57:35 -0700604 private static boolean finishIfDone(InflationProgress result,
605 @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
Selim Cinek01d3da62017-04-28 15:03:48 -0700606 HashMap<Integer, CancellationSignal> runningInflations,
Selim Cinekc3fec682019-06-06 18:11:07 -0700607 @Nullable InflationCallback endListener, ExpandableNotificationRow row) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700608 Assert.isMainThread();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500609 NotificationEntry entry = row.getEntry();
Selim Cinek01d3da62017-04-28 15:03:48 -0700610 NotificationContentView privateLayout = row.getPrivateLayout();
611 NotificationContentView publicLayout = row.getPublicLayout();
612 if (runningInflations.isEmpty()) {
Kevind5022f92018-10-08 18:30:26 -0700613 if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700614 if (result.inflatedContentView != null) {
Kevin217ae4e2019-03-07 15:49:17 -0800615 // New view case
Selim Cinek01d3da62017-04-28 15:03:48 -0700616 privateLayout.setContractedChild(result.inflatedContentView);
Kevin217ae4e2019-03-07 15:49:17 -0800617 cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
618 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
619 // Reinflation case. Only update if it's still cached (i.e. view has not been
620 // freed while inflating).
621 cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700622 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700623 }
624
Kevind5022f92018-10-08 18:30:26 -0700625 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700626 if (result.inflatedExpandedView != null) {
627 privateLayout.setExpandedChild(result.inflatedExpandedView);
Kevin217ae4e2019-03-07 15:49:17 -0800628 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700629 } else if (result.newExpandedView == null) {
630 privateLayout.setExpandedChild(null);
Kevin217ae4e2019-03-07 15:49:17 -0800631 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
632 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
633 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700634 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000635 if (result.newExpandedView != null) {
636 privateLayout.setExpandedInflatedSmartReplies(
637 result.expandedInflatedSmartReplies);
638 } else {
639 privateLayout.setExpandedInflatedSmartReplies(null);
640 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700641 row.setExpandable(result.newExpandedView != null);
642 }
643
Kevind5022f92018-10-08 18:30:26 -0700644 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700645 if (result.inflatedHeadsUpView != null) {
646 privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
Kevin217ae4e2019-03-07 15:49:17 -0800647 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700648 } else if (result.newHeadsUpView == null) {
649 privateLayout.setHeadsUpChild(null);
Kevin217ae4e2019-03-07 15:49:17 -0800650 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
651 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
652 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700653 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000654 if (result.newHeadsUpView != null) {
655 privateLayout.setHeadsUpInflatedSmartReplies(
656 result.headsUpInflatedSmartReplies);
657 } else {
658 privateLayout.setHeadsUpInflatedSmartReplies(null);
659 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700660 }
661
Kevind5022f92018-10-08 18:30:26 -0700662 if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700663 if (result.inflatedPublicView != null) {
664 publicLayout.setContractedChild(result.inflatedPublicView);
Kevin217ae4e2019-03-07 15:49:17 -0800665 cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
666 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
667 cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700668 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700669 }
670
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800671 entry.headsUpStatusBarText = result.headsUpStatusBarText;
672 entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
Selim Cinek01d3da62017-04-28 15:03:48 -0700673 if (endListener != null) {
Kevind4660b22018-09-27 10:57:35 -0700674 endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
Selim Cinek01d3da62017-04-28 15:03:48 -0700675 }
676 return true;
677 }
678 return false;
679 }
680
681 private static RemoteViews createExpandedView(Notification.Builder builder,
Selim Cinek1a48bab2017-02-17 19:38:40 -0800682 boolean isLowPriority) {
683 RemoteViews bigContentView = builder.createBigContentView();
684 if (bigContentView != null) {
685 return bigContentView;
686 }
687 if (isLowPriority) {
688 RemoteViews contentView = builder.createContentView();
689 Notification.Builder.makeHeaderExpanded(contentView);
690 return contentView;
691 }
692 return null;
693 }
694
Selim Cinek01d3da62017-04-28 15:03:48 -0700695 private static RemoteViews createContentView(Notification.Builder builder,
Selim Cinek1a48bab2017-02-17 19:38:40 -0800696 boolean isLowPriority, boolean useLarge) {
697 if (isLowPriority) {
698 return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
699 }
700 return builder.createContentView(useLarge);
701 }
702
Selim Cinekfc8073c2017-08-16 17:50:20 -0700703 /**
704 * @param newView The new view that will be applied
705 * @param oldView The old view that was applied to the existing view before
706 * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply.
707 */
708 @VisibleForTesting
709 static boolean canReapplyRemoteView(final RemoteViews newView,
710 final RemoteViews oldView) {
711 return (newView == null && oldView == null) ||
712 (newView != null && oldView != null
713 && oldView.getPackage() != null
714 && newView.getPackage() != null
715 && newView.getPackage().equals(oldView.getPackage())
716 && newView.getLayoutId() == oldView.getLayoutId()
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800717 && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
Selim Cinek1a48bab2017-02-17 19:38:40 -0800718 }
Selim Cinekc478f902017-02-22 20:55:44 -0800719
Selim Cinek2630dc72017-04-20 15:16:10 -0700720 public void setInflationCallback(InflationCallback callback) {
721 mCallback = callback;
Selim Cinekc478f902017-02-22 20:55:44 -0800722 }
723
Selim Cinek2630dc72017-04-20 15:16:10 -0700724 public interface InflationCallback {
Selim Cinek01d3da62017-04-28 15:03:48 -0700725 void handleInflationException(StatusBarNotification notification, Exception e);
Kevind4660b22018-09-27 10:57:35 -0700726
727 /**
728 * Callback for after the content views finish inflating.
729 *
730 * @param entry the entry with the content views set
731 * @param inflatedFlags the flags associated with the content views that were inflated
732 */
Ned Burns342c3a02019-02-15 18:08:39 -0500733 void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags);
Selim Cinekc478f902017-02-22 20:55:44 -0800734 }
Selim Cinek10790672017-03-08 16:33:05 -0800735
Lucas Dupinf03e7522018-06-25 16:21:13 -0700736 public void clearCachesAndReInflate() {
Kevind4660b22018-09-27 10:57:35 -0700737 mCachedContentViews.clear();
Selim Cinek2630dc72017-04-20 15:16:10 -0700738 inflateNotificationViews();
739 }
740
Ned Burns342c3a02019-02-15 18:08:39 -0500741 /**
742 * Sets whether to perform inflation on the same thread as the caller. This method should only
743 * be used in tests, not in production.
744 */
745 @VisibleForTesting
746 void setInflateSynchronously(boolean inflateSynchronously) {
747 mInflateSynchronously = inflateSynchronously;
748 }
749
Selim Cinek01d3da62017-04-28 15:03:48 -0700750 public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
Selim Cinek67ff2482017-05-25 10:27:28 -0700751 implements InflationCallback, InflationTask {
Selim Cinek2630dc72017-04-20 15:16:10 -0700752
753 private final StatusBarNotification mSbn;
754 private final Context mContext;
Ned Burns342c3a02019-02-15 18:08:39 -0500755 private final boolean mInflateSynchronously;
Selim Cinek01d3da62017-04-28 15:03:48 -0700756 private final boolean mIsLowPriority;
757 private final boolean mIsChildInGroup;
758 private final boolean mUsesIncreasedHeight;
759 private final InflationCallback mCallback;
760 private final boolean mUsesIncreasedHeadsUpHeight;
Kevind4660b22018-09-27 10:57:35 -0700761 private @InflationFlag int mReInflateFlags;
762 private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
Selim Cinek01d3da62017-04-28 15:03:48 -0700763 private ExpandableNotificationRow mRow;
Selim Cinek2630dc72017-04-20 15:16:10 -0700764 private Exception mError;
Selim Cinek01d3da62017-04-28 15:03:48 -0700765 private RemoteViews.OnClickHandler mRemoteViewClickHandler;
766 private CancellationSignal mCancellationSignal;
Selim Cinek2630dc72017-04-20 15:16:10 -0700767
Ned Burns342c3a02019-02-15 18:08:39 -0500768 private AsyncInflationTask(
769 StatusBarNotification notification,
770 boolean inflateSynchronously,
Kevind4660b22018-09-27 10:57:35 -0700771 @InflationFlag int reInflateFlags,
Ned Burns342c3a02019-02-15 18:08:39 -0500772 ArrayMap<Integer, RemoteViews> cachedContentViews,
773 ExpandableNotificationRow row,
774 boolean isLowPriority,
775 boolean isChildInGroup,
776 boolean usesIncreasedHeight,
777 boolean usesIncreasedHeadsUpHeight,
Ned Burns342c3a02019-02-15 18:08:39 -0500778 InflationCallback callback,
779 RemoteViews.OnClickHandler remoteViewClickHandler) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700780 mRow = row;
Selim Cinek2630dc72017-04-20 15:16:10 -0700781 mSbn = notification;
Ned Burns342c3a02019-02-15 18:08:39 -0500782 mInflateSynchronously = inflateSynchronously;
Selim Cinek2630dc72017-04-20 15:16:10 -0700783 mReInflateFlags = reInflateFlags;
Kevind4660b22018-09-27 10:57:35 -0700784 mCachedContentViews = cachedContentViews;
Selim Cinek01d3da62017-04-28 15:03:48 -0700785 mContext = mRow.getContext();
786 mIsLowPriority = isLowPriority;
787 mIsChildInGroup = isChildInGroup;
788 mUsesIncreasedHeight = usesIncreasedHeight;
789 mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
Selim Cinek01d3da62017-04-28 15:03:48 -0700790 mRemoteViewClickHandler = remoteViewClickHandler;
791 mCallback = callback;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500792 NotificationEntry entry = row.getEntry();
Selim Cinek67ff2482017-05-25 10:27:28 -0700793 entry.setInflationTask(this);
794 }
795
796 @VisibleForTesting
Kevind4660b22018-09-27 10:57:35 -0700797 @InflationFlag
Selim Cinek67ff2482017-05-25 10:27:28 -0700798 public int getReInflateFlags() {
799 return mReInflateFlags;
Selim Cinek2630dc72017-04-20 15:16:10 -0700800 }
801
802 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700803 protected InflationProgress doInBackground(Void... params) {
Selim Cinek2630dc72017-04-20 15:16:10 -0700804 try {
805 final Notification.Builder recoveredBuilder
806 = Notification.Builder.recoverBuilder(mContext,
807 mSbn.getNotification());
Tony Mak628cb932018-06-19 18:30:41 +0100808
Selim Cinek01d3da62017-04-28 15:03:48 -0700809 Context packageContext = mSbn.getPackageContext(mContext);
Selim Cinek5fb73f82017-04-20 16:55:38 -0700810 Notification notification = mSbn.getNotification();
Selim Cinek0847acd2017-04-24 19:48:29 -0700811 if (notification.isMediaNotification()) {
812 MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
Selim Cinek01d3da62017-04-28 15:03:48 -0700813 packageContext);
Selim Cinek5fb73f82017-04-20 16:55:38 -0700814 processor.processNotification(notification, recoveredBuilder);
815 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000816 InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
Selim Cinekc3fec682019-06-06 18:11:07 -0700817 recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
818 mUsesIncreasedHeadsUpHeight, packageContext);
Gustav Sennton5759f872019-02-13 17:25:26 +0000819 return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100820 mRow.getContext(), mRow.getHeadsUpManager(),
821 mRow.getExistingSmartRepliesAndActions());
Selim Cinek2630dc72017-04-20 15:16:10 -0700822 } catch (Exception e) {
823 mError = e;
824 return null;
825 }
826 }
827
828 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700829 protected void onPostExecute(InflationProgress result) {
Selim Cinek2630dc72017-04-20 15:16:10 -0700830 if (mError == null) {
Ned Burns342c3a02019-02-15 18:08:39 -0500831 mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
Selim Cinekc3fec682019-06-06 18:11:07 -0700832 mCachedContentViews, mRow, mRemoteViewClickHandler, this);
Selim Cinek2630dc72017-04-20 15:16:10 -0700833 } else {
834 handleError(mError);
835 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800836 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800837
Selim Cinek01d3da62017-04-28 15:03:48 -0700838 private void handleError(Exception e) {
839 mRow.getEntry().onInflationTaskFinished();
840 StatusBarNotification sbn = mRow.getStatusBarNotification();
841 final String ident = sbn.getPackageName() + "/0x"
842 + Integer.toHexString(sbn.getId());
843 Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
844 mCallback.handleInflationException(sbn,
845 new InflationException("Couldn't inflate contentViews" + e));
Selim Cinek2630dc72017-04-20 15:16:10 -0700846 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700847
Selim Cinek0f66a4c2017-04-28 19:26:28 -0700848 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700849 public void abort() {
850 cancel(true /* mayInterruptIfRunning */);
851 if (mCancellationSignal != null) {
852 mCancellationSignal.cancel();
853 }
854 }
855
856 @Override
Selim Cinek67ff2482017-05-25 10:27:28 -0700857 public void supersedeTask(InflationTask task) {
858 if (task instanceof AsyncInflationTask) {
859 // We want to inflate all flags of the previous task as well
860 mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags;
861 }
862 }
863
864 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700865 public void handleInflationException(StatusBarNotification notification, Exception e) {
866 handleError(e);
867 }
868
869 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500870 public void onAsyncInflationFinished(NotificationEntry entry,
Kevind4660b22018-09-27 10:57:35 -0700871 @InflationFlag int inflatedFlags) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700872 mRow.getEntry().onInflationTaskFinished();
873 mRow.onNotificationUpdated();
Kevind4660b22018-09-27 10:57:35 -0700874 mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
Ahan Wude396fa2018-05-08 20:42:24 +0800875
876 // Notify the resolver that the inflation task has finished,
877 // try to purge unnecessary cached entries.
878 mRow.getImageResolver().purgeCache();
Selim Cinek01d3da62017-04-28 15:03:48 -0700879 }
Selim Cinek2630dc72017-04-20 15:16:10 -0700880 }
881
Selim Cinekd246bed2017-06-19 16:58:35 -0700882 @VisibleForTesting
883 static class InflationProgress {
Selim Cinek01d3da62017-04-28 15:03:48 -0700884 private RemoteViews newContentView;
885 private RemoteViews newHeadsUpView;
886 private RemoteViews newExpandedView;
Selim Cinek01d3da62017-04-28 15:03:48 -0700887 private RemoteViews newPublicView;
888
Selim Cinekd246bed2017-06-19 16:58:35 -0700889 @VisibleForTesting
890 Context packageContext;
Selim Cinek01d3da62017-04-28 15:03:48 -0700891
892 private View inflatedContentView;
893 private View inflatedHeadsUpView;
894 private View inflatedExpandedView;
Selim Cinek01d3da62017-04-28 15:03:48 -0700895 private View inflatedPublicView;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800896 private CharSequence headsUpStatusBarText;
897 private CharSequence headsUpStatusBarTextPublic;
Gustav Sennton5759f872019-02-13 17:25:26 +0000898
899 private InflatedSmartReplies expandedInflatedSmartReplies;
900 private InflatedSmartReplies headsUpInflatedSmartReplies;
Selim Cinek01d3da62017-04-28 15:03:48 -0700901 }
902
Selim Cinekd246bed2017-06-19 16:58:35 -0700903 @VisibleForTesting
904 abstract static class ApplyCallback {
Selim Cinek01d3da62017-04-28 15:03:48 -0700905 public abstract void setResultView(View v);
906 public abstract RemoteViews getRemoteView();
Selim Cinek2630dc72017-04-20 15:16:10 -0700907 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800908}