blob: f1b401e77fbc2b7a69d6b114fdc2e36e0eb69995 [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
18
Mady Mellor3df7ab02019-12-09 15:07:10 -080019import static android.os.AsyncTask.Status.FINISHED;
Issei Suzukicac2a502019-04-16 16:52:50 +020020import static android.view.Display.INVALID_DISPLAY;
21
Mark Renouf9ba6cea2019-04-17 11:53:50 -040022import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
23
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;
Mady Mellorb547e5e2019-12-02 10:15:56 -080030import android.content.pm.LauncherApps;
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 Mellorb547e5e2019-12-02 10:15:56 -080036import android.graphics.Rect;
Mady Mellordf898fd2020-01-09 09:26:36 -080037import android.graphics.drawable.Drawable;
Mady Mellorb547e5e2019-12-02 10:15:56 -080038import android.os.Bundle;
Mark Renouf71a3af62019-04-08 15:02:54 -040039import android.os.UserHandle;
Mady Mellor99a302602019-06-14 11:39:56 -070040import android.provider.Settings;
Lyn Han3cd75d72020-02-15 19:10:12 -080041import android.service.notification.StatusBarNotification;
Mady Mellor99a302602019-06-14 11:39:56 -070042import android.util.Log;
Mady Mellor3dff9e62019-02-05 18:12:53 -080043
Mark Renouf9ba6cea2019-04-17 11:53:50 -040044import com.android.internal.annotations.VisibleForTesting;
Lyn Han3cd75d72020-02-15 19:10:12 -080045import com.android.systemui.shared.system.SysUiStatsLog;
Mady Mellor3dff9e62019-02-05 18:12:53 -080046import com.android.systemui.statusbar.notification.collection.NotificationEntry;
47
Mady Mellor70cba7bb2019-07-02 15:06:07 -070048import java.io.FileDescriptor;
49import java.io.PrintWriter;
Mark Renouf71a3af62019-04-08 15:02:54 -040050import java.util.Objects;
51
Mady Mellor3dff9e62019-02-05 18:12:53 -080052/**
53 * Encapsulates the data and UI elements of a bubble.
54 */
Lyn Han3cd75d72020-02-15 19:10:12 -080055class Bubble implements BubbleViewProvider {
Mady Mellor99a302602019-06-14 11:39:56 -070056 private static final String TAG = "Bubble";
57
58 private NotificationEntry mEntry;
Mark Renouf85e0a902019-04-05 15:51:51 -040059 private final String mKey;
Mark Renouf71a3af62019-04-08 15:02:54 -040060 private final String mGroupId;
Mady Mellor99a302602019-06-14 11:39:56 -070061
Mark Renouf9ba6cea2019-04-17 11:53:50 -040062 private long mLastUpdated;
63 private long mLastAccessed;
Mady Mellor7f234902019-10-20 12:06:29 -070064
Mady Mellorf44b6832020-01-14 13:26:14 -080065 private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
66
67 /** Whether the bubble should show a dot for the notification indicating updated content. */
68 private boolean mShowBubbleUpdateDot = true;
69
70 /** Whether flyout text should be suppressed, regardless of any other flags or state. */
71 private boolean mSuppressFlyout;
72
Mady Mellor3df7ab02019-12-09 15:07:10 -080073 // Items that are typically loaded later
74 private String mAppName;
75 private ShortcutInfo mShortcutInfo;
76 private BadgedImageView mIconView;
77 private BubbleExpandedView mExpandedView;
78
Mady Mellor3df7ab02019-12-09 15:07:10 -080079 private BubbleViewInfoTask mInflationTask;
80 private boolean mInflateSynchronously;
Lyn Han6cb4e5f2020-04-27 15:11:18 -070081 private boolean mPendingIntentCanceled;
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 {
87 @Nullable public Drawable senderAvatar;
88 @Nullable public CharSequence senderName;
89 @Nullable public CharSequence message;
90 @Nullable public boolean isGroupChat;
91 }
92
93 private FlyoutMessage mFlyoutMessage;
Joshua Tsuji6855cab2020-04-16 01:05:39 -040094 private Drawable mBadgedAppIcon;
Lyn Hanb58c7562020-01-07 14:29:20 -080095 private Bitmap mBadgedImage;
96 private int mDotColor;
97 private Path mDotPath;
Mady Mellordf898fd2020-01-09 09:26:36 -080098
Lyn Hancd4f87e2020-02-19 20:33:45 -080099
Mark Renoufba5ab512019-05-02 15:21:01 -0400100 public static String groupId(NotificationEntry entry) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400101 UserHandle user = entry.getSbn().getUser();
102 return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400103 }
104
Pinyao Tingd4c1ba92020-05-04 20:29:56 -0700105 // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble
106 Bubble(ShortcutInfo shortcutInfo) {
107 mShortcutInfo = shortcutInfo;
108 mKey = shortcutInfo.getId();
109 mGroupId = shortcutInfo.getId();
110 }
111
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400112 /** Used in tests when no UI is required. */
113 @VisibleForTesting(visibility = PRIVATE)
Mady Mellorf44b6832020-01-14 13:26:14 -0800114 Bubble(NotificationEntry e,
115 BubbleController.NotificationSuppressionChangedListener listener) {
Mady Mellored99c272019-06-13 15:58:30 -0700116 mEntry = e;
Ned Burns00b4b2d2019-10-17 22:09:27 -0400117 mKey = e.getKey();
118 mLastUpdated = e.getSbn().getPostTime();
Mark Renouf71a3af62019-04-08 15:02:54 -0400119 mGroupId = groupId(e);
Mady Mellorf44b6832020-01-14 13:26:14 -0800120 mSuppressionListener = listener;
Mark Renouf85e0a902019-04-05 15:51:51 -0400121 }
122
Lyn Hancd4f87e2020-02-19 20:33:45 -0800123 @Override
Mark Renouf85e0a902019-04-05 15:51:51 -0400124 public String getKey() {
125 return mKey;
126 }
127
Mady Mellored99c272019-06-13 15:58:30 -0700128 public NotificationEntry getEntry() {
129 return mEntry;
130 }
131
Mark Renouf71a3af62019-04-08 15:02:54 -0400132 public String getGroupId() {
133 return mGroupId;
134 }
135
136 public String getPackageName() {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400137 return mEntry.getSbn().getPackageName();
Mark Renouf71a3af62019-04-08 15:02:54 -0400138 }
139
Lyn Hancd4f87e2020-02-19 20:33:45 -0800140 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800141 public Bitmap getBadgedImage() {
142 return mBadgedImage;
143 }
144
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400145 public Drawable getBadgedAppIcon() {
146 return mBadgedAppIcon;
147 }
148
Lyn Hancd4f87e2020-02-19 20:33:45 -0800149 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800150 public int getDotColor() {
151 return mDotColor;
152 }
153
Lyn Hancd4f87e2020-02-19 20:33:45 -0800154 @Override
Lyn Hanb58c7562020-01-07 14:29:20 -0800155 public Path getDotPath() {
156 return mDotPath;
157 }
158
Mady Mellor3df7ab02019-12-09 15:07:10 -0800159 @Nullable
Lyn Han6c40fe72019-05-08 14:06:33 -0700160 public String getAppName() {
161 return mAppName;
162 }
163
Mady Mellorb547e5e2019-12-02 10:15:56 -0800164 @Nullable
165 public ShortcutInfo getShortcutInfo() {
166 return mShortcutInfo;
167 }
168
Mady Mellor3df7ab02019-12-09 15:07:10 -0800169 @Nullable
Lyn Hancd4f87e2020-02-19 20:33:45 -0800170 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800171 public BadgedImageView getIconView() {
Mady Mellored99c272019-06-13 15:58:30 -0700172 return mIconView;
173 }
174
Lyn Hancd4f87e2020-02-19 20:33:45 -0800175 @Override
Mady Mellor3df7ab02019-12-09 15:07:10 -0800176 @Nullable
Lyn Han3cd75d72020-02-15 19:10:12 -0800177 public BubbleExpandedView getExpandedView() {
Mady Mellored99c272019-06-13 15:58:30 -0700178 return mExpandedView;
179 }
180
Mady Mellore967e962020-03-26 17:36:44 -0700181 /**
182 * Call when the views should be removed, ensure this is called to clean up ActivityView
183 * content.
184 */
185 void cleanupViews() {
Mark Renoufc19b4732019-06-26 12:08:33 -0400186 if (mExpandedView != null) {
187 mExpandedView.cleanUpExpandedState();
Mady Mellore967e962020-03-26 17:36:44 -0700188 mExpandedView = null;
Mark Renoufc19b4732019-06-26 12:08:33 -0400189 }
Mady Mellore967e962020-03-26 17:36:44 -0700190 mIconView = null;
Mark Renoufc19b4732019-06-26 12:08:33 -0400191 }
192
Lyn Han6cb4e5f2020-04-27 15:11:18 -0700193 void setPendingIntentCanceled() {
194 mPendingIntentCanceled = true;
195 }
196
197 boolean getPendingIntentCanceled() {
198 return mPendingIntentCanceled;
199 }
200
Mady Mellor3df7ab02019-12-09 15:07:10 -0800201 /**
202 * Sets whether to perform inflation on the same thread as the caller. This method should only
203 * be used in tests, not in production.
204 */
205 @VisibleForTesting
206 void setInflateSynchronously(boolean inflateSynchronously) {
207 mInflateSynchronously = inflateSynchronously;
208 }
209
210 /**
211 * Starts a task to inflate & load any necessary information to display a bubble.
212 *
213 * @param callback the callback to notify one the bubble is ready to be displayed.
214 * @param context the context for the bubble.
215 * @param stackView the stackView the bubble is eventually added to.
216 * @param iconFactory the iconfactory use to create badged images for the bubble.
217 */
218 void inflate(BubbleViewInfoTask.Callback callback,
219 Context context,
220 BubbleStackView stackView,
221 BubbleIconFactory iconFactory) {
222 if (isBubbleLoading()) {
223 mInflationTask.cancel(true /* mayInterruptIfRunning */);
Mark Renouf85e0a902019-04-05 15:51:51 -0400224 }
Mady Mellor3df7ab02019-12-09 15:07:10 -0800225 mInflationTask = new BubbleViewInfoTask(this,
226 context,
227 stackView,
228 iconFactory,
229 callback);
230 if (mInflateSynchronously) {
231 mInflationTask.onPostExecute(mInflationTask.doInBackground());
232 } else {
233 mInflationTask.execute();
234 }
235 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800236
Mady Mellor3df7ab02019-12-09 15:07:10 -0800237 private boolean isBubbleLoading() {
238 return mInflationTask != null && mInflationTask.getStatus() != FINISHED;
239 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700240
Mady Mellor3df7ab02019-12-09 15:07:10 -0800241 boolean isInflated() {
Mady Mellore967e962020-03-26 17:36:44 -0700242 return mIconView != null && mExpandedView != null;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800243 }
244
Lyn Han18cdc1c2020-03-18 19:12:42 -0700245 void stopInflation() {
246 if (mInflationTask == null) {
247 return;
248 }
Mady Mellore967e962020-03-26 17:36:44 -0700249 mInflationTask.cancel(true /* mayInterruptIfRunning */);
250 cleanupViews();
Lyn Han18cdc1c2020-03-18 19:12:42 -0700251 }
252
Mady Mellor3df7ab02019-12-09 15:07:10 -0800253 void setViewInfo(BubbleViewInfoTask.BubbleViewInfo info) {
254 if (!isInflated()) {
255 mIconView = info.imageView;
256 mExpandedView = info.expandedView;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800257 }
258
259 mShortcutInfo = info.shortcutInfo;
260 mAppName = info.appName;
Mady Mellordf898fd2020-01-09 09:26:36 -0800261 mFlyoutMessage = info.flyoutMessage;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800262
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400263 mBadgedAppIcon = info.badgedAppIcon;
Lyn Hanb58c7562020-01-07 14:29:20 -0800264 mBadgedImage = info.badgedBubbleImage;
265 mDotColor = info.dotColor;
266 mDotPath = info.dotPath;
267
Lyn Han18cdc1c2020-03-18 19:12:42 -0700268 if (mExpandedView != null) {
269 mExpandedView.update(/* bubble */ this);
270 }
271 if (mIconView != null) {
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400272 mIconView.setRenderedBubble(/* bubble */ this);
Lyn Han18cdc1c2020-03-18 19:12:42 -0700273 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800274 }
275
Issei Suzukicac2a502019-04-16 16:52:50 +0200276 /**
277 * Set visibility of bubble in the expanded state.
278 *
279 * @param visibility {@code true} if the expanded bubble should be visible on the screen.
280 *
281 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
282 * and setting {@code false} actually means rendering the expanded view in transparent.
283 */
Lyn Hancd4f87e2020-02-19 20:33:45 -0800284 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800285 public void setContentVisibility(boolean visibility) {
Mady Mellored99c272019-06-13 15:58:30 -0700286 if (mExpandedView != null) {
287 mExpandedView.setContentVisibility(visibility);
Issei Suzukicac2a502019-04-16 16:52:50 +0200288 }
289 }
290
Mady Mellor3df7ab02019-12-09 15:07:10 -0800291 /**
292 * Sets the entry associated with this bubble.
293 */
294 void setEntry(NotificationEntry entry) {
Mady Mellor99a302602019-06-14 11:39:56 -0700295 mEntry = entry;
Ned Burns00b4b2d2019-10-17 22:09:27 -0400296 mLastUpdated = entry.getSbn().getPostTime();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800297 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400298
Mark Renoufba5ab512019-05-02 15:21:01 -0400299 /**
300 * @return the newer of {@link #getLastUpdateTime()} and {@link #getLastAccessTime()}
301 */
Mady Mellor99a302602019-06-14 11:39:56 -0700302 long getLastActivity() {
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400303 return Math.max(mLastUpdated, mLastAccessed);
304 }
305
306 /**
Mark Renoufba5ab512019-05-02 15:21:01 -0400307 * @return the timestamp in milliseconds of the most recent notification entry for this bubble
308 */
Mady Mellor99a302602019-06-14 11:39:56 -0700309 long getLastUpdateTime() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400310 return mLastUpdated;
311 }
312
313 /**
Lyn Han6cb4e5f2020-04-27 15:11:18 -0700314 * @return if the bubble was ever expanded
315 */
316 boolean getWasAccessed() {
317 return mLastAccessed != 0L;
318 }
319
320 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200321 * @return the display id of the virtual display on which bubble contents is drawn.
322 */
Lyn Han9f66c3b2020-03-05 23:59:29 -0800323 @Override
324 public int getDisplayId() {
Mady Mellored99c272019-06-13 15:58:30 -0700325 return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
Issei Suzukicac2a502019-04-16 16:52:50 +0200326 }
327
328 /**
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400329 * Should be invoked whenever a Bubble is accessed (selected while expanded).
330 */
331 void markAsAccessedAt(long lastAccessedMillis) {
332 mLastAccessed = lastAccessedMillis;
Mady Mellorf44b6832020-01-14 13:26:14 -0800333 setSuppressNotification(true);
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400334 setShowDot(false /* show */);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400335 }
336
337 /**
Lyn Hanb58c7562020-01-07 14:29:20 -0800338 * Should be invoked whenever a Bubble is promoted from overflow.
339 */
340 void markUpdatedAt(long lastAccessedMillis) {
341 mLastUpdated = lastAccessedMillis;
342 }
343
344 /**
Mady Mellorf44b6832020-01-14 13:26:14 -0800345 * Whether this notification should be shown in the shade.
Mady Mellorce23c462019-06-17 17:30:07 -0700346 */
Mady Mellorb8aaf972019-11-26 10:28:00 -0800347 boolean showInShade() {
Mady Mellorf44b6832020-01-14 13:26:14 -0800348 return !shouldSuppressNotification() || !mEntry.isClearable();
Mady Mellorce23c462019-06-17 17:30:07 -0700349 }
350
351 /**
Mady Mellorf44b6832020-01-14 13:26:14 -0800352 * Sets whether this notification should be suppressed in the shade.
Mady Mellorce23c462019-06-17 17:30:07 -0700353 */
Mady Mellorf44b6832020-01-14 13:26:14 -0800354 void setSuppressNotification(boolean suppressNotification) {
355 boolean prevShowInShade = showInShade();
356
357 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
358 int flags = data.getFlags();
359 if (suppressNotification) {
360 flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
361 } else {
362 flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
363 }
364 data.setFlags(flags);
365
366 if (showInShade() != prevShowInShade && mSuppressionListener != null) {
367 mSuppressionListener.onBubbleNotificationSuppressionChange(this);
368 }
Mady Mellorce23c462019-06-17 17:30:07 -0700369 }
370
371 /**
Mady Mellordf48d0a2019-06-25 18:26:46 -0700372 * Sets whether the bubble for this notification should show a dot indicating updated content.
373 */
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400374 void setShowDot(boolean showDot) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700375 mShowBubbleUpdateDot = showDot;
Joshua Tsuji2ed260e2020-03-26 14:26:01 -0400376
377 if (mIconView != null) {
378 mIconView.updateDotVisibility(true /* animate */);
Mady Mellorb8aaf972019-11-26 10:28:00 -0800379 }
Mady Mellordf48d0a2019-06-25 18:26:46 -0700380 }
381
382 /**
383 * Whether the bubble for this notification should show a dot indicating updated content.
384 */
Lyn Hancd4f87e2020-02-19 20:33:45 -0800385 @Override
386 public boolean showDot() {
Mady Mellorb25f9062019-12-09 12:12:57 -0800387 return mShowBubbleUpdateDot
388 && !mEntry.shouldSuppressNotificationDot()
389 && !shouldSuppressNotification();
Mady Mellordf48d0a2019-06-25 18:26:46 -0700390 }
391
392 /**
393 * Whether the flyout for the bubble should be shown.
394 */
Mady Mellorb8aaf972019-11-26 10:28:00 -0800395 boolean showFlyout() {
Mark Renoufc19b4732019-06-26 12:08:33 -0400396 return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
Mady Mellorb25f9062019-12-09 12:12:57 -0800397 && !shouldSuppressNotification()
Mark Renoufc19b4732019-06-26 12:08:33 -0400398 && !mEntry.shouldSuppressNotificationList();
399 }
400
401 /**
402 * Set whether the flyout text for the bubble should be shown when an update is received.
403 *
404 * @param suppressFlyout whether the flyout text is shown
405 */
406 void setSuppressFlyout(boolean suppressFlyout) {
407 mSuppressFlyout = suppressFlyout;
Mady Mellordf48d0a2019-06-25 18:26:46 -0700408 }
409
Mady Mellordf898fd2020-01-09 09:26:36 -0800410 FlyoutMessage getFlyoutMessage() {
411 return mFlyoutMessage;
412 }
413
Mady Mellordf48d0a2019-06-25 18:26:46 -0700414 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700415 * Returns whether the notification for this bubble is a foreground service. It shows that this
416 * is an ongoing bubble.
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400417 */
Mady Mellor99a302602019-06-14 11:39:56 -0700418 boolean isOngoing() {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400419 int flags = mEntry.getSbn().getNotification().flags;
Mady Mellor99a302602019-06-14 11:39:56 -0700420 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
421 }
422
423 float getDesiredHeight(Context context) {
424 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
425 boolean useRes = data.getDesiredHeightResId() != 0;
426 if (useRes) {
427 return getDimenForPackageUser(context, data.getDesiredHeightResId(),
Ned Burns00b4b2d2019-10-17 22:09:27 -0400428 mEntry.getSbn().getPackageName(),
429 mEntry.getSbn().getUser().getIdentifier());
Mady Mellor99a302602019-06-14 11:39:56 -0700430 } else {
431 return data.getDesiredHeight()
432 * context.getResources().getDisplayMetrics().density;
433 }
434 }
435
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700436 String getDesiredHeightString() {
437 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
438 boolean useRes = data.getDesiredHeightResId() != 0;
439 if (useRes) {
440 return String.valueOf(data.getDesiredHeightResId());
441 } else {
442 return String.valueOf(data.getDesiredHeight());
443 }
444 }
445
Mady Mellor3df7ab02019-12-09 15:07:10 -0800446 /**
447 * Whether shortcut information should be used to populate the bubble.
448 * <p>
449 * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
450 * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
451 */
452 boolean usingShortcutInfo() {
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800453 return mEntry.getBubbleMetadata().getShortcutId() != null;
Mady Mellor3df7ab02019-12-09 15:07:10 -0800454 }
455
Mady Mellor99a302602019-06-14 11:39:56 -0700456 @Nullable
Mady Mellorb547e5e2019-12-02 10:15:56 -0800457 PendingIntent getBubbleIntent() {
Mady Mellor7f234902019-10-20 12:06:29 -0700458 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
459 if (data != null) {
Mady Melloraa9ce172020-03-17 10:34:20 -0700460 return data.getIntent();
Mady Mellor99a302602019-06-14 11:39:56 -0700461 }
462 return null;
463 }
464
465 Intent getSettingsIntent() {
466 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
467 intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
Ned Burns00b4b2d2019-10-17 22:09:27 -0400468 intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid());
Mady Mellor99a302602019-06-14 11:39:56 -0700469 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
470 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
471 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
472 return intent;
473 }
474
Mady Mellor99a302602019-06-14 11:39:56 -0700475 private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
476 PackageManager pm = context.getPackageManager();
477 Resources r;
478 if (pkg != null) {
479 try {
480 if (userId == UserHandle.USER_ALL) {
481 userId = UserHandle.USER_SYSTEM;
482 }
483 r = pm.getResourcesForApplicationAsUser(pkg, userId);
484 return r.getDimensionPixelSize(resId);
485 } catch (PackageManager.NameNotFoundException ex) {
486 // Uninstalled, don't care
487 } catch (Resources.NotFoundException e) {
488 // Invalid res id, return 0 and user our default
489 Log.e(TAG, "Couldn't find desired height res id", e);
490 }
491 }
492 return 0;
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400493 }
494
Mady Mellorce23c462019-06-17 17:30:07 -0700495 private boolean shouldSuppressNotification() {
496 return mEntry.getBubbleMetadata() != null
497 && mEntry.getBubbleMetadata().isNotificationSuppressed();
498 }
499
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700500 boolean shouldAutoExpand() {
501 Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
502 return metadata != null && metadata.getAutoExpandBubble();
503 }
504
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400505 @Override
506 public String toString() {
507 return "Bubble{" + mKey + '}';
508 }
509
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700510 /**
511 * Description of current bubble state.
512 */
513 public void dump(
514 @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
515 pw.print("key: "); pw.println(mKey);
Mady Mellorb8aaf972019-11-26 10:28:00 -0800516 pw.print(" showInShade: "); pw.println(showInShade());
517 pw.print(" showDot: "); pw.println(showDot());
518 pw.print(" showFlyout: "); pw.println(showFlyout());
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700519 pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
520 pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
521 pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
522 }
523
Mark Renouf71a3af62019-04-08 15:02:54 -0400524 @Override
525 public boolean equals(Object o) {
526 if (this == o) return true;
527 if (!(o instanceof Bubble)) return false;
528 Bubble bubble = (Bubble) o;
529 return Objects.equals(mKey, bubble.mKey);
530 }
531
532 @Override
533 public int hashCode() {
534 return Objects.hash(mKey);
535 }
Lyn Han3cd75d72020-02-15 19:10:12 -0800536
Lyn Hancd4f87e2020-02-19 20:33:45 -0800537 @Override
Lyn Han3cd75d72020-02-15 19:10:12 -0800538 public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
539 if (this.getEntry() == null
540 || this.getEntry().getSbn() == null) {
541 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
542 null /* package name */,
543 null /* notification channel */,
544 0 /* notification ID */,
545 0 /* bubble position */,
546 bubbleCount,
547 action,
548 normalX,
549 normalY,
550 false /* unread bubble */,
551 false /* on-going bubble */,
552 false /* isAppForeground (unused) */);
553 } else {
554 StatusBarNotification notification = this.getEntry().getSbn();
555 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
556 notification.getPackageName(),
557 notification.getNotification().getChannelId(),
558 notification.getId(),
559 index,
560 bubbleCount,
561 action,
562 normalX,
563 normalY,
564 this.showInShade(),
565 this.isOngoing(),
566 false /* isAppForeground (unused) */);
567 }
568 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800569}