Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.systemui.statusbar.notification; |
| 18 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 19 | import android.annotation.Nullable; |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 20 | import android.app.Notification; |
| 21 | import android.content.Context; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 22 | import android.os.AsyncTask; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 23 | import android.os.CancellationSignal; |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 24 | import android.service.notification.StatusBarNotification; |
| 25 | import android.util.Log; |
| 26 | import android.view.View; |
| 27 | import android.widget.RemoteViews; |
| 28 | |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 29 | import com.android.internal.annotations.VisibleForTesting; |
Selim Cinek | ac5f027 | 2017-05-02 16:05:41 -0700 | [diff] [blame] | 30 | import com.android.systemui.R; |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 31 | import com.android.systemui.statusbar.InflationTask; |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 32 | import com.android.systemui.statusbar.ExpandableNotificationRow; |
| 33 | import com.android.systemui.statusbar.NotificationContentView; |
| 34 | import com.android.systemui.statusbar.NotificationData; |
| 35 | import com.android.systemui.statusbar.phone.StatusBar; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 36 | import com.android.systemui.util.Assert; |
| 37 | |
| 38 | import java.util.HashMap; |
Selim Cinek | ae8de8c | 2017-05-19 17:54:48 -0700 | [diff] [blame] | 39 | import java.util.concurrent.Executor; |
| 40 | import java.util.concurrent.LinkedBlockingQueue; |
| 41 | import java.util.concurrent.ThreadFactory; |
| 42 | import java.util.concurrent.ThreadPoolExecutor; |
| 43 | import java.util.concurrent.TimeUnit; |
| 44 | import java.util.concurrent.atomic.AtomicInteger; |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 45 | |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 46 | /** |
| 47 | * A utility that inflates the right kind of contentView based on the state |
| 48 | */ |
| 49 | public class NotificationInflater { |
| 50 | |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 51 | public static final String TAG = "NotificationInflater"; |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 52 | @VisibleForTesting |
| 53 | static final int FLAG_REINFLATE_ALL = ~0; |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 54 | private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 55 | @VisibleForTesting |
| 56 | static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1; |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 57 | private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2; |
| 58 | private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3; |
| 59 | private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4; |
Selim Cinek | ae8de8c | 2017-05-19 17:54:48 -0700 | [diff] [blame] | 60 | private static final InflationExecutor EXECUTOR = new InflationExecutor(); |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 61 | |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 62 | private final ExpandableNotificationRow mRow; |
| 63 | private boolean mIsLowPriority; |
| 64 | private boolean mUsesIncreasedHeight; |
| 65 | private boolean mUsesIncreasedHeadsUpHeight; |
| 66 | private RemoteViews.OnClickHandler mRemoteViewClickHandler; |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 67 | private boolean mIsChildInGroup; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 68 | private InflationCallback mCallback; |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 69 | private boolean mRedactAmbient; |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 70 | |
| 71 | public NotificationInflater(ExpandableNotificationRow row) { |
| 72 | mRow = row; |
| 73 | } |
| 74 | |
| 75 | public void setIsLowPriority(boolean isLowPriority) { |
| 76 | mIsLowPriority = isLowPriority; |
| 77 | } |
| 78 | |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 79 | /** |
| 80 | * Set whether the notification is a child in a group |
| 81 | * |
| 82 | * @return whether the view was re-inflated |
| 83 | */ |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 84 | public void setIsChildInGroup(boolean childInGroup) { |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 85 | if (childInGroup != mIsChildInGroup) { |
| 86 | mIsChildInGroup = childInGroup; |
| 87 | if (mIsLowPriority) { |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 88 | int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW; |
| 89 | inflateNotificationViews(flags); |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 90 | } |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 91 | } ; |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 92 | } |
| 93 | |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 94 | public void setUsesIncreasedHeight(boolean usesIncreasedHeight) { |
| 95 | mUsesIncreasedHeight = usesIncreasedHeight; |
| 96 | } |
| 97 | |
| 98 | public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) { |
| 99 | mUsesIncreasedHeadsUpHeight = usesIncreasedHeight; |
| 100 | } |
| 101 | |
| 102 | public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) { |
| 103 | mRemoteViewClickHandler = remoteViewClickHandler; |
| 104 | } |
| 105 | |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 106 | public void setRedactAmbient(boolean redactAmbient) { |
| 107 | if (mRedactAmbient != redactAmbient) { |
| 108 | mRedactAmbient = redactAmbient; |
| 109 | if (mRow.getEntry() == null) { |
| 110 | return; |
| 111 | } |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 112 | inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW); |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 113 | } |
| 114 | } |
| 115 | |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 116 | /** |
| 117 | * Inflate all views of this notification on a background thread. This is asynchronous and will |
| 118 | * notify the callback once it's finished. |
| 119 | */ |
| 120 | public void inflateNotificationViews() { |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 121 | inflateNotificationViews(FLAG_REINFLATE_ALL); |
| 122 | } |
| 123 | |
| 124 | /** |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 125 | * Reinflate all views for the specified flags on a background thread. This is asynchronous and |
| 126 | * will notify the callback once it's finished. |
| 127 | * |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 128 | * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL} |
| 129 | * to reinflate all of views. |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 130 | */ |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 131 | @VisibleForTesting |
| 132 | void inflateNotificationViews(int reInflateFlags) { |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 133 | if (mRow.isRemoved()) { |
| 134 | // We don't want to reinflate anything for removed notifications. Otherwise views might |
| 135 | // be readded to the stack, leading to leaks. This may happen with low-priority groups |
| 136 | // where the removal of already removed children can lead to a reinflation. |
| 137 | return; |
| 138 | } |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 139 | StatusBarNotification sbn = mRow.getEntry().notification; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 140 | new AsyncInflationTask(sbn, reInflateFlags, mRow, mIsLowPriority, |
| 141 | mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, |
| 142 | mCallback, mRemoteViewClickHandler).execute(); |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 143 | } |
| 144 | |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 145 | @VisibleForTesting |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 146 | InflationProgress inflateNotificationViews(int reInflateFlags, |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 147 | Notification.Builder builder, Context packageContext) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 148 | InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority, |
| 149 | mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, |
| 150 | mRedactAmbient, packageContext); |
| 151 | apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null); |
| 152 | return result; |
| 153 | } |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 154 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 155 | private static InflationProgress createRemoteViews(int reInflateFlags, |
| 156 | Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup, |
| 157 | boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, |
| 158 | Context packageContext) { |
| 159 | InflationProgress result = new InflationProgress(); |
| 160 | isLowPriority = isLowPriority && !isChildInGroup; |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 161 | if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 162 | result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 166 | result.newExpandedView = createExpandedView(builder, isLowPriority); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 170 | result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 174 | result.newPublicView = builder.makePublicContentView(); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 178 | result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification() |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 179 | : builder.makeAmbientNotification(); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 180 | } |
| 181 | result.packageContext = packageContext; |
| 182 | return result; |
| 183 | } |
Adrian Roos | 1a1ecfc | 2017-04-17 11:17:59 -0700 | [diff] [blame] | 184 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 185 | public static CancellationSignal apply(InflationProgress result, int reInflateFlags, |
| 186 | ExpandableNotificationRow row, boolean redactAmbient, |
| 187 | RemoteViews.OnClickHandler remoteViewClickHandler, |
| 188 | @Nullable InflationCallback callback) { |
| 189 | NotificationData.Entry entry = row.getEntry(); |
| 190 | NotificationContentView privateLayout = row.getPrivateLayout(); |
| 191 | NotificationContentView publicLayout = row.getPublicLayout(); |
| 192 | final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>(); |
| 193 | |
| 194 | int flag = FLAG_REINFLATE_CONTENT_VIEW; |
| 195 | if ((reInflateFlags & flag) != 0) { |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 196 | boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 197 | ApplyCallback applyCallback = new ApplyCallback() { |
| 198 | @Override |
| 199 | public void setResultView(View v) { |
| 200 | result.inflatedContentView = v; |
| 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public RemoteViews getRemoteView() { |
| 205 | return result.newContentView; |
| 206 | } |
| 207 | }; |
| 208 | applyRemoteView(result, reInflateFlags, flag, row, redactAmbient, |
| 209 | isNewView, remoteViewClickHandler, callback, entry, privateLayout, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 210 | privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( |
| 211 | NotificationContentView.VISIBLE_TYPE_CONTRACTED), |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 212 | runningInflations, applyCallback); |
| 213 | } |
| 214 | |
| 215 | flag = FLAG_REINFLATE_EXPANDED_VIEW; |
| 216 | if ((reInflateFlags & flag) != 0) { |
| 217 | if (result.newExpandedView != null) { |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 218 | boolean isNewView = !canReapplyRemoteView(result.newExpandedView, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 219 | entry.cachedBigContentView); |
| 220 | ApplyCallback applyCallback = new ApplyCallback() { |
| 221 | @Override |
| 222 | public void setResultView(View v) { |
| 223 | result.inflatedExpandedView = v; |
| 224 | } |
| 225 | |
| 226 | @Override |
| 227 | public RemoteViews getRemoteView() { |
| 228 | return result.newExpandedView; |
| 229 | } |
| 230 | }; |
| 231 | applyRemoteView(result, reInflateFlags, flag, row, |
| 232 | redactAmbient, isNewView, remoteViewClickHandler, callback, entry, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 233 | privateLayout, privateLayout.getExpandedChild(), |
| 234 | privateLayout.getVisibleWrapper( |
| 235 | NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 236 | applyCallback); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 237 | } |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | flag = FLAG_REINFLATE_HEADS_UP_VIEW; |
| 241 | if ((reInflateFlags & flag) != 0) { |
| 242 | if (result.newHeadsUpView != null) { |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 243 | boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 244 | entry.cachedHeadsUpContentView); |
| 245 | ApplyCallback applyCallback = new ApplyCallback() { |
| 246 | @Override |
| 247 | public void setResultView(View v) { |
| 248 | result.inflatedHeadsUpView = v; |
| 249 | } |
| 250 | |
| 251 | @Override |
| 252 | public RemoteViews getRemoteView() { |
| 253 | return result.newHeadsUpView; |
| 254 | } |
| 255 | }; |
| 256 | applyRemoteView(result, reInflateFlags, flag, row, |
| 257 | redactAmbient, isNewView, remoteViewClickHandler, callback, entry, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 258 | privateLayout, privateLayout.getHeadsUpChild(), |
| 259 | privateLayout.getVisibleWrapper( |
| 260 | NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 261 | applyCallback); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | flag = FLAG_REINFLATE_PUBLIC_VIEW; |
| 266 | if ((reInflateFlags & flag) != 0) { |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 267 | boolean isNewView = !canReapplyRemoteView(result.newPublicView, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 268 | entry.cachedPublicContentView); |
| 269 | ApplyCallback applyCallback = new ApplyCallback() { |
| 270 | @Override |
| 271 | public void setResultView(View v) { |
| 272 | result.inflatedPublicView = v; |
| 273 | } |
| 274 | |
| 275 | @Override |
| 276 | public RemoteViews getRemoteView() { |
| 277 | return result.newPublicView; |
| 278 | } |
| 279 | }; |
| 280 | applyRemoteView(result, reInflateFlags, flag, row, |
| 281 | redactAmbient, isNewView, remoteViewClickHandler, callback, entry, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 282 | publicLayout, publicLayout.getContractedChild(), |
| 283 | publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED), |
| 284 | runningInflations, applyCallback); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 285 | } |
| 286 | |
| 287 | flag = FLAG_REINFLATE_AMBIENT_VIEW; |
| 288 | if ((reInflateFlags & flag) != 0) { |
| 289 | NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout; |
| 290 | boolean isNewView = !canReapplyAmbient(row, redactAmbient) || |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 291 | !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 292 | ApplyCallback applyCallback = new ApplyCallback() { |
| 293 | @Override |
| 294 | public void setResultView(View v) { |
| 295 | result.inflatedAmbientView = v; |
| 296 | } |
| 297 | |
| 298 | @Override |
| 299 | public RemoteViews getRemoteView() { |
| 300 | return result.newAmbientView; |
| 301 | } |
| 302 | }; |
| 303 | applyRemoteView(result, reInflateFlags, flag, row, |
| 304 | redactAmbient, isNewView, remoteViewClickHandler, callback, entry, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 305 | newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper( |
| 306 | NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 307 | applyCallback); |
| 308 | } |
| 309 | |
| 310 | // Let's try to finish, maybe nobody is even inflating anything |
| 311 | finishIfDone(result, reInflateFlags, runningInflations, callback, row, |
| 312 | redactAmbient); |
| 313 | CancellationSignal cancellationSignal = new CancellationSignal(); |
| 314 | cancellationSignal.setOnCancelListener( |
| 315 | () -> runningInflations.values().forEach(CancellationSignal::cancel)); |
| 316 | return cancellationSignal; |
| 317 | } |
| 318 | |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 319 | @VisibleForTesting |
| 320 | static void applyRemoteView(final InflationProgress result, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 321 | final int reInflateFlags, int inflationId, |
| 322 | final ExpandableNotificationRow row, |
| 323 | final boolean redactAmbient, boolean isNewView, |
| 324 | RemoteViews.OnClickHandler remoteViewClickHandler, |
| 325 | @Nullable final InflationCallback callback, NotificationData.Entry entry, |
| 326 | NotificationContentView parentLayout, View existingView, |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 327 | NotificationViewWrapper existingWrapper, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 328 | final HashMap<Integer, CancellationSignal> runningInflations, |
| 329 | ApplyCallback applyCallback) { |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 330 | RemoteViews newContentView = applyCallback.getRemoteView(); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 331 | RemoteViews.OnViewAppliedListener listener |
| 332 | = new RemoteViews.OnViewAppliedListener() { |
| 333 | |
| 334 | @Override |
| 335 | public void onViewApplied(View v) { |
| 336 | if (isNewView) { |
| 337 | v.setIsRootNamespace(true); |
| 338 | applyCallback.setResultView(v); |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 339 | } else if (existingWrapper != null) { |
| 340 | existingWrapper.onReinflated(); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 341 | } |
| 342 | runningInflations.remove(inflationId); |
| 343 | finishIfDone(result, reInflateFlags, runningInflations, callback, row, |
| 344 | redactAmbient); |
| 345 | } |
| 346 | |
| 347 | @Override |
| 348 | public void onError(Exception e) { |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 349 | // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could |
| 350 | // actually also be a system issue, so let's try on the UI thread again to be safe. |
| 351 | try { |
| 352 | View newView = existingView; |
| 353 | if (isNewView) { |
| 354 | newView = newContentView.apply( |
| 355 | result.packageContext, |
| 356 | parentLayout, |
| 357 | remoteViewClickHandler); |
| 358 | } else { |
| 359 | newContentView.reapply( |
| 360 | result.packageContext, |
| 361 | existingView, |
| 362 | remoteViewClickHandler); |
| 363 | } |
| 364 | Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.", |
| 365 | e); |
| 366 | onViewApplied(newView); |
| 367 | } catch (Exception anotherException) { |
| 368 | runningInflations.remove(inflationId); |
| 369 | handleInflationError(runningInflations, e, entry.notification, callback); |
| 370 | } |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 371 | } |
| 372 | }; |
| 373 | CancellationSignal cancellationSignal; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 374 | if (isNewView) { |
| 375 | cancellationSignal = newContentView.applyAsync( |
| 376 | result.packageContext, |
| 377 | parentLayout, |
Selim Cinek | ae8de8c | 2017-05-19 17:54:48 -0700 | [diff] [blame] | 378 | EXECUTOR, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 379 | listener, |
| 380 | remoteViewClickHandler); |
| 381 | } else { |
| 382 | cancellationSignal = newContentView.reapplyAsync( |
| 383 | result.packageContext, |
| 384 | existingView, |
Selim Cinek | ae8de8c | 2017-05-19 17:54:48 -0700 | [diff] [blame] | 385 | EXECUTOR, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 386 | listener, |
| 387 | remoteViewClickHandler); |
| 388 | } |
| 389 | runningInflations.put(inflationId, cancellationSignal); |
| 390 | } |
| 391 | |
| 392 | private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations, |
| 393 | Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) { |
| 394 | Assert.isMainThread(); |
| 395 | runningInflations.values().forEach(CancellationSignal::cancel); |
| 396 | if (callback != null) { |
| 397 | callback.handleInflationException(notification, e); |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 398 | } |
| 399 | } |
| 400 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 401 | /** |
| 402 | * Finish the inflation of the views |
| 403 | * |
| 404 | * @return true if the inflation was finished |
| 405 | */ |
| 406 | private static boolean finishIfDone(InflationProgress result, int reInflateFlags, |
| 407 | HashMap<Integer, CancellationSignal> runningInflations, |
| 408 | @Nullable InflationCallback endListener, ExpandableNotificationRow row, |
| 409 | boolean redactAmbient) { |
| 410 | Assert.isMainThread(); |
| 411 | NotificationData.Entry entry = row.getEntry(); |
| 412 | NotificationContentView privateLayout = row.getPrivateLayout(); |
| 413 | NotificationContentView publicLayout = row.getPublicLayout(); |
| 414 | if (runningInflations.isEmpty()) { |
| 415 | if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) { |
| 416 | if (result.inflatedContentView != null) { |
| 417 | privateLayout.setContractedChild(result.inflatedContentView); |
| 418 | } |
| 419 | entry.cachedContentView = result.newContentView; |
| 420 | } |
| 421 | |
| 422 | if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) { |
| 423 | if (result.inflatedExpandedView != null) { |
| 424 | privateLayout.setExpandedChild(result.inflatedExpandedView); |
| 425 | } else if (result.newExpandedView == null) { |
| 426 | privateLayout.setExpandedChild(null); |
| 427 | } |
| 428 | entry.cachedBigContentView = result.newExpandedView; |
| 429 | row.setExpandable(result.newExpandedView != null); |
| 430 | } |
| 431 | |
| 432 | if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) { |
| 433 | if (result.inflatedHeadsUpView != null) { |
| 434 | privateLayout.setHeadsUpChild(result.inflatedHeadsUpView); |
| 435 | } else if (result.newHeadsUpView == null) { |
| 436 | privateLayout.setHeadsUpChild(null); |
| 437 | } |
| 438 | entry.cachedHeadsUpContentView = result.newHeadsUpView; |
| 439 | } |
| 440 | |
| 441 | if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) { |
| 442 | if (result.inflatedPublicView != null) { |
| 443 | publicLayout.setContractedChild(result.inflatedPublicView); |
| 444 | } |
| 445 | entry.cachedPublicContentView = result.newPublicView; |
| 446 | } |
| 447 | |
| 448 | if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) { |
| 449 | if (result.inflatedAmbientView != null) { |
| 450 | NotificationContentView newParent = redactAmbient |
| 451 | ? publicLayout : privateLayout; |
| 452 | NotificationContentView otherParent = !redactAmbient |
| 453 | ? publicLayout : privateLayout; |
| 454 | newParent.setAmbientChild(result.inflatedAmbientView); |
| 455 | otherParent.setAmbientChild(null); |
| 456 | } |
| 457 | entry.cachedAmbientContentView = result.newAmbientView; |
| 458 | } |
| 459 | if (endListener != null) { |
| 460 | endListener.onAsyncInflationFinished(row.getEntry()); |
| 461 | } |
| 462 | return true; |
| 463 | } |
| 464 | return false; |
| 465 | } |
| 466 | |
| 467 | private static RemoteViews createExpandedView(Notification.Builder builder, |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 468 | boolean isLowPriority) { |
| 469 | RemoteViews bigContentView = builder.createBigContentView(); |
| 470 | if (bigContentView != null) { |
| 471 | return bigContentView; |
| 472 | } |
| 473 | if (isLowPriority) { |
| 474 | RemoteViews contentView = builder.createContentView(); |
| 475 | Notification.Builder.makeHeaderExpanded(contentView); |
| 476 | return contentView; |
| 477 | } |
| 478 | return null; |
| 479 | } |
| 480 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 481 | private static RemoteViews createContentView(Notification.Builder builder, |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 482 | boolean isLowPriority, boolean useLarge) { |
| 483 | if (isLowPriority) { |
| 484 | return builder.makeLowPriorityContentView(false /* useRegularSubtext */); |
| 485 | } |
| 486 | return builder.createContentView(useLarge); |
| 487 | } |
| 488 | |
Selim Cinek | fc8073c | 2017-08-16 17:50:20 -0700 | [diff] [blame] | 489 | /** |
| 490 | * @param newView The new view that will be applied |
| 491 | * @param oldView The old view that was applied to the existing view before |
| 492 | * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply. |
| 493 | */ |
| 494 | @VisibleForTesting |
| 495 | static boolean canReapplyRemoteView(final RemoteViews newView, |
| 496 | final RemoteViews oldView) { |
| 497 | return (newView == null && oldView == null) || |
| 498 | (newView != null && oldView != null |
| 499 | && oldView.getPackage() != null |
| 500 | && newView.getPackage() != null |
| 501 | && newView.getPackage().equals(oldView.getPackage()) |
| 502 | && newView.getLayoutId() == oldView.getLayoutId() |
| 503 | && !oldView.isReapplyDisallowed()); |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 504 | } |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 505 | |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 506 | public void setInflationCallback(InflationCallback callback) { |
| 507 | mCallback = callback; |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 508 | } |
| 509 | |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 510 | public interface InflationCallback { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 511 | void handleInflationException(StatusBarNotification notification, Exception e); |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 512 | void onAsyncInflationFinished(NotificationData.Entry entry); |
Selim Cinek | c478f90 | 2017-02-22 20:55:44 -0800 | [diff] [blame] | 513 | } |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 514 | |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 515 | public void onDensityOrFontScaleChanged() { |
| 516 | NotificationData.Entry entry = mRow.getEntry(); |
| 517 | entry.cachedAmbientContentView = null; |
| 518 | entry.cachedBigContentView = null; |
| 519 | entry.cachedContentView = null; |
| 520 | entry.cachedHeadsUpContentView = null; |
| 521 | entry.cachedPublicContentView = null; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 522 | inflateNotificationViews(); |
| 523 | } |
| 524 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 525 | private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) { |
| 526 | NotificationContentView ambientView = redactAmbient ? row.getPublicLayout() |
| 527 | : row.getPrivateLayout(); ; |
| 528 | return ambientView.getAmbientChild() != null; |
| 529 | } |
| 530 | |
| 531 | public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress> |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 532 | implements InflationCallback, InflationTask { |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 533 | |
| 534 | private final StatusBarNotification mSbn; |
| 535 | private final Context mContext; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 536 | private final boolean mIsLowPriority; |
| 537 | private final boolean mIsChildInGroup; |
| 538 | private final boolean mUsesIncreasedHeight; |
| 539 | private final InflationCallback mCallback; |
| 540 | private final boolean mUsesIncreasedHeadsUpHeight; |
| 541 | private final boolean mRedactAmbient; |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 542 | private int mReInflateFlags; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 543 | private ExpandableNotificationRow mRow; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 544 | private Exception mError; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 545 | private RemoteViews.OnClickHandler mRemoteViewClickHandler; |
| 546 | private CancellationSignal mCancellationSignal; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 547 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 548 | private AsyncInflationTask(StatusBarNotification notification, |
| 549 | int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority, |
| 550 | boolean isChildInGroup, boolean usesIncreasedHeight, |
| 551 | boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, |
| 552 | InflationCallback callback, |
| 553 | RemoteViews.OnClickHandler remoteViewClickHandler) { |
| 554 | mRow = row; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 555 | mSbn = notification; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 556 | mReInflateFlags = reInflateFlags; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 557 | mContext = mRow.getContext(); |
| 558 | mIsLowPriority = isLowPriority; |
| 559 | mIsChildInGroup = isChildInGroup; |
| 560 | mUsesIncreasedHeight = usesIncreasedHeight; |
| 561 | mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight; |
| 562 | mRedactAmbient = redactAmbient; |
| 563 | mRemoteViewClickHandler = remoteViewClickHandler; |
| 564 | mCallback = callback; |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 565 | NotificationData.Entry entry = row.getEntry(); |
| 566 | entry.setInflationTask(this); |
| 567 | } |
| 568 | |
| 569 | @VisibleForTesting |
| 570 | public int getReInflateFlags() { |
| 571 | return mReInflateFlags; |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 572 | } |
| 573 | |
| 574 | @Override |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 575 | protected InflationProgress doInBackground(Void... params) { |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 576 | try { |
| 577 | final Notification.Builder recoveredBuilder |
| 578 | = Notification.Builder.recoverBuilder(mContext, |
| 579 | mSbn.getNotification()); |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 580 | Context packageContext = mSbn.getPackageContext(mContext); |
Selim Cinek | 5fb73f8 | 2017-04-20 16:55:38 -0700 | [diff] [blame] | 581 | Notification notification = mSbn.getNotification(); |
Selim Cinek | ac5f027 | 2017-05-02 16:05:41 -0700 | [diff] [blame] | 582 | if (mIsLowPriority) { |
| 583 | int backgroundColor = mContext.getColor( |
| 584 | R.color.notification_material_background_low_priority_color); |
| 585 | recoveredBuilder.setBackgroundColorHint(backgroundColor); |
| 586 | } |
Selim Cinek | 0847acd | 2017-04-24 19:48:29 -0700 | [diff] [blame] | 587 | if (notification.isMediaNotification()) { |
| 588 | MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext, |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 589 | packageContext); |
Selim Cinek | 0847acd | 2017-04-24 19:48:29 -0700 | [diff] [blame] | 590 | processor.setIsLowPriority(mIsLowPriority); |
Selim Cinek | 5fb73f8 | 2017-04-20 16:55:38 -0700 | [diff] [blame] | 591 | processor.processNotification(notification, recoveredBuilder); |
| 592 | } |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 593 | return createRemoteViews(mReInflateFlags, |
| 594 | recoveredBuilder, mIsLowPriority, mIsChildInGroup, |
| 595 | mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, |
| 596 | packageContext); |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 597 | } catch (Exception e) { |
| 598 | mError = e; |
| 599 | return null; |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | @Override |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 604 | protected void onPostExecute(InflationProgress result) { |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 605 | if (mError == null) { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 606 | mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient, |
| 607 | mRemoteViewClickHandler, this); |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 608 | } else { |
| 609 | handleError(mError); |
| 610 | } |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 611 | } |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 612 | |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 613 | private void handleError(Exception e) { |
| 614 | mRow.getEntry().onInflationTaskFinished(); |
| 615 | StatusBarNotification sbn = mRow.getStatusBarNotification(); |
| 616 | final String ident = sbn.getPackageName() + "/0x" |
| 617 | + Integer.toHexString(sbn.getId()); |
| 618 | Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e); |
| 619 | mCallback.handleInflationException(sbn, |
| 620 | new InflationException("Couldn't inflate contentViews" + e)); |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 621 | } |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 622 | |
Selim Cinek | 0f66a4c | 2017-04-28 19:26:28 -0700 | [diff] [blame] | 623 | @Override |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 624 | public void abort() { |
| 625 | cancel(true /* mayInterruptIfRunning */); |
| 626 | if (mCancellationSignal != null) { |
| 627 | mCancellationSignal.cancel(); |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | @Override |
Selim Cinek | 67ff248 | 2017-05-25 10:27:28 -0700 | [diff] [blame] | 632 | public void supersedeTask(InflationTask task) { |
| 633 | if (task instanceof AsyncInflationTask) { |
| 634 | // We want to inflate all flags of the previous task as well |
| 635 | mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags; |
| 636 | } |
| 637 | } |
| 638 | |
| 639 | @Override |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 640 | public void handleInflationException(StatusBarNotification notification, Exception e) { |
| 641 | handleError(e); |
| 642 | } |
| 643 | |
| 644 | @Override |
| 645 | public void onAsyncInflationFinished(NotificationData.Entry entry) { |
| 646 | mRow.getEntry().onInflationTaskFinished(); |
| 647 | mRow.onNotificationUpdated(); |
| 648 | mCallback.onAsyncInflationFinished(mRow.getEntry()); |
| 649 | } |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 650 | } |
| 651 | |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 652 | @VisibleForTesting |
| 653 | static class InflationProgress { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 654 | private RemoteViews newContentView; |
| 655 | private RemoteViews newHeadsUpView; |
| 656 | private RemoteViews newExpandedView; |
| 657 | private RemoteViews newAmbientView; |
| 658 | private RemoteViews newPublicView; |
| 659 | |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 660 | @VisibleForTesting |
| 661 | Context packageContext; |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 662 | |
| 663 | private View inflatedContentView; |
| 664 | private View inflatedHeadsUpView; |
| 665 | private View inflatedExpandedView; |
| 666 | private View inflatedAmbientView; |
| 667 | private View inflatedPublicView; |
| 668 | } |
| 669 | |
Selim Cinek | d246bed | 2017-06-19 16:58:35 -0700 | [diff] [blame] | 670 | @VisibleForTesting |
| 671 | abstract static class ApplyCallback { |
Selim Cinek | 01d3da6 | 2017-04-28 15:03:48 -0700 | [diff] [blame] | 672 | public abstract void setResultView(View v); |
| 673 | public abstract RemoteViews getRemoteView(); |
Selim Cinek | 2630dc7 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 674 | } |
Selim Cinek | ae8de8c | 2017-05-19 17:54:48 -0700 | [diff] [blame] | 675 | |
| 676 | /** |
| 677 | * A custom executor that allows more tasks to be queued. Default values are copied from |
| 678 | * AsyncTask |
| 679 | */ |
| 680 | private static class InflationExecutor implements Executor { |
| 681 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); |
| 682 | // We want at least 2 threads and at most 4 threads in the core pool, |
| 683 | // preferring to have 1 less than the CPU count to avoid saturating |
| 684 | // the CPU with background work |
| 685 | private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); |
| 686 | private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; |
| 687 | private static final int KEEP_ALIVE_SECONDS = 30; |
| 688 | |
| 689 | private static final ThreadFactory sThreadFactory = new ThreadFactory() { |
| 690 | private final AtomicInteger mCount = new AtomicInteger(1); |
| 691 | |
| 692 | public Thread newThread(Runnable r) { |
| 693 | return new Thread(r, "InflaterThread #" + mCount.getAndIncrement()); |
| 694 | } |
| 695 | }; |
| 696 | |
| 697 | private final ThreadPoolExecutor mExecutor; |
| 698 | |
| 699 | private InflationExecutor() { |
| 700 | mExecutor = new ThreadPoolExecutor( |
| 701 | CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, |
| 702 | new LinkedBlockingQueue<>(), sThreadFactory); |
| 703 | mExecutor.allowCoreThreadTimeOut(true); |
| 704 | } |
| 705 | |
| 706 | @Override |
| 707 | public void execute(Runnable runnable) { |
| 708 | mExecutor.execute(runnable); |
| 709 | } |
| 710 | } |
Selim Cinek | 1a48bab | 2017-02-17 19:38:40 -0800 | [diff] [blame] | 711 | } |