blob: 9f3ff782211de7cc9935da8e0c4c4ffa41986498 [file] [log] [blame]
Mady Mellorc3d6f7d2018-11-07 09:36:56 -08001/*
2 * Copyright (C) 2018 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
17package com.android.systemui.bubbles;
18
Mady Mellord1c78b262018-11-06 18:04:40 -080019import static android.view.View.INVISIBLE;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080020import static android.view.View.VISIBLE;
21import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
22
23import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
24
Mady Mellor5549dd22018-11-06 18:07:34 -080025import android.app.Notification;
Mark Renouf89b1a4a2018-12-04 14:59:45 -050026import android.app.PendingIntent;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080027import android.content.Context;
Mark Renouf89b1a4a2018-12-04 14:59:45 -050028import android.content.pm.ActivityInfo;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080029import android.graphics.Point;
Mady Mellord1c78b262018-11-06 18:04:40 -080030import android.graphics.Rect;
Mady Mellorceced172018-11-27 11:18:39 -080031import android.provider.Settings;
Mady Mellor5549dd22018-11-06 18:07:34 -080032import android.service.notification.StatusBarNotification;
Mark Renouf89b1a4a2018-12-04 14:59:45 -050033import android.util.Log;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080034import android.view.ViewGroup;
35import android.view.WindowManager;
36import android.widget.FrameLayout;
37
Mark Renouf89b1a4a2018-12-04 14:59:45 -050038import androidx.annotation.Nullable;
39
Mady Mellorebdbbb92018-11-15 14:36:48 -080040import com.android.internal.annotations.VisibleForTesting;
Ned Burns01e38212019-01-03 16:32:52 -050041import com.android.systemui.Dependency;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080042import com.android.systemui.R;
Ned Burns01e38212019-01-03 16:32:52 -050043import com.android.systemui.statusbar.notification.NotificationEntryListener;
44import com.android.systemui.statusbar.notification.NotificationEntryManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050045import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080046import com.android.systemui.statusbar.phone.StatusBarWindowController;
47
Mady Mellor5549dd22018-11-06 18:07:34 -080048import java.util.ArrayList;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080049import java.util.HashMap;
50import java.util.Map;
51
Jason Monk27d01a622018-12-10 15:57:09 -050052import javax.inject.Inject;
53import javax.inject.Singleton;
54
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080055/**
56 * Bubbles are a special type of content that can "float" on top of other apps or System UI.
57 * Bubbles can be expanded to show more content.
58 *
59 * The controller manages addition, removal, and visible state of bubbles on screen.
60 */
Jason Monk27d01a622018-12-10 15:57:09 -050061@Singleton
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080062public class BubbleController {
63 private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
64
65 private static final String TAG = "BubbleController";
66
Mady Mellor5549dd22018-11-06 18:07:34 -080067 // Enables some subset of notifs to automatically become bubbles
Ned Burns01e38212019-01-03 16:32:52 -050068 private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
Mady Mellor5549dd22018-11-06 18:07:34 -080069 // When a bubble is dismissed, recreate it as a notification
Ned Burns01e38212019-01-03 16:32:52 -050070 private static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
Mady Mellor5549dd22018-11-06 18:07:34 -080071
Mady Mellorceced172018-11-27 11:18:39 -080072 // Secure settings
73 private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
74 private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
75 private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
Mark Renouf89b1a4a2018-12-04 14:59:45 -050076 private static final String ENABLE_BUBBLE_ACTIVITY_VIEW = "experiment_bubble_activity_view";
77 private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
Mady Mellorceced172018-11-27 11:18:39 -080078
Ned Burns01e38212019-01-03 16:32:52 -050079 private final Context mContext;
80 private final NotificationEntryManager mNotificationEntryManager;
Mady Mellord1c78b262018-11-06 18:04:40 -080081 private BubbleStateChangeListener mStateChangeListener;
Mady Mellorcd9b1302018-11-06 18:08:04 -080082 private BubbleExpandListener mExpandListener;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080083
Ned Burns01e38212019-01-03 16:32:52 -050084 private final Map<String, BubbleView> mBubbles = new HashMap<>();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080085 private BubbleStackView mStackView;
Ned Burns01e38212019-01-03 16:32:52 -050086 private final Point mDisplaySize;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080087
88 // Bubbles get added to the status bar view
Ned Burns01e38212019-01-03 16:32:52 -050089 private final StatusBarWindowController mStatusBarWindowController;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080090
Mady Mellord1c78b262018-11-06 18:04:40 -080091 // Used for determining view rect for touch interaction
92 private Rect mTempRect = new Rect();
93
Mady Mellor5549dd22018-11-06 18:07:34 -080094 /**
Mady Mellord1c78b262018-11-06 18:04:40 -080095 * Listener to be notified when some states of the bubbles change.
96 */
97 public interface BubbleStateChangeListener {
98 /**
99 * Called when the stack has bubbles or no longer has bubbles.
100 */
101 void onHasBubblesChanged(boolean hasBubbles);
102 }
103
Mady Mellorcd9b1302018-11-06 18:08:04 -0800104 /**
105 * Listener to find out about stack expansion / collapse events.
106 */
107 public interface BubbleExpandListener {
108 /**
109 * Called when the expansion state of the bubble stack changes.
110 *
111 * @param isExpanding whether it's expanding or collapsing
112 * @param amount fraction of how expanded or collapsed it is, 1 being fully, 0 at the start
113 */
114 void onBubbleExpandChanged(boolean isExpanding, float amount);
115 }
116
Jason Monk27d01a622018-12-10 15:57:09 -0500117 @Inject
Jason Monk92d5c242018-12-21 14:37:34 -0500118 public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800119 mContext = context;
Ned Burns01e38212019-01-03 16:32:52 -0500120 mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800121 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
122 mDisplaySize = new Point();
123 wm.getDefaultDisplay().getSize(mDisplaySize);
Jason Monk92d5c242018-12-21 14:37:34 -0500124 mStatusBarWindowController = statusBarWindowController;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800125
Ned Burns01e38212019-01-03 16:32:52 -0500126 mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
Mady Mellor5549dd22018-11-06 18:07:34 -0800127 }
128
129 /**
Mady Mellord1c78b262018-11-06 18:04:40 -0800130 * Set a listener to be notified when some states of the bubbles change.
131 */
132 public void setBubbleStateChangeListener(BubbleStateChangeListener listener) {
133 mStateChangeListener = listener;
134 }
135
136 /**
Mady Mellorcd9b1302018-11-06 18:08:04 -0800137 * Set a listener to be notified of bubble expand events.
138 */
139 public void setExpandListener(BubbleExpandListener listener) {
140 mExpandListener = listener;
141 if (mStackView != null) {
142 mStackView.setExpandListener(mExpandListener);
143 }
144 }
145
146 /**
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800147 * Whether or not there are bubbles present, regardless of them being visible on the
148 * screen (e.g. if on AOD).
149 */
150 public boolean hasBubbles() {
151 return mBubbles.size() > 0;
152 }
153
154 /**
155 * Whether the stack of bubbles is expanded or not.
156 */
157 public boolean isStackExpanded() {
158 return mStackView != null && mStackView.isExpanded();
159 }
160
161 /**
162 * Tell the stack of bubbles to collapse.
163 */
164 public void collapseStack() {
165 if (mStackView != null) {
166 mStackView.animateExpansion(false);
167 }
168 }
169
170 /**
171 * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
172 */
Ned Burns01e38212019-01-03 16:32:52 -0500173 void dismissStack() {
Mady Mellord1c78b262018-11-06 18:04:40 -0800174 if (mStackView == null) {
175 return;
176 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800177 Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
178 // Reset the position of the stack (TODO - or should we save / respect last user position?)
179 mStackView.setPosition(startPoint.x, startPoint.y);
180 for (String key: mBubbles.keySet()) {
181 removeBubble(key);
182 }
Ned Burns01e38212019-01-03 16:32:52 -0500183 mNotificationEntryManager.updateNotifications();
Mady Mellord1c78b262018-11-06 18:04:40 -0800184 updateBubblesShowing();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800185 }
186
187 /**
188 * Adds a bubble associated with the provided notification entry or updates it if it exists.
189 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500190 public void addBubble(NotificationEntry notif) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800191 if (mBubbles.containsKey(notif.key)) {
192 // It's an update
193 BubbleView bubble = mBubbles.get(notif.key);
194 mStackView.updateBubble(bubble, notif);
195 } else {
196 // It's new
197 BubbleView bubble = new BubbleView(mContext);
198 bubble.setNotif(notif);
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500199 if (shouldUseActivityView(mContext)) {
200 bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
201 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800202 mBubbles.put(bubble.getKey(), bubble);
203
Mady Mellord1c78b262018-11-06 18:04:40 -0800204 boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800205 if (mStackView == null) {
206 setPosition = true;
207 mStackView = new BubbleStackView(mContext);
Mady Mellord1c78b262018-11-06 18:04:40 -0800208 ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800209 // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
210 // between bubble and the shade
211 int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1;
212 sbv.addView(mStackView, bubblePosition,
213 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
Mady Mellorcd9b1302018-11-06 18:08:04 -0800214 if (mExpandListener != null) {
215 mStackView.setExpandListener(mExpandListener);
216 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800217 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800218 mStackView.addBubble(bubble);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800219 if (setPosition) {
220 // Need to add the bubble to the stack before we can know the width
221 Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
222 mStackView.setPosition(startPoint.x, startPoint.y);
Mady Mellord1c78b262018-11-06 18:04:40 -0800223 mStackView.setVisibility(VISIBLE);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800224 }
Mady Mellord1c78b262018-11-06 18:04:40 -0800225 updateBubblesShowing();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800226 }
227 }
228
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500229 @Nullable
230 private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
231 Notification notification = notif.notification.getNotification();
232 if (canLaunchInActivityView(notification.getAppOverlayIntent())) {
233 return notification.getAppOverlayIntent();
234 } else if (shouldUseContentIntent(mContext)
235 && canLaunchInActivityView(notification.contentIntent)) {
236 Log.d(TAG, "[addBubble " + notif.key
237 + "]: No appOverlayIntent, using contentIntent.");
238 return notification.contentIntent;
239 }
240 Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
241 return null;
242 }
243
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800244 /**
245 * Removes the bubble associated with the {@param uri}.
246 */
Ned Burns01e38212019-01-03 16:32:52 -0500247 void removeBubble(String key) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800248 BubbleView bv = mBubbles.get(key);
Mady Mellord1c78b262018-11-06 18:04:40 -0800249 if (mStackView != null && bv != null) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800250 mStackView.removeBubble(bv);
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500251 bv.destroyActivityView(mStackView);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800252 bv.getEntry().setBubbleDismissed(true);
253 }
Ned Burns01e38212019-01-03 16:32:52 -0500254
Ned Burnsf81c4c42019-01-07 14:10:43 -0500255 NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
Ned Burns01e38212019-01-03 16:32:52 -0500256 if (entry != null) {
257 entry.setBubbleDismissed(true);
258 if (!DEBUG_DEMOTE_TO_NOTIF) {
259 mNotificationEntryManager.performRemoveNotification(entry.notification);
260 }
Mady Mellor5549dd22018-11-06 18:07:34 -0800261 }
Ned Burns01e38212019-01-03 16:32:52 -0500262 mNotificationEntryManager.updateNotifications();
263
Mady Mellord1c78b262018-11-06 18:04:40 -0800264 updateBubblesShowing();
265 }
266
Ned Burns01e38212019-01-03 16:32:52 -0500267 @SuppressWarnings("FieldCanBeLocal")
268 private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
269 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500270 public void onPendingEntryAdded(NotificationEntry entry) {
Ned Burns01e38212019-01-03 16:32:52 -0500271 if (shouldAutoBubble(mContext, entry)) {
272 entry.setIsBubble(true);
273 }
274 }
275 };
276
Mady Mellord1c78b262018-11-06 18:04:40 -0800277 private void updateBubblesShowing() {
278 boolean hasBubblesShowing = false;
279 for (BubbleView bv : mBubbles.values()) {
280 if (!bv.getEntry().isBubbleDismissed()) {
281 hasBubblesShowing = true;
282 break;
283 }
284 }
285 boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
286 mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
287 if (mStackView != null && !hasBubblesShowing) {
288 mStackView.setVisibility(INVISIBLE);
289 }
290 if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
291 mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
292 }
Mady Mellor5549dd22018-11-06 18:07:34 -0800293 }
294
295 /**
296 * Sets the visibility of the bubbles, doesn't un-bubble them, just changes visibility.
297 */
298 public void updateVisibility(boolean visible) {
299 if (mStackView == null) {
300 return;
301 }
302 ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
303 for (BubbleView bv : mBubbles.values()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500304 NotificationEntry entry = bv.getEntry();
Mady Mellor5549dd22018-11-06 18:07:34 -0800305 if (entry != null) {
Evan Laird94492852018-10-25 13:43:01 -0400306 if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
Mady Mellor5549dd22018-11-06 18:07:34 -0800307 viewsToRemove.add(bv);
308 }
309 }
310 }
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500311 for (BubbleView bubbleView : viewsToRemove) {
312 mBubbles.remove(bubbleView.getKey());
313 mStackView.removeBubble(bubbleView);
314 bubbleView.destroyActivityView(mStackView);
Mady Mellor5549dd22018-11-06 18:07:34 -0800315 }
316 if (mStackView != null) {
Mady Mellord1c78b262018-11-06 18:04:40 -0800317 mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
318 if (!visible) {
319 collapseStack();
320 }
Mady Mellor5549dd22018-11-06 18:07:34 -0800321 }
Mady Mellord1c78b262018-11-06 18:04:40 -0800322 updateBubblesShowing();
323 }
324
325 /**
326 * Rect indicating the touchable region for the bubble stack / expanded stack.
327 */
328 public Rect getTouchableRegion() {
329 if (mStackView == null || mStackView.getVisibility() != VISIBLE) {
330 return null;
331 }
332 mStackView.getBoundsOnScreen(mTempRect);
333 return mTempRect;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800334 }
335
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500336 private boolean canLaunchInActivityView(PendingIntent intent) {
337 if (intent == null) {
338 return false;
339 }
340 ActivityInfo info =
341 intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
342 return info != null
343 && ActivityInfo.isResizeableMode(info.resizeMode)
344 && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
345 }
346
Mady Mellorebdbbb92018-11-15 14:36:48 -0800347 @VisibleForTesting
Ned Burns01e38212019-01-03 16:32:52 -0500348 BubbleStackView getStackView() {
Mady Mellorebdbbb92018-11-15 14:36:48 -0800349 return mStackView;
350 }
351
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800352 // TODO: factor in PIP location / maybe last place user had it
353 /**
354 * Gets an appropriate starting point to position the bubble stack.
355 */
Ned Burns01e38212019-01-03 16:32:52 -0500356 private static Point getStartPoint(int size, Point displaySize) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800357 final int x = displaySize.x - size + EDGE_OVERLAP;
358 final int y = displaySize.y / 4;
359 return new Point(x, y);
360 }
361
362 /**
363 * Gets an appropriate position for the bubble when the stack is expanded.
364 */
Ned Burns01e38212019-01-03 16:32:52 -0500365 static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800366 // Same place for now..
367 return new Point(EDGE_OVERLAP, size);
368 }
369
Mady Mellor5549dd22018-11-06 18:07:34 -0800370 /**
371 * Whether the notification should bubble or not.
372 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500373 private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
Mady Mellorceced172018-11-27 11:18:39 -0800374 if (entry.isBubbleDismissed()) {
Mady Mellor5549dd22018-11-06 18:07:34 -0800375 return false;
376 }
Mady Mellorceced172018-11-27 11:18:39 -0800377
378 boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
379 boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
380 boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
381
Mady Mellor5549dd22018-11-06 18:07:34 -0800382 StatusBarNotification n = entry.notification;
383 boolean hasRemoteInput = false;
384 if (n.getNotification().actions != null) {
385 for (Notification.Action action : n.getNotification().actions) {
386 if (action.getRemoteInputs() != null) {
387 hasRemoteInput = true;
388 break;
389 }
390 }
391 }
Mady Mellor711f9562018-12-05 14:53:46 -0800392 boolean isCall = Notification.CATEGORY_CALL.equals(n.getNotification().category)
393 && n.isOngoing();
394 boolean isMusic = n.getNotification().hasMediaSession();
395 boolean isImportantOngoing = isMusic || isCall;
Mady Mellorceced172018-11-27 11:18:39 -0800396
Mady Mellor5549dd22018-11-06 18:07:34 -0800397 Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
Mady Mellore3175372018-12-04 17:05:11 -0800398 boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category);
399 boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
400 return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
Mady Mellor711f9562018-12-05 14:53:46 -0800401 || (isImportantOngoing && autoBubbleOngoing)
Mady Mellorceced172018-11-27 11:18:39 -0800402 || autoBubbleAll;
403 }
404
405 private static boolean shouldAutoBubbleMessages(Context context) {
406 return Settings.Secure.getInt(context.getContentResolver(),
407 ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0;
408 }
409
410 private static boolean shouldAutoBubbleOngoing(Context context) {
411 return Settings.Secure.getInt(context.getContentResolver(),
412 ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0;
413 }
414
415 private static boolean shouldAutoBubbleAll(Context context) {
416 return Settings.Secure.getInt(context.getContentResolver(),
417 ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
Mady Mellor5549dd22018-11-06 18:07:34 -0800418 }
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500419
420 private static boolean shouldUseActivityView(Context context) {
421 return Settings.Secure.getInt(context.getContentResolver(),
422 ENABLE_BUBBLE_ACTIVITY_VIEW, 0) != 0;
423 }
424
425 private static boolean shouldUseContentIntent(Context context) {
426 return Settings.Secure.getInt(context.getContentResolver(),
427 ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
428 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800429}