blob: a612a1721c41fa78e2caa8c504a38620f08f26d5 [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 Senntondfa968d2019-09-13 12:00:50 +0100267 mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100268 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 Senntondfa968d2019-09-13 12:00:50 +0100314 Context packageContext, HeadsUpManager headsUpManager,
315 SmartRepliesAndActions previousSmartRepliesAndActions) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000316 SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
317 SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
318 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
319 result.expandedInflatedSmartReplies =
320 InflatedSmartReplies.inflate(
Gustav Senntondfa968d2019-09-13 12:00:50 +0100321 context, packageContext, entry, smartReplyConstants,
322 smartReplyController, headsUpManager, previousSmartRepliesAndActions);
Gustav Sennton5759f872019-02-13 17:25:26 +0000323 }
324 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
325 result.headsUpInflatedSmartReplies =
326 InflatedSmartReplies.inflate(
Gustav Senntondfa968d2019-09-13 12:00:50 +0100327 context, packageContext, entry, smartReplyConstants,
328 smartReplyController, headsUpManager, previousSmartRepliesAndActions);
Gustav Sennton5759f872019-02-13 17:25:26 +0000329 }
330 return result;
331 }
332
Kevind4660b22018-09-27 10:57:35 -0700333 private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Selim Cinek01d3da62017-04-28 15:03:48 -0700334 Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
Selim Cinekc3fec682019-06-06 18:11:07 -0700335 boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight,
Selim Cinek01d3da62017-04-28 15:03:48 -0700336 Context packageContext) {
337 InflationProgress result = new InflationProgress();
338 isLowPriority = isLowPriority && !isChildInGroup;
Gustav Sennton5759f872019-02-13 17:25:26 +0000339
Kevind5022f92018-10-08 18:30:26 -0700340 if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700341 result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
Selim Cinek10790672017-03-08 16:33:05 -0800342 }
343
Kevind5022f92018-10-08 18:30:26 -0700344 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700345 result.newExpandedView = createExpandedView(builder, isLowPriority);
Selim Cinek10790672017-03-08 16:33:05 -0800346 }
347
Kevind5022f92018-10-08 18:30:26 -0700348 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700349 result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
Selim Cinek10790672017-03-08 16:33:05 -0800350 }
351
Kevind5022f92018-10-08 18:30:26 -0700352 if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
Selim Cinekabcc2012019-07-23 18:44:07 -0700353 result.newPublicView = builder.makePublicContentView(isLowPriority);
Selim Cinek10790672017-03-08 16:33:05 -0800354 }
355
Selim Cinek01d3da62017-04-28 15:03:48 -0700356 result.packageContext = packageContext;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800357 result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
358 result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
359 true /* showingPublic */);
Selim Cinek01d3da62017-04-28 15:03:48 -0700360 return result;
361 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700362
Ned Burns342c3a02019-02-15 18:08:39 -0500363 public static CancellationSignal apply(
364 boolean inflateSynchronously,
365 InflationProgress result,
366 @InflationFlag int reInflateFlags,
367 ArrayMap<Integer, RemoteViews> cachedContentViews,
368 ExpandableNotificationRow row,
Selim Cinek01d3da62017-04-28 15:03:48 -0700369 RemoteViews.OnClickHandler remoteViewClickHandler,
370 @Nullable InflationCallback callback) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700371 NotificationContentView privateLayout = row.getPrivateLayout();
372 NotificationContentView publicLayout = row.getPublicLayout();
373 final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
374
Kevind5022f92018-10-08 18:30:26 -0700375 int flag = FLAG_CONTENT_VIEW_CONTRACTED;
Selim Cinek01d3da62017-04-28 15:03:48 -0700376 if ((reInflateFlags & flag) != 0) {
Kevind4660b22018-09-27 10:57:35 -0700377 boolean isNewView =
378 !canReapplyRemoteView(result.newContentView,
Kevind5022f92018-10-08 18:30:26 -0700379 cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
Selim Cinek01d3da62017-04-28 15:03:48 -0700380 ApplyCallback applyCallback = new ApplyCallback() {
381 @Override
382 public void setResultView(View v) {
383 result.inflatedContentView = v;
384 }
385
386 @Override
387 public RemoteViews getRemoteView() {
388 return result.newContentView;
389 }
390 };
Ned Burns342c3a02019-02-15 18:08:39 -0500391 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
Selim Cinekc3fec682019-06-06 18:11:07 -0700392 row, isNewView, remoteViewClickHandler, callback, privateLayout,
Selim Cinek131f1a42017-06-05 17:50:19 -0700393 privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
394 NotificationContentView.VISIBLE_TYPE_CONTRACTED),
Selim Cinek01d3da62017-04-28 15:03:48 -0700395 runningInflations, applyCallback);
396 }
397
Kevind5022f92018-10-08 18:30:26 -0700398 flag = FLAG_CONTENT_VIEW_EXPANDED;
Selim Cinek01d3da62017-04-28 15:03:48 -0700399 if ((reInflateFlags & flag) != 0) {
400 if (result.newExpandedView != null) {
Kevind4660b22018-09-27 10:57:35 -0700401 boolean isNewView =
402 !canReapplyRemoteView(result.newExpandedView,
Kevind5022f92018-10-08 18:30:26 -0700403 cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
Selim Cinek01d3da62017-04-28 15:03:48 -0700404 ApplyCallback applyCallback = new ApplyCallback() {
405 @Override
406 public void setResultView(View v) {
407 result.inflatedExpandedView = v;
408 }
409
410 @Override
411 public RemoteViews getRemoteView() {
412 return result.newExpandedView;
413 }
414 };
Ned Burns342c3a02019-02-15 18:08:39 -0500415 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
Selim Cinekc3fec682019-06-06 18:11:07 -0700416 cachedContentViews, row, isNewView, remoteViewClickHandler,
Ned Burns342c3a02019-02-15 18:08:39 -0500417 callback, privateLayout, privateLayout.getExpandedChild(),
Selim Cinek131f1a42017-06-05 17:50:19 -0700418 privateLayout.getVisibleWrapper(
419 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
Selim Cinek01d3da62017-04-28 15:03:48 -0700420 applyCallback);
Selim Cinek10790672017-03-08 16:33:05 -0800421 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700422 }
423
Kevind5022f92018-10-08 18:30:26 -0700424 flag = FLAG_CONTENT_VIEW_HEADS_UP;
Selim Cinek01d3da62017-04-28 15:03:48 -0700425 if ((reInflateFlags & flag) != 0) {
426 if (result.newHeadsUpView != null) {
Kevind4660b22018-09-27 10:57:35 -0700427 boolean isNewView =
428 !canReapplyRemoteView(result.newHeadsUpView,
Kevind5022f92018-10-08 18:30:26 -0700429 cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
Selim Cinek01d3da62017-04-28 15:03:48 -0700430 ApplyCallback applyCallback = new ApplyCallback() {
431 @Override
432 public void setResultView(View v) {
433 result.inflatedHeadsUpView = v;
434 }
435
436 @Override
437 public RemoteViews getRemoteView() {
438 return result.newHeadsUpView;
439 }
440 };
Ned Burns342c3a02019-02-15 18:08:39 -0500441 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
Selim Cinekc3fec682019-06-06 18:11:07 -0700442 cachedContentViews, row, isNewView, remoteViewClickHandler,
Ned Burns342c3a02019-02-15 18:08:39 -0500443 callback, privateLayout, privateLayout.getHeadsUpChild(),
Selim Cinek131f1a42017-06-05 17:50:19 -0700444 privateLayout.getVisibleWrapper(
Kevind5022f92018-10-08 18:30:26 -0700445 VISIBLE_TYPE_HEADSUP), runningInflations,
Selim Cinek01d3da62017-04-28 15:03:48 -0700446 applyCallback);
447 }
448 }
449
Kevind5022f92018-10-08 18:30:26 -0700450 flag = FLAG_CONTENT_VIEW_PUBLIC;
Selim Cinek01d3da62017-04-28 15:03:48 -0700451 if ((reInflateFlags & flag) != 0) {
Kevind4660b22018-09-27 10:57:35 -0700452 boolean isNewView =
453 !canReapplyRemoteView(result.newPublicView,
Kevind5022f92018-10-08 18:30:26 -0700454 cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
Selim Cinek01d3da62017-04-28 15:03:48 -0700455 ApplyCallback applyCallback = new ApplyCallback() {
456 @Override
457 public void setResultView(View v) {
458 result.inflatedPublicView = v;
459 }
460
461 @Override
462 public RemoteViews getRemoteView() {
463 return result.newPublicView;
464 }
465 };
Ned Burns342c3a02019-02-15 18:08:39 -0500466 applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
Selim Cinekc3fec682019-06-06 18:11:07 -0700467 row, isNewView, remoteViewClickHandler, callback,
Selim Cinek131f1a42017-06-05 17:50:19 -0700468 publicLayout, publicLayout.getContractedChild(),
469 publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
470 runningInflations, applyCallback);
Selim Cinek01d3da62017-04-28 15:03:48 -0700471 }
472
Selim Cinek01d3da62017-04-28 15:03:48 -0700473 // Let's try to finish, maybe nobody is even inflating anything
Selim Cinekc3fec682019-06-06 18:11:07 -0700474 finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
Selim Cinek01d3da62017-04-28 15:03:48 -0700475 CancellationSignal cancellationSignal = new CancellationSignal();
476 cancellationSignal.setOnCancelListener(
477 () -> runningInflations.values().forEach(CancellationSignal::cancel));
478 return cancellationSignal;
479 }
480
Selim Cinekd246bed2017-06-19 16:58:35 -0700481 @VisibleForTesting
Ned Burns342c3a02019-02-15 18:08:39 -0500482 static void applyRemoteView(
483 boolean inflateSynchronously,
484 final InflationProgress result,
485 final @InflationFlag int reInflateFlags,
486 @InflationFlag int inflationId,
Kevind4660b22018-09-27 10:57:35 -0700487 final ArrayMap<Integer, RemoteViews> cachedContentViews,
Ned Burns342c3a02019-02-15 18:08:39 -0500488 final ExpandableNotificationRow row,
Ned Burns342c3a02019-02-15 18:08:39 -0500489 boolean isNewView,
Selim Cinek01d3da62017-04-28 15:03:48 -0700490 RemoteViews.OnClickHandler remoteViewClickHandler,
Kevind4660b22018-09-27 10:57:35 -0700491 @Nullable final InflationCallback callback,
Ned Burns342c3a02019-02-15 18:08:39 -0500492 NotificationContentView parentLayout,
493 View existingView,
Selim Cinek131f1a42017-06-05 17:50:19 -0700494 NotificationViewWrapper existingWrapper,
Selim Cinek01d3da62017-04-28 15:03:48 -0700495 final HashMap<Integer, CancellationSignal> runningInflations,
496 ApplyCallback applyCallback) {
Selim Cinekd246bed2017-06-19 16:58:35 -0700497 RemoteViews newContentView = applyCallback.getRemoteView();
Ned Burns342c3a02019-02-15 18:08:39 -0500498 if (inflateSynchronously) {
Jason Monk6dceace2018-05-15 20:24:07 -0400499 try {
500 if (isNewView) {
501 View v = newContentView.apply(
502 result.packageContext,
503 parentLayout,
504 remoteViewClickHandler);
505 v.setIsRootNamespace(true);
506 applyCallback.setResultView(v);
507 } else {
508 newContentView.reapply(
509 result.packageContext,
510 existingView,
511 remoteViewClickHandler);
512 existingWrapper.onReinflated();
513 }
514 } catch (Exception e) {
Kevind4660b22018-09-27 10:57:35 -0700515 handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
Jason Monk6dceace2018-05-15 20:24:07 -0400516 // Add a running inflation to make sure we don't trigger callbacks.
517 // Safe to do because only happens in tests.
518 runningInflations.put(inflationId, new CancellationSignal());
519 }
520 return;
521 }
Ahan Wude396fa2018-05-08 20:42:24 +0800522 RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
523
524 @Override
525 public void onViewInflated(View v) {
526 if (v instanceof ImageMessageConsumer) {
527 ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver());
528 }
529 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700530
531 @Override
532 public void onViewApplied(View v) {
533 if (isNewView) {
534 v.setIsRootNamespace(true);
535 applyCallback.setResultView(v);
Selim Cinek131f1a42017-06-05 17:50:19 -0700536 } else if (existingWrapper != null) {
537 existingWrapper.onReinflated();
Selim Cinek01d3da62017-04-28 15:03:48 -0700538 }
539 runningInflations.remove(inflationId);
Kevind4660b22018-09-27 10:57:35 -0700540 finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
Selim Cinekc3fec682019-06-06 18:11:07 -0700541 callback, row);
Selim Cinek01d3da62017-04-28 15:03:48 -0700542 }
543
544 @Override
545 public void onError(Exception e) {
Selim Cinekd246bed2017-06-19 16:58:35 -0700546 // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
547 // actually also be a system issue, so let's try on the UI thread again to be safe.
548 try {
549 View newView = existingView;
550 if (isNewView) {
551 newView = newContentView.apply(
552 result.packageContext,
553 parentLayout,
554 remoteViewClickHandler);
555 } else {
556 newContentView.reapply(
557 result.packageContext,
558 existingView,
559 remoteViewClickHandler);
560 }
561 Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
562 e);
563 onViewApplied(newView);
564 } catch (Exception anotherException) {
565 runningInflations.remove(inflationId);
Kevind4660b22018-09-27 10:57:35 -0700566 handleInflationError(runningInflations, e, row.getStatusBarNotification(),
567 callback);
Selim Cinekd246bed2017-06-19 16:58:35 -0700568 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700569 }
570 };
571 CancellationSignal cancellationSignal;
Selim Cinek01d3da62017-04-28 15:03:48 -0700572 if (isNewView) {
573 cancellationSignal = newContentView.applyAsync(
574 result.packageContext,
575 parentLayout,
Selim Cinek9c7b3072019-04-12 19:03:41 -0700576 null,
Selim Cinek01d3da62017-04-28 15:03:48 -0700577 listener,
578 remoteViewClickHandler);
579 } else {
580 cancellationSignal = newContentView.reapplyAsync(
581 result.packageContext,
582 existingView,
Selim Cinek9c7b3072019-04-12 19:03:41 -0700583 null,
Selim Cinek01d3da62017-04-28 15:03:48 -0700584 listener,
585 remoteViewClickHandler);
586 }
587 runningInflations.put(inflationId, cancellationSignal);
588 }
589
Kevind4660b22018-09-27 10:57:35 -0700590 private static void handleInflationError(
591 HashMap<Integer, CancellationSignal> runningInflations, Exception e,
592 StatusBarNotification notification, @Nullable InflationCallback callback) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700593 Assert.isMainThread();
594 runningInflations.values().forEach(CancellationSignal::cancel);
595 if (callback != null) {
596 callback.handleInflationException(notification, e);
Selim Cinek10790672017-03-08 16:33:05 -0800597 }
598 }
599
Selim Cinek01d3da62017-04-28 15:03:48 -0700600 /**
601 * Finish the inflation of the views
602 *
603 * @return true if the inflation was finished
604 */
Kevind4660b22018-09-27 10:57:35 -0700605 private static boolean finishIfDone(InflationProgress result,
606 @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
Selim Cinek01d3da62017-04-28 15:03:48 -0700607 HashMap<Integer, CancellationSignal> runningInflations,
Selim Cinekc3fec682019-06-06 18:11:07 -0700608 @Nullable InflationCallback endListener, ExpandableNotificationRow row) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700609 Assert.isMainThread();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500610 NotificationEntry entry = row.getEntry();
Selim Cinek01d3da62017-04-28 15:03:48 -0700611 NotificationContentView privateLayout = row.getPrivateLayout();
612 NotificationContentView publicLayout = row.getPublicLayout();
613 if (runningInflations.isEmpty()) {
Kevind5022f92018-10-08 18:30:26 -0700614 if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700615 if (result.inflatedContentView != null) {
Kevin217ae4e2019-03-07 15:49:17 -0800616 // New view case
Selim Cinek01d3da62017-04-28 15:03:48 -0700617 privateLayout.setContractedChild(result.inflatedContentView);
Kevin217ae4e2019-03-07 15:49:17 -0800618 cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
619 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
620 // Reinflation case. Only update if it's still cached (i.e. view has not been
621 // freed while inflating).
622 cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700623 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700624 }
625
Kevind5022f92018-10-08 18:30:26 -0700626 if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700627 if (result.inflatedExpandedView != null) {
628 privateLayout.setExpandedChild(result.inflatedExpandedView);
Kevin217ae4e2019-03-07 15:49:17 -0800629 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700630 } else if (result.newExpandedView == null) {
631 privateLayout.setExpandedChild(null);
Kevin217ae4e2019-03-07 15:49:17 -0800632 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
633 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
634 cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700635 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000636 if (result.newExpandedView != null) {
637 privateLayout.setExpandedInflatedSmartReplies(
638 result.expandedInflatedSmartReplies);
639 } else {
640 privateLayout.setExpandedInflatedSmartReplies(null);
641 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700642 row.setExpandable(result.newExpandedView != null);
643 }
644
Kevind5022f92018-10-08 18:30:26 -0700645 if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700646 if (result.inflatedHeadsUpView != null) {
647 privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
Kevin217ae4e2019-03-07 15:49:17 -0800648 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700649 } else if (result.newHeadsUpView == null) {
650 privateLayout.setHeadsUpChild(null);
Kevin217ae4e2019-03-07 15:49:17 -0800651 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
652 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
653 cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700654 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000655 if (result.newHeadsUpView != null) {
656 privateLayout.setHeadsUpInflatedSmartReplies(
657 result.headsUpInflatedSmartReplies);
658 } else {
659 privateLayout.setHeadsUpInflatedSmartReplies(null);
660 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700661 }
662
Kevind5022f92018-10-08 18:30:26 -0700663 if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700664 if (result.inflatedPublicView != null) {
665 publicLayout.setContractedChild(result.inflatedPublicView);
Kevin217ae4e2019-03-07 15:49:17 -0800666 cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
667 } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
668 cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
Selim Cinek01d3da62017-04-28 15:03:48 -0700669 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700670 }
671
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800672 entry.headsUpStatusBarText = result.headsUpStatusBarText;
673 entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
Selim Cinek01d3da62017-04-28 15:03:48 -0700674 if (endListener != null) {
Kevind4660b22018-09-27 10:57:35 -0700675 endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
Selim Cinek01d3da62017-04-28 15:03:48 -0700676 }
677 return true;
678 }
679 return false;
680 }
681
682 private static RemoteViews createExpandedView(Notification.Builder builder,
Selim Cinek1a48bab2017-02-17 19:38:40 -0800683 boolean isLowPriority) {
684 RemoteViews bigContentView = builder.createBigContentView();
685 if (bigContentView != null) {
686 return bigContentView;
687 }
688 if (isLowPriority) {
689 RemoteViews contentView = builder.createContentView();
690 Notification.Builder.makeHeaderExpanded(contentView);
691 return contentView;
692 }
693 return null;
694 }
695
Selim Cinek01d3da62017-04-28 15:03:48 -0700696 private static RemoteViews createContentView(Notification.Builder builder,
Selim Cinek1a48bab2017-02-17 19:38:40 -0800697 boolean isLowPriority, boolean useLarge) {
698 if (isLowPriority) {
699 return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
700 }
701 return builder.createContentView(useLarge);
702 }
703
Selim Cinekfc8073c2017-08-16 17:50:20 -0700704 /**
705 * @param newView The new view that will be applied
706 * @param oldView The old view that was applied to the existing view before
707 * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply.
708 */
709 @VisibleForTesting
710 static boolean canReapplyRemoteView(final RemoteViews newView,
711 final RemoteViews oldView) {
712 return (newView == null && oldView == null) ||
713 (newView != null && oldView != null
714 && oldView.getPackage() != null
715 && newView.getPackage() != null
716 && newView.getPackage().equals(oldView.getPackage())
717 && newView.getLayoutId() == oldView.getLayoutId()
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800718 && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
Selim Cinek1a48bab2017-02-17 19:38:40 -0800719 }
Selim Cinekc478f902017-02-22 20:55:44 -0800720
Selim Cinek2630dc72017-04-20 15:16:10 -0700721 public void setInflationCallback(InflationCallback callback) {
722 mCallback = callback;
Selim Cinekc478f902017-02-22 20:55:44 -0800723 }
724
Selim Cinek2630dc72017-04-20 15:16:10 -0700725 public interface InflationCallback {
Selim Cinek01d3da62017-04-28 15:03:48 -0700726 void handleInflationException(StatusBarNotification notification, Exception e);
Kevind4660b22018-09-27 10:57:35 -0700727
728 /**
729 * Callback for after the content views finish inflating.
730 *
731 * @param entry the entry with the content views set
732 * @param inflatedFlags the flags associated with the content views that were inflated
733 */
Ned Burns342c3a02019-02-15 18:08:39 -0500734 void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags);
Selim Cinekc478f902017-02-22 20:55:44 -0800735 }
Selim Cinek10790672017-03-08 16:33:05 -0800736
Lucas Dupinf03e7522018-06-25 16:21:13 -0700737 public void clearCachesAndReInflate() {
Kevind4660b22018-09-27 10:57:35 -0700738 mCachedContentViews.clear();
Selim Cinek2630dc72017-04-20 15:16:10 -0700739 inflateNotificationViews();
740 }
741
Ned Burns342c3a02019-02-15 18:08:39 -0500742 /**
743 * Sets whether to perform inflation on the same thread as the caller. This method should only
744 * be used in tests, not in production.
745 */
746 @VisibleForTesting
747 void setInflateSynchronously(boolean inflateSynchronously) {
748 mInflateSynchronously = inflateSynchronously;
749 }
750
Selim Cinek01d3da62017-04-28 15:03:48 -0700751 public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
Selim Cinek67ff2482017-05-25 10:27:28 -0700752 implements InflationCallback, InflationTask {
Selim Cinek2630dc72017-04-20 15:16:10 -0700753
754 private final StatusBarNotification mSbn;
755 private final Context mContext;
Ned Burns342c3a02019-02-15 18:08:39 -0500756 private final boolean mInflateSynchronously;
Selim Cinek01d3da62017-04-28 15:03:48 -0700757 private final boolean mIsLowPriority;
758 private final boolean mIsChildInGroup;
759 private final boolean mUsesIncreasedHeight;
760 private final InflationCallback mCallback;
761 private final boolean mUsesIncreasedHeadsUpHeight;
Kevind4660b22018-09-27 10:57:35 -0700762 private @InflationFlag int mReInflateFlags;
763 private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
Selim Cinek01d3da62017-04-28 15:03:48 -0700764 private ExpandableNotificationRow mRow;
Selim Cinek2630dc72017-04-20 15:16:10 -0700765 private Exception mError;
Selim Cinek01d3da62017-04-28 15:03:48 -0700766 private RemoteViews.OnClickHandler mRemoteViewClickHandler;
767 private CancellationSignal mCancellationSignal;
Selim Cinek2630dc72017-04-20 15:16:10 -0700768
Ned Burns342c3a02019-02-15 18:08:39 -0500769 private AsyncInflationTask(
770 StatusBarNotification notification,
771 boolean inflateSynchronously,
Kevind4660b22018-09-27 10:57:35 -0700772 @InflationFlag int reInflateFlags,
Ned Burns342c3a02019-02-15 18:08:39 -0500773 ArrayMap<Integer, RemoteViews> cachedContentViews,
774 ExpandableNotificationRow row,
775 boolean isLowPriority,
776 boolean isChildInGroup,
777 boolean usesIncreasedHeight,
778 boolean usesIncreasedHeadsUpHeight,
Ned Burns342c3a02019-02-15 18:08:39 -0500779 InflationCallback callback,
780 RemoteViews.OnClickHandler remoteViewClickHandler) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700781 mRow = row;
Selim Cinek2630dc72017-04-20 15:16:10 -0700782 mSbn = notification;
Ned Burns342c3a02019-02-15 18:08:39 -0500783 mInflateSynchronously = inflateSynchronously;
Selim Cinek2630dc72017-04-20 15:16:10 -0700784 mReInflateFlags = reInflateFlags;
Kevind4660b22018-09-27 10:57:35 -0700785 mCachedContentViews = cachedContentViews;
Selim Cinek01d3da62017-04-28 15:03:48 -0700786 mContext = mRow.getContext();
787 mIsLowPriority = isLowPriority;
788 mIsChildInGroup = isChildInGroup;
789 mUsesIncreasedHeight = usesIncreasedHeight;
790 mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
Selim Cinek01d3da62017-04-28 15:03:48 -0700791 mRemoteViewClickHandler = remoteViewClickHandler;
792 mCallback = callback;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500793 NotificationEntry entry = row.getEntry();
Selim Cinek67ff2482017-05-25 10:27:28 -0700794 entry.setInflationTask(this);
795 }
796
797 @VisibleForTesting
Kevind4660b22018-09-27 10:57:35 -0700798 @InflationFlag
Selim Cinek67ff2482017-05-25 10:27:28 -0700799 public int getReInflateFlags() {
800 return mReInflateFlags;
Selim Cinek2630dc72017-04-20 15:16:10 -0700801 }
802
803 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700804 protected InflationProgress doInBackground(Void... params) {
Selim Cinek2630dc72017-04-20 15:16:10 -0700805 try {
806 final Notification.Builder recoveredBuilder
807 = Notification.Builder.recoverBuilder(mContext,
808 mSbn.getNotification());
Tony Mak628cb932018-06-19 18:30:41 +0100809
Selim Cinek01d3da62017-04-28 15:03:48 -0700810 Context packageContext = mSbn.getPackageContext(mContext);
Selim Cinek5fb73f82017-04-20 16:55:38 -0700811 Notification notification = mSbn.getNotification();
Selim Cinek0847acd2017-04-24 19:48:29 -0700812 if (notification.isMediaNotification()) {
813 MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
Selim Cinek01d3da62017-04-28 15:03:48 -0700814 packageContext);
Selim Cinek5fb73f82017-04-20 16:55:38 -0700815 processor.processNotification(notification, recoveredBuilder);
816 }
Gustav Sennton5759f872019-02-13 17:25:26 +0000817 InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
Selim Cinekc3fec682019-06-06 18:11:07 -0700818 recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
819 mUsesIncreasedHeadsUpHeight, packageContext);
Gustav Sennton5759f872019-02-13 17:25:26 +0000820 return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
Gustav Senntondfa968d2019-09-13 12:00:50 +0100821 mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
Gustav Sennton8a52dc32019-04-15 12:48:23 +0100822 mRow.getExistingSmartRepliesAndActions());
Selim Cinek2630dc72017-04-20 15:16:10 -0700823 } catch (Exception e) {
824 mError = e;
825 return null;
826 }
827 }
828
829 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700830 protected void onPostExecute(InflationProgress result) {
Selim Cinek2630dc72017-04-20 15:16:10 -0700831 if (mError == null) {
Ned Burns342c3a02019-02-15 18:08:39 -0500832 mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
Selim Cinekc3fec682019-06-06 18:11:07 -0700833 mCachedContentViews, mRow, mRemoteViewClickHandler, this);
Selim Cinek2630dc72017-04-20 15:16:10 -0700834 } else {
835 handleError(mError);
836 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800837 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800838
Selim Cinek01d3da62017-04-28 15:03:48 -0700839 private void handleError(Exception e) {
840 mRow.getEntry().onInflationTaskFinished();
841 StatusBarNotification sbn = mRow.getStatusBarNotification();
842 final String ident = sbn.getPackageName() + "/0x"
843 + Integer.toHexString(sbn.getId());
844 Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
845 mCallback.handleInflationException(sbn,
846 new InflationException("Couldn't inflate contentViews" + e));
Selim Cinek2630dc72017-04-20 15:16:10 -0700847 }
Selim Cinek01d3da62017-04-28 15:03:48 -0700848
Selim Cinek0f66a4c2017-04-28 19:26:28 -0700849 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700850 public void abort() {
851 cancel(true /* mayInterruptIfRunning */);
852 if (mCancellationSignal != null) {
853 mCancellationSignal.cancel();
854 }
855 }
856
857 @Override
Selim Cinek67ff2482017-05-25 10:27:28 -0700858 public void supersedeTask(InflationTask task) {
859 if (task instanceof AsyncInflationTask) {
860 // We want to inflate all flags of the previous task as well
861 mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags;
862 }
863 }
864
865 @Override
Selim Cinek01d3da62017-04-28 15:03:48 -0700866 public void handleInflationException(StatusBarNotification notification, Exception e) {
867 handleError(e);
868 }
869
870 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500871 public void onAsyncInflationFinished(NotificationEntry entry,
Kevind4660b22018-09-27 10:57:35 -0700872 @InflationFlag int inflatedFlags) {
Selim Cinek01d3da62017-04-28 15:03:48 -0700873 mRow.getEntry().onInflationTaskFinished();
874 mRow.onNotificationUpdated();
Kevind4660b22018-09-27 10:57:35 -0700875 mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
Ahan Wude396fa2018-05-08 20:42:24 +0800876
877 // Notify the resolver that the inflation task has finished,
878 // try to purge unnecessary cached entries.
879 mRow.getImageResolver().purgeCache();
Selim Cinek01d3da62017-04-28 15:03:48 -0700880 }
Selim Cinek2630dc72017-04-20 15:16:10 -0700881 }
882
Selim Cinekd246bed2017-06-19 16:58:35 -0700883 @VisibleForTesting
884 static class InflationProgress {
Selim Cinek01d3da62017-04-28 15:03:48 -0700885 private RemoteViews newContentView;
886 private RemoteViews newHeadsUpView;
887 private RemoteViews newExpandedView;
Selim Cinek01d3da62017-04-28 15:03:48 -0700888 private RemoteViews newPublicView;
889
Selim Cinekd246bed2017-06-19 16:58:35 -0700890 @VisibleForTesting
891 Context packageContext;
Selim Cinek01d3da62017-04-28 15:03:48 -0700892
893 private View inflatedContentView;
894 private View inflatedHeadsUpView;
895 private View inflatedExpandedView;
Selim Cinek01d3da62017-04-28 15:03:48 -0700896 private View inflatedPublicView;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800897 private CharSequence headsUpStatusBarText;
898 private CharSequence headsUpStatusBarTextPublic;
Gustav Sennton5759f872019-02-13 17:25:26 +0000899
900 private InflatedSmartReplies expandedInflatedSmartReplies;
901 private InflatedSmartReplies headsUpInflatedSmartReplies;
Selim Cinek01d3da62017-04-28 15:03:48 -0700902 }
903
Selim Cinekd246bed2017-06-19 16:58:35 -0700904 @VisibleForTesting
905 abstract static class ApplyCallback {
Selim Cinek01d3da62017-04-28 15:03:48 -0700906 public abstract void setResultView(View v);
907 public abstract RemoteViews getRemoteView();
Selim Cinek2630dc72017-04-20 15:16:10 -0700908 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800909}