blob: 6dc8322a5cf3da17e44fbf423e6790e8f62bc825 [file] [log] [blame]
Mady Mellor3dff9e62019-02-05 18:12:53 -08001/*
2 * Copyright (C) 2019 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 */
16package com.android.systemui.bubbles;
17
Mady Mellor3df7ab02019-12-09 15:07:10 -080018import static android.os.AsyncTask.Status.FINISHED;
Issei Suzukicac2a502019-04-16 16:52:50 +020019import static android.view.Display.INVALID_DISPLAY;
20
Mark Renouf9ba6cea2019-04-17 11:53:50 -040021import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
22
Pinyao Tingb073b762020-06-04 11:15:03 -070023import android.annotation.DimenRes;
Mady Mellor70cba7bb2019-07-02 15:06:07 -070024import android.annotation.NonNull;
Mady Mellor99a302602019-06-14 11:39:56 -070025import android.annotation.Nullable;
26import android.app.Notification;
27import android.app.PendingIntent;
Lyn Han6c40fe72019-05-08 14:06:33 -070028import android.content.Context;
Mady Mellor99a302602019-06-14 11:39:56 -070029import android.content.Intent;
Pinyao Ting3c930612020-05-19 00:26:03 +000030import android.content.pm.ApplicationInfo;
Lyn Han6c40fe72019-05-08 14:06:33 -070031import android.content.pm.PackageManager;
Mady Mellorb547e5e2019-12-02 10:15:56 -080032import android.content.pm.ShortcutInfo;
Mady Mellor99a302602019-06-14 11:39:56 -070033import android.content.res.Resources;
Lyn Hanb58c7562020-01-07 14:29:20 -080034import android.graphics.Bitmap;
35import android.graphics.Path;
Mady Mellordf898fd2020-01-09 09:26:36 -080036import android.graphics.drawable.Drawable;
Pinyao Ting175a5b82020-06-15 23:41:14 +000037import android.graphics.drawable.Icon;
Mark Renouf71a3af62019-04-08 15:02:54 -040038import android.os.UserHandle;
Mady Mellor99a302602019-06-14 11:39:56 -070039import android.provider.Settings;
Mady Mellor99a302602019-06-14 11:39:56 -070040import android.util.Log;
Mady Mellor3dff9e62019-02-05 18:12:53 -080041
Mark Renouf9ba6cea2019-04-17 11:53:50 -040042import com.android.internal.annotations.VisibleForTesting;
Pinyao Ting175a5b82020-06-15 23:41:14 +000043import com.android.internal.logging.InstanceId;
Lyn Han3cd75d72020-02-15 19:10:12 -080044import com.android.systemui.shared.system.SysUiStatsLog;
Mady Mellor3dff9e62019-02-05 18:12:53 -080045import com.android.systemui.statusbar.notification.collection.NotificationEntry;
46
Mady Mellor70cba7bb2019-07-02 15:06:07 -070047import java.io.FileDescriptor;
48import java.io.PrintWriter;
Mark Renouf71a3af62019-04-08 15:02:54 -040049import java.util.Objects;
50
Mady Mellor3dff9e62019-02-05 18:12:53 -080051/**
52 * Encapsulates the data and UI elements of a bubble.
53 */
Lyn Han3cd75d72020-02-15 19:10:12 -080054class Bubble implements BubbleViewProvider {
Mady Mellor99a302602019-06-14 11:39:56 -070055 private static final String TAG = "Bubble";
56
Mark Renouf85e0a902019-04-05 15:51:51 -040057 private final String mKey;
Mady Mellor99a302602019-06-14 11:39:56 -070058
Mark Renouf9ba6cea2019-04-17 11:53:50 -040059 private long mLastUpdated;
60 private long mLastAccessed;
Mady Mellor7f234902019-10-20 12:06:29 -070061
Pinyao Ting175a5b82020-06-15 23:41:14 +000062 @Nullable
Mady Mellorf44b6832020-01-14 13:26:14 -080063 private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
64
65 /** Whether the bubble should show a dot for the notification indicating updated content. */
66 private boolean mShowBubbleUpdateDot = true;
67
68 /** Whether flyout text should be suppressed, regardless of any other flags or state. */
69 private boolean mSuppressFlyout;
70
Mady Mellor3df7ab02019-12-09 15:07:10 -080071 // Items that are typically loaded later
72 private String mAppName;
73 private ShortcutInfo mShortcutInfo;
Lyn Hanefbaa972020-06-23 14:59:39 -070074 private String mMetadataShortcutId;
Mady Mellor3df7ab02019-12-09 15:07:10 -080075 private BadgedImageView mIconView;
76 private BubbleExpandedView mExpandedView;
77
Mady Mellor3df7ab02019-12-09 15:07:10 -080078 private BubbleViewInfoTask mInflationTask;
79 private boolean mInflateSynchronously;
Lyn Han6cb4e5f2020-04-27 15:11:18 -070080 private boolean mPendingIntentCanceled;
Santhosh Thangarajc39224b2020-06-18 10:05:54 -070081 private boolean mIsImportantConversation;
Mark Renouf71a3af62019-04-08 15:02:54 -040082
Mady Mellorce23c462019-06-17 17:30:07 -070083 /**
Mady Mellordf898fd2020-01-09 09:26:36 -080084 * Presentational info about the flyout.
85 */
86 public static class FlyoutMessage {
Pinyao Ting175a5b82020-06-15 23:41:14 +000087 @Nullable public Icon senderIcon;
Mady Mellordf898fd2020-01-09 09:26:36 -080088 @Nullable public Drawable senderAvatar;
89 @Nullable public CharSequence senderName;
90 @Nullable public CharSequence message;
91 @Nullable public boolean isGroupChat;
92 }
93
94 private FlyoutMessage mFlyoutMessage;
Joshua Tsuji6855cab2020-04-16 01:05:39 -040095 private Drawable mBadgedAppIcon;
Lyn Hanb58c7562020-01-07 14:29:20 -080096 private Bitmap mBadgedImage;
97 private int mDotColor;
98 private Path mDotPath;
Pinyao Ting3c930612020-05-19 00:26:03 +000099 private int mFlags;
Mady Mellordf898fd2020-01-09 09:26:36 -0800100
Pinyao Tingb073b762020-06-04 11:15:03 -0700101 @NonNull
102 private UserHandle mUser;
103 @NonNull
104 private String mPackageName;
Pinyao Ting175a5b82020-06-15 23:41:14 +0000105 @Nullable
106 private String mTitle;
107 @Nullable
108 private Icon mIcon;
109 private boolean mIsBubble;
110 private boolean mIsVisuallyInterruptive;
111 private boolean mIsClearable;
112 private boolean mShouldSuppressNotificationDot;
113 private boolean mShouldSuppressNotificationList;
114 private boolean mShouldSuppressPeek;
Pinyao Tingb073b762020-06-04 11:15:03 -0700115 private int mDesiredHeight;
116 @DimenRes
117 private int mDesiredHeightResId;
118
Pinyao Ting175a5b82020-06-15 23:41:14 +0000119 /** for logging **/
120 @Nullable
121 private InstanceId mInstanceId;
122 @Nullable
123 private String mChannelId;
124 private int mNotificationId;
125 private int mAppUid = -1;
126
127 @Nullable
128 private PendingIntent mIntent;
129 @Nullable
130 private PendingIntent mDeleteIntent;
131
Pinyao Ting3c930612020-05-19 00:26:03 +0000132 /**
Pinyao Ting3c930612020-05-19 00:26:03 +0000133 * Create a bubble with limited information based on given {@link ShortcutInfo}.
134 * Note: Currently this is only being used when the bubble is persisted to disk.
135 */
Pinyao Tingb073b762020-06-04 11:15:03 -0700136 Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
Pinyao Ting175a5b82020-06-15 23:41:14 +0000137 final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
Pinyao Ting6a8fab02020-05-21 14:30:21 -0700138 Objects.requireNonNull(key);
139 Objects.requireNonNull(shortcutInfo);
Josh Tsuji050b48d2020-06-24 14:22:13 -0400140 mMetadataShortcutId = shortcutInfo.getId();
Pinyao Tingd4c1ba92020-05-04 20:29:56 -0700141 mShortcutInfo = shortcutInfo;
Pinyao Ting6a8fab02020-05-21 14:30:21 -0700142 mKey = key;
Pinyao Ting3c930612020-05-19 00:26:03 +0000143 mFlags = 0;
Pinyao Tingb073b762020-06-04 11:15:03 -0700144 mUser = shortcutInfo.getUserHandle();
145 mPackageName = shortcutInfo.getPackage();
Pinyao Ting175a5b82020-06-15 23:41:14 +0000146 mIcon = shortcutInfo.getIcon();
Pinyao Tingb073b762020-06-04 11:15:03 -0700147 mDesiredHeight = desiredHeight;
148 mDesiredHeightResId = desiredHeightResId;
Pinyao Ting175a5b82020-06-15 23:41:14 +0000149 mTitle = title;
Mady Mellor1c264ea2020-06-17 16:52:57 -0700150 mShowBubbleUpdateDot = false;
Pinyao Tingd4c1ba92020-05-04 20:29:56 -0700151 }
152
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400153 /** Used in tests when no UI is required. */
154 @VisibleForTesting(visibility = PRIVATE)
Pinyao Tingb073b762020-06-04 11:15:03 -0700155 Bubble(@NonNull final NotificationEntry e,
156 @Nullable final BubbleController.NotificationSuppressionChangedListener listener) {
157 Objects.requireNonNull(e);
Pinyao Ting6a8fab02020-05-21 14:30:21 -0700158 mKey = e.getKey();
Mady Mellorf44b6832020-01-14 13:26:14 -0800159 mSuppressionListener = listener;
Pinyao Tingb073b762020-06-04 11:15:03 -0700160 setEntry(e);
Mark Renouf85e0a902019-04-05 15:51:51 -0400161 }
162
Lyn Hancd4f87e2020-02-19 20:33:45 -0800163 @Override
Mark Renouf85e0a902019-04-05 15:51:51 -0400164 public String getKey() {
165 return mKey;
166 }
167
Pinyao Ting3c930612020-05-19 00:26:03 +0000168 public UserHandle getUser() {
Pinyao Tingb073b762020-06-04 11:15:03 -0700169 return mUser;
Pinyao Ting3c930612020-05-19 00:26:03 +0000170 }
171
Pinyao Tingb073b762020-06-04 11:15:03 -0700172 @NonNull
Mark Renouf71a3af62019-04-08 15:02:54 -0400173 public String getPackageName() {
Pinyao Tingb073b762020-06-04 11:15:03 -0700174 return mPackageName;
Mark Renouf71a3af62019-04-08 15:02:54 -0400175 }
176
Lyn Hancd4f87e2020-02-19 20:33:45 -0800177 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800178 public Bitmap getBadgedImage() {
179 return mBadgedImage;
180 }
181
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400182 public Drawable getBadgedAppIcon() {
183 return mBadgedAppIcon;
184 }
185
Lyn Hancd4f87e2020-02-19 20:33:45 -0800186 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800187 public int getDotColor() {
188 return mDotColor;
189 }
190
Lyn Hancd4f87e2020-02-19 20:33:45 -0800191 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800192 public Path getDotPath() {
193 return mDotPath;
194 }
195
Mady Mellor3df7ab02019-12-09 15:07:10 -0800196 @Nullable
Lyn Han6c40fe72019-05-08 14:06:33 -0700197 public String getAppName() {
198 return mAppName;
199 }
200
Mady Mellorb547e5e2019-12-02 10:15:56 -0800201 @Nullable
202 public ShortcutInfo getShortcutInfo() {
203 return mShortcutInfo;
204 }
205
Mady Mellor3df7ab02019-12-09 15:07:10 -0800206 @Nullable
Lyn Hancd4f87e2020-02-19 20:33:45 -0800207 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800208 public BadgedImageView getIconView() {
Mady Mellored99c272019-06-13 15:58:30 -0700209 return mIconView;
210 }
211
Lyn Hancd4f87e2020-02-19 20:33:45 -0800212 @Override
Mady Mellor3df7ab02019-12-09 15:07:10 -0800213 @Nullable
Lyn Han3cd75d72020-02-15 19:10:12 -0800214 public BubbleExpandedView getExpandedView() {
Mady Mellored99c272019-06-13 15:58:30 -0700215 return mExpandedView;
216 }
217
Pinyao Ting3c930612020-05-19 00:26:03 +0000218 @Nullable
219 public String getTitle() {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000220 return mTitle;
Pinyao Ting3c930612020-05-19 00:26:03 +0000221 }
222
Lyn Hanefbaa972020-06-23 14:59:39 -0700223 String getMetadataShortcutId() {
224 return mMetadataShortcutId;
225 }
226
227 boolean hasMetadataShortcutId() {
228 return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty());
229 }
230
Mady Mellore967e962020-03-26 17:36:44 -0700231 /**
232 * Call when the views should be removed, ensure this is called to clean up ActivityView
233 * content.
234 */
235 void cleanupViews() {
Mark Renoufc19b4732019-06-26 12:08:33 -0400236 if (mExpandedView != null) {
237 mExpandedView.cleanUpExpandedState();
Mady Mellore967e962020-03-26 17:36:44 -0700238 mExpandedView = null;
Mark Renoufc19b4732019-06-26 12:08:33 -0400239 }
Mady Mellore967e962020-03-26 17:36:44 -0700240 mIconView = null;
Mark Renoufc19b4732019-06-26 12:08:33 -0400241 }
242
Lyn Han6cb4e5f2020-04-27 15:11:18 -0700243 void setPendingIntentCanceled() {
244 mPendingIntentCanceled = true;
245 }
246
247 boolean getPendingIntentCanceled() {
248 return mPendingIntentCanceled;
249 }
250
Mady Mellor3df7ab02019-12-09 15:07:10 -0800251 /**
252 * Sets whether to perform inflation on the same thread as the caller. This method should only
253 * be used in tests, not in production.
254 */
255 @VisibleForTesting
256 void setInflateSynchronously(boolean inflateSynchronously) {
257 mInflateSynchronously = inflateSynchronously;
258 }
259
260 /**
261 * Starts a task to inflate & load any necessary information to display a bubble.
262 *
263 * @param callback the callback to notify one the bubble is ready to be displayed.
264 * @param context the context for the bubble.
265 * @param stackView the stackView the bubble is eventually added to.
266 * @param iconFactory the iconfactory use to create badged images for the bubble.
267 */
268 void inflate(BubbleViewInfoTask.Callback callback,
269 Context context,
270 BubbleStackView stackView,
Pinyao Ting3c930612020-05-19 00:26:03 +0000271 BubbleIconFactory iconFactory,
272 boolean skipInflation) {
Mady Mellor3df7ab02019-12-09 15:07:10 -0800273 if (isBubbleLoading()) {
274 mInflationTask.cancel(true /* mayInterruptIfRunning */);
Mark Renouf85e0a902019-04-05 15:51:51 -0400275 }
Mady Mellor3df7ab02019-12-09 15:07:10 -0800276 mInflationTask = new BubbleViewInfoTask(this,
277 context,
278 stackView,
279 iconFactory,
Pinyao Ting3c930612020-05-19 00:26:03 +0000280 skipInflation,
Mady Mellor3df7ab02019-12-09 15:07:10 -0800281 callback);
282 if (mInflateSynchronously) {
283 mInflationTask.onPostExecute(mInflationTask.doInBackground());
284 } else {
285 mInflationTask.execute();
286 }
287 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800288
Mady Mellor3df7ab02019-12-09 15:07:10 -0800289 private boolean isBubbleLoading() {
290 return mInflationTask != null && mInflationTask.getStatus() != FINISHED;
291 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700292
Mady Mellor3df7ab02019-12-09 15:07:10 -0800293 boolean isInflated() {
Mady Mellore967e962020-03-26 17:36:44 -0700294 return mIconView != null && mExpandedView != null;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800295 }
296
Lyn Han18cdc1c2020-03-18 19:12:42 -0700297 void stopInflation() {
298 if (mInflationTask == null) {
299 return;
300 }
Mady Mellore967e962020-03-26 17:36:44 -0700301 mInflationTask.cancel(true /* mayInterruptIfRunning */);
302 cleanupViews();
Lyn Han18cdc1c2020-03-18 19:12:42 -0700303 }
304
Mady Mellor3df7ab02019-12-09 15:07:10 -0800305 void setViewInfo(BubbleViewInfoTask.BubbleViewInfo info) {
306 if (!isInflated()) {
307 mIconView = info.imageView;
308 mExpandedView = info.expandedView;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800309 }
310
311 mShortcutInfo = info.shortcutInfo;
312 mAppName = info.appName;
Mady Mellordf898fd2020-01-09 09:26:36 -0800313 mFlyoutMessage = info.flyoutMessage;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800314
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400315 mBadgedAppIcon = info.badgedAppIcon;
Lyn Hanb58c7562020-01-07 14:29:20 -0800316 mBadgedImage = info.badgedBubbleImage;
317 mDotColor = info.dotColor;
318 mDotPath = info.dotPath;
319
Lyn Han18cdc1c2020-03-18 19:12:42 -0700320 if (mExpandedView != null) {
Mady Mellor458a6262020-06-07 21:09:19 -0700321 mExpandedView.update(this /* bubble */);
Lyn Han18cdc1c2020-03-18 19:12:42 -0700322 }
323 if (mIconView != null) {
Mady Mellor458a6262020-06-07 21:09:19 -0700324 mIconView.setRenderedBubble(this /* bubble */);
Lyn Han18cdc1c2020-03-18 19:12:42 -0700325 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800326 }
327
Issei Suzukicac2a502019-04-16 16:52:50 +0200328 /**
329 * Set visibility of bubble in the expanded state.
330 *
331 * @param visibility {@code true} if the expanded bubble should be visible on the screen.
332 *
333 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
334 * and setting {@code false} actually means rendering the expanded view in transparent.
335 */
Lyn Hancd4f87e2020-02-19 20:33:45 -0800336 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800337 public void setContentVisibility(boolean visibility) {
Mady Mellored99c272019-06-13 15:58:30 -0700338 if (mExpandedView != null) {
339 mExpandedView.setContentVisibility(visibility);
Issei Suzukicac2a502019-04-16 16:52:50 +0200340 }
341 }
342
Mady Mellor3df7ab02019-12-09 15:07:10 -0800343 /**
344 * Sets the entry associated with this bubble.
345 */
Pinyao Tingb073b762020-06-04 11:15:03 -0700346 void setEntry(@NonNull final NotificationEntry entry) {
347 Objects.requireNonNull(entry);
348 Objects.requireNonNull(entry.getSbn());
Ned Burns00b4b2d2019-10-17 22:09:27 -0400349 mLastUpdated = entry.getSbn().getPostTime();
Pinyao Ting175a5b82020-06-15 23:41:14 +0000350 mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
Pinyao Tingb073b762020-06-04 11:15:03 -0700351 mPackageName = entry.getSbn().getPackageName();
352 mUser = entry.getSbn().getUser();
Pinyao Ting175a5b82020-06-15 23:41:14 +0000353 mTitle = getTitle(entry);
354 mIsClearable = entry.isClearable();
355 mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
356 mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
357 mShouldSuppressPeek = entry.shouldSuppressPeek();
358 mChannelId = entry.getSbn().getNotification().getChannelId();
359 mNotificationId = entry.getSbn().getId();
360 mAppUid = entry.getSbn().getUid();
361 mInstanceId = entry.getSbn().getInstanceId();
362 mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
Lyn Hanefbaa972020-06-23 14:59:39 -0700363 mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null);
364 mMetadataShortcutId = (entry.getBubbleMetadata() != null
365 ? entry.getBubbleMetadata().getShortcutId() : null);
Pinyao Ting175a5b82020-06-15 23:41:14 +0000366 if (entry.getRanking() != null) {
367 mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
368 }
Pinyao Tingb073b762020-06-04 11:15:03 -0700369 if (entry.getBubbleMetadata() != null) {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000370 mFlags = entry.getBubbleMetadata().getFlags();
Pinyao Tingb073b762020-06-04 11:15:03 -0700371 mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
372 mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
Pinyao Ting175a5b82020-06-15 23:41:14 +0000373 mIcon = entry.getBubbleMetadata().getIcon();
374 mIntent = entry.getBubbleMetadata().getIntent();
375 mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
Pinyao Tingb073b762020-06-04 11:15:03 -0700376 }
Santhosh Thangarajc39224b2020-06-18 10:05:54 -0700377 mIsImportantConversation =
378 entry.getChannel() == null ? false : entry.getChannel().isImportantConversation();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800379 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400380
Pinyao Ting175a5b82020-06-15 23:41:14 +0000381 @Nullable
382 Icon getIcon() {
383 return mIcon;
384 }
385
386 boolean isVisuallyInterruptive() {
387 return mIsVisuallyInterruptive;
388 }
389
Mark Renoufba5ab512019-05-02 15:21:01 -0400390 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700391 * @return the last time this bubble was updated or accessed, whichever is most recent.
Mark Renoufba5ab512019-05-02 15:21:01 -0400392 */
Mady Mellor99a302602019-06-14 11:39:56 -0700393 long getLastActivity() {
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400394 return Math.max(mLastUpdated, mLastAccessed);
395 }
396
397 /**
Lyn Han6cb4e5f2020-04-27 15:11:18 -0700398 * @return if the bubble was ever expanded
399 */
400 boolean getWasAccessed() {
401 return mLastAccessed != 0L;
402 }
403
404 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200405 * @return the display id of the virtual display on which bubble contents is drawn.
406 */
Lyn Han9f66c3b2020-03-05 23:59:29 -0800407 @Override
408 public int getDisplayId() {
Mady Mellored99c272019-06-13 15:58:30 -0700409 return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
Issei Suzukicac2a502019-04-16 16:52:50 +0200410 }
411
Pinyao Ting175a5b82020-06-15 23:41:14 +0000412 public InstanceId getInstanceId() {
413 return mInstanceId;
414 }
415
416 @Nullable
417 public String getChannelId() {
418 return mChannelId;
419 }
420
421 public int getNotificationId() {
422 return mNotificationId;
423 }
424
Issei Suzukicac2a502019-04-16 16:52:50 +0200425 /**
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400426 * Should be invoked whenever a Bubble is accessed (selected while expanded).
427 */
428 void markAsAccessedAt(long lastAccessedMillis) {
429 mLastAccessed = lastAccessedMillis;
Mady Mellorf44b6832020-01-14 13:26:14 -0800430 setSuppressNotification(true);
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400431 setShowDot(false /* show */);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400432 }
433
434 /**
Lyn Hanb58c7562020-01-07 14:29:20 -0800435 * Should be invoked whenever a Bubble is promoted from overflow.
436 */
437 void markUpdatedAt(long lastAccessedMillis) {
438 mLastUpdated = lastAccessedMillis;
439 }
440
441 /**
Mady Mellorf44b6832020-01-14 13:26:14 -0800442 * Whether this notification should be shown in the shade.
Mady Mellorce23c462019-06-17 17:30:07 -0700443 */
Mady Mellorb8aaf972019-11-26 10:28:00 -0800444 boolean showInShade() {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000445 return !shouldSuppressNotification() || !mIsClearable;
Mady Mellorce23c462019-06-17 17:30:07 -0700446 }
447
448 /**
Santhosh Thangarajc39224b2020-06-18 10:05:54 -0700449 * Whether this notification conversation is important.
450 */
451 boolean isImportantConversation() {
452 return mIsImportantConversation;
453 }
454
455 /**
Mady Mellorf44b6832020-01-14 13:26:14 -0800456 * Sets whether this notification should be suppressed in the shade.
Mady Mellorce23c462019-06-17 17:30:07 -0700457 */
Mady Mellorf44b6832020-01-14 13:26:14 -0800458 void setSuppressNotification(boolean suppressNotification) {
459 boolean prevShowInShade = showInShade();
Mady Mellorf44b6832020-01-14 13:26:14 -0800460 if (suppressNotification) {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000461 mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
Mady Mellorf44b6832020-01-14 13:26:14 -0800462 } else {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000463 mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
Mady Mellorf44b6832020-01-14 13:26:14 -0800464 }
Mady Mellorf44b6832020-01-14 13:26:14 -0800465
466 if (showInShade() != prevShowInShade && mSuppressionListener != null) {
467 mSuppressionListener.onBubbleNotificationSuppressionChange(this);
468 }
Mady Mellorce23c462019-06-17 17:30:07 -0700469 }
470
471 /**
Mady Mellordf48d0a2019-06-25 18:26:46 -0700472 * Sets whether the bubble for this notification should show a dot indicating updated content.
473 */
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400474 void setShowDot(boolean showDot) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700475 mShowBubbleUpdateDot = showDot;
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400476
477 if (mIconView != null) {
478 mIconView.updateDotVisibility(true /* animate */);
Mady Mellorb8aaf972019-11-26 10:28:00 -0800479 }
Mady Mellordf48d0a2019-06-25 18:26:46 -0700480 }
481
482 /**
483 * Whether the bubble for this notification should show a dot indicating updated content.
484 */
Lyn Hancd4f87e2020-02-19 20:33:45 -0800485 @Override
486 public boolean showDot() {
Mady Mellorb25f9062019-12-09 12:12:57 -0800487 return mShowBubbleUpdateDot
Pinyao Ting175a5b82020-06-15 23:41:14 +0000488 && !mShouldSuppressNotificationDot
Mady Mellorb25f9062019-12-09 12:12:57 -0800489 && !shouldSuppressNotification();
Mady Mellordf48d0a2019-06-25 18:26:46 -0700490 }
491
492 /**
493 * Whether the flyout for the bubble should be shown.
494 */
Mady Mellorb8aaf972019-11-26 10:28:00 -0800495 boolean showFlyout() {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000496 return !mSuppressFlyout && !mShouldSuppressPeek
Mady Mellorb25f9062019-12-09 12:12:57 -0800497 && !shouldSuppressNotification()
Pinyao Ting175a5b82020-06-15 23:41:14 +0000498 && !mShouldSuppressNotificationList;
Mark Renoufc19b4732019-06-26 12:08:33 -0400499 }
500
501 /**
502 * Set whether the flyout text for the bubble should be shown when an update is received.
503 *
504 * @param suppressFlyout whether the flyout text is shown
505 */
506 void setSuppressFlyout(boolean suppressFlyout) {
507 mSuppressFlyout = suppressFlyout;
Mady Mellordf48d0a2019-06-25 18:26:46 -0700508 }
509
Mady Mellordf898fd2020-01-09 09:26:36 -0800510 FlyoutMessage getFlyoutMessage() {
511 return mFlyoutMessage;
512 }
513
Pinyao Tingb073b762020-06-04 11:15:03 -0700514 int getRawDesiredHeight() {
515 return mDesiredHeight;
516 }
517
518 int getRawDesiredHeightResId() {
519 return mDesiredHeightResId;
520 }
521
Mady Mellor99a302602019-06-14 11:39:56 -0700522 float getDesiredHeight(Context context) {
Pinyao Tingb073b762020-06-04 11:15:03 -0700523 boolean useRes = mDesiredHeightResId != 0;
Mady Mellor99a302602019-06-14 11:39:56 -0700524 if (useRes) {
Pinyao Tingb073b762020-06-04 11:15:03 -0700525 return getDimenForPackageUser(context, mDesiredHeightResId, mPackageName,
526 mUser.getIdentifier());
Mady Mellor99a302602019-06-14 11:39:56 -0700527 } else {
Pinyao Tingb073b762020-06-04 11:15:03 -0700528 return mDesiredHeight * context.getResources().getDisplayMetrics().density;
Mady Mellor99a302602019-06-14 11:39:56 -0700529 }
530 }
531
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700532 String getDesiredHeightString() {
Pinyao Tingb073b762020-06-04 11:15:03 -0700533 boolean useRes = mDesiredHeightResId != 0;
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700534 if (useRes) {
Pinyao Tingb073b762020-06-04 11:15:03 -0700535 return String.valueOf(mDesiredHeightResId);
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700536 } else {
Pinyao Tingb073b762020-06-04 11:15:03 -0700537 return String.valueOf(mDesiredHeight);
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700538 }
539 }
540
Pinyao Ting175a5b82020-06-15 23:41:14 +0000541 @Nullable
542 PendingIntent getBubbleIntent() {
543 return mIntent;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800544 }
545
Mady Mellor99a302602019-06-14 11:39:56 -0700546 @Nullable
Pinyao Ting175a5b82020-06-15 23:41:14 +0000547 PendingIntent getDeleteIntent() {
548 return mDeleteIntent;
Mady Mellor99a302602019-06-14 11:39:56 -0700549 }
550
Pinyao Ting3c930612020-05-19 00:26:03 +0000551 Intent getSettingsIntent(final Context context) {
Mady Mellor99a302602019-06-14 11:39:56 -0700552 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
553 intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
Pinyao Ting3c930612020-05-19 00:26:03 +0000554 final int uid = getUid(context);
555 if (uid != -1) {
556 intent.putExtra(Settings.EXTRA_APP_UID, uid);
557 }
Mady Mellor99a302602019-06-14 11:39:56 -0700558 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
559 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
560 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
561 return intent;
562 }
563
Pinyao Ting175a5b82020-06-15 23:41:14 +0000564 public int getAppUid() {
565 return mAppUid;
566 }
567
Pinyao Ting3c930612020-05-19 00:26:03 +0000568 private int getUid(final Context context) {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000569 if (mAppUid != -1) return mAppUid;
Pinyao Ting3c930612020-05-19 00:26:03 +0000570 final PackageManager pm = context.getPackageManager();
571 if (pm == null) return -1;
572 try {
573 final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0);
574 return info.uid;
575 } catch (PackageManager.NameNotFoundException e) {
576 Log.e(TAG, "cannot find uid", e);
577 }
578 return -1;
579 }
580
Mady Mellor99a302602019-06-14 11:39:56 -0700581 private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
582 PackageManager pm = context.getPackageManager();
583 Resources r;
584 if (pkg != null) {
585 try {
586 if (userId == UserHandle.USER_ALL) {
587 userId = UserHandle.USER_SYSTEM;
588 }
589 r = pm.getResourcesForApplicationAsUser(pkg, userId);
590 return r.getDimensionPixelSize(resId);
591 } catch (PackageManager.NameNotFoundException ex) {
592 // Uninstalled, don't care
593 } catch (Resources.NotFoundException e) {
594 // Invalid res id, return 0 and user our default
595 Log.e(TAG, "Couldn't find desired height res id", e);
596 }
597 }
598 return 0;
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400599 }
600
Mady Mellorce23c462019-06-17 17:30:07 -0700601 private boolean shouldSuppressNotification() {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000602 return isEnabled(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
Mady Mellorce23c462019-06-17 17:30:07 -0700603 }
604
Pinyao Ting175a5b82020-06-15 23:41:14 +0000605 public boolean shouldAutoExpand() {
606 return isEnabled(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
Mady Mellor76343012020-05-13 11:02:50 -0700607 }
608
609 void setShouldAutoExpand(boolean shouldAutoExpand) {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000610 if (shouldAutoExpand) {
611 enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
612 } else {
613 disable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
614 }
615 }
616
617 public void setIsBubble(final boolean isBubble) {
618 mIsBubble = isBubble;
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700619 }
620
Pinyao Ting3c930612020-05-19 00:26:03 +0000621 public boolean isBubble() {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000622 return mIsBubble;
Pinyao Ting3c930612020-05-19 00:26:03 +0000623 }
624
625 public void enable(int option) {
626 mFlags |= option;
627 }
628
629 public void disable(int option) {
630 mFlags &= ~option;
631 }
632
Pinyao Ting175a5b82020-06-15 23:41:14 +0000633 public boolean isEnabled(int option) {
634 return (mFlags & option) != 0;
635 }
636
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400637 @Override
638 public String toString() {
639 return "Bubble{" + mKey + '}';
640 }
641
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700642 /**
643 * Description of current bubble state.
644 */
645 public void dump(
646 @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
647 pw.print("key: "); pw.println(mKey);
Mady Mellorb8aaf972019-11-26 10:28:00 -0800648 pw.print(" showInShade: "); pw.println(showInShade());
649 pw.print(" showDot: "); pw.println(showDot());
650 pw.print(" showFlyout: "); pw.println(showFlyout());
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700651 pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
652 pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
653 pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
654 }
655
Mark Renouf71a3af62019-04-08 15:02:54 -0400656 @Override
657 public boolean equals(Object o) {
658 if (this == o) return true;
659 if (!(o instanceof Bubble)) return false;
660 Bubble bubble = (Bubble) o;
661 return Objects.equals(mKey, bubble.mKey);
662 }
663
664 @Override
665 public int hashCode() {
666 return Objects.hash(mKey);
667 }
Lyn Han3cd75d72020-02-15 19:10:12 -0800668
Lyn Hancd4f87e2020-02-19 20:33:45 -0800669 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800670 public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
Pinyao Ting175a5b82020-06-15 23:41:14 +0000671 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
672 mPackageName,
673 mChannelId,
674 mNotificationId,
675 index,
676 bubbleCount,
677 action,
678 normalX,
679 normalY,
680 showInShade(),
681 false /* isOngoing (unused) */,
682 false /* isAppForeground (unused) */);
683 }
684
685 @Nullable
686 private static String getTitle(@NonNull final NotificationEntry e) {
687 final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
688 Notification.EXTRA_TITLE);
689 return titleCharSeq == null ? null : titleCharSeq.toString();
Lyn Han3cd75d72020-02-15 19:10:12 -0800690 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800691}