blob: 6e2bcc185d7e49ddc748759131344efd8ad31d60 [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 Mellor3a0a1b42019-05-23 06:40:21 -070019import static android.app.Notification.FLAG_BUBBLE;
Mady Mellorca0c24c2019-05-16 16:14:32 -070020import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
Mady Mellorc2ff0112019-03-28 14:18:06 -070021import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
22import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
23import static android.service.notification.NotificationListenerService.REASON_CANCEL;
24import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
Mady Mellor390bff42019-04-05 15:09:01 -070025import static android.view.Display.DEFAULT_DISPLAY;
26import static android.view.Display.INVALID_DISPLAY;
Mady Mellord1c78b262018-11-06 18:04:40 -080027import static android.view.View.INVISIBLE;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080028import static android.view.View.VISIBLE;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080029import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080030
Issei Suzukia8d07312019-06-07 12:56:19 +020031import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
32import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
33import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellor3f2efdb2018-11-21 11:30:45 -080034import static com.android.systemui.statusbar.StatusBarState.SHADE;
Mady Mellor1a4e86f2019-05-03 16:07:23 -070035import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080036
Mark Renoufba5ab512019-05-02 15:21:01 -040037import static java.lang.annotation.ElementType.FIELD;
38import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
39import static java.lang.annotation.ElementType.PARAMETER;
Mark Renouf08bc42a2019-03-07 13:01:59 -050040import static java.lang.annotation.RetentionPolicy.SOURCE;
41
Mark Renoufc808f062019-02-07 15:20:37 -050042import android.app.ActivityManager.RunningTaskInfo;
Mady Mellor66efd5e2019-05-15 13:38:11 -070043import android.app.NotificationManager;
Mady Mellorca0c24c2019-05-16 16:14:32 -070044import android.app.PendingIntent;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080045import android.content.Context;
Mady Mellorca0c24c2019-05-16 16:14:32 -070046import android.content.pm.ActivityInfo;
Joshua Tsujia19515f2019-02-13 18:02:29 -050047import android.content.pm.ParceledListSlice;
Joshua Tsujif418f9e2019-04-04 17:09:53 -040048import android.content.res.Configuration;
Mady Mellord1c78b262018-11-06 18:04:40 -080049import android.graphics.Rect;
Mark Renoufcecc77b2019-01-30 16:32:24 -050050import android.os.RemoteException;
Mady Mellorb4991e62019-01-10 15:14:51 -080051import android.os.ServiceManager;
Mady Mellorceced172018-11-27 11:18:39 -080052import android.provider.Settings;
Mark Renoufbbcf07f2019-05-09 10:42:43 -040053import android.service.notification.NotificationListenerService.RankingMap;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -040054import android.service.notification.ZenModeConfig;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040055import android.util.Log;
Mark Renouf82a40e62019-05-23 16:16:24 -040056import android.util.Pair;
Mark Renoufcecc77b2019-01-30 16:32:24 -050057import android.view.Display;
Joshua Tsujia19515f2019-02-13 18:02:29 -050058import android.view.IPinnedStackController;
59import android.view.IPinnedStackListener;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080060import android.view.ViewGroup;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080061import android.widget.FrameLayout;
62
Mark Renouf08bc42a2019-03-07 13:01:59 -050063import androidx.annotation.IntDef;
Mark Renouf658c6bc2019-01-30 10:26:54 -050064import androidx.annotation.MainThread;
Joshua Tsujic650a142019-05-22 11:31:19 -040065import androidx.annotation.Nullable;
Mark Renouf658c6bc2019-01-30 10:26:54 -050066
Mady Mellorebdbbb92018-11-15 14:36:48 -080067import com.android.internal.annotations.VisibleForTesting;
Mady Mellora54e9fa2019-04-18 13:26:18 -070068import com.android.internal.statusbar.IStatusBarService;
Ned Burns01e38212019-01-03 16:32:52 -050069import com.android.systemui.Dependency;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080070import com.android.systemui.R;
Beverly8fdb5332019-02-04 14:29:49 -050071import com.android.systemui.plugins.statusbar.StatusBarStateController;
Mark Renoufcecc77b2019-01-30 16:32:24 -050072import com.android.systemui.shared.system.ActivityManagerWrapper;
73import com.android.systemui.shared.system.TaskStackChangeListener;
Joshua Tsujia19515f2019-02-13 18:02:29 -050074import com.android.systemui.shared.system.WindowManagerWrapper;
Mady Mellorc2ff0112019-03-28 14:18:06 -070075import com.android.systemui.statusbar.NotificationRemoveInterceptor;
Ned Burns01e38212019-01-03 16:32:52 -050076import com.android.systemui.statusbar.notification.NotificationEntryListener;
77import com.android.systemui.statusbar.notification.NotificationEntryManager;
Mady Mellor3f2efdb2018-11-21 11:30:45 -080078import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
Ned Burnsf81c4c42019-01-07 14:10:43 -050079import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080080import com.android.systemui.statusbar.phone.StatusBarWindowController;
Lyn Hanf1c9b8b2019-03-14 16:49:48 -070081import com.android.systemui.statusbar.policy.ConfigurationController;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -040082import com.android.systemui.statusbar.policy.ZenModeController;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080083
Mady Mellor70cba7bb2019-07-02 15:06:07 -070084import java.io.FileDescriptor;
85import java.io.PrintWriter;
Mark Renouf08bc42a2019-03-07 13:01:59 -050086import java.lang.annotation.Retention;
Mark Renoufba5ab512019-05-02 15:21:01 -040087import java.lang.annotation.Target;
Mady Mellore80930e2019-03-21 16:00:45 -070088import java.util.List;
Mark Renouf08bc42a2019-03-07 13:01:59 -050089
Jason Monk27d01a622018-12-10 15:57:09 -050090import javax.inject.Inject;
91import javax.inject.Singleton;
92
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080093/**
94 * Bubbles are a special type of content that can "float" on top of other apps or System UI.
95 * Bubbles can be expanded to show more content.
96 *
97 * The controller manages addition, removal, and visible state of bubbles on screen.
98 */
Jason Monk27d01a622018-12-10 15:57:09 -050099@Singleton
Mark Renouf71a3af62019-04-08 15:02:54 -0400100public class BubbleController implements ConfigurationController.ConfigurationListener {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800101
Issei Suzukia8d07312019-06-07 12:56:19 +0200102 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800103
Mark Renouf08bc42a2019-03-07 13:01:59 -0500104 @Retention(SOURCE)
105 @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
Mady Melloraa8fef22019-04-11 13:36:40 -0700106 DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE})
Mark Renoufba5ab512019-05-02 15:21:01 -0400107 @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
Mark Renouf08bc42a2019-03-07 13:01:59 -0500108 @interface DismissReason {}
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700109
Mark Renouf08bc42a2019-03-07 13:01:59 -0500110 static final int DISMISS_USER_GESTURE = 1;
111 static final int DISMISS_AGED = 2;
112 static final int DISMISS_TASK_FINISHED = 3;
113 static final int DISMISS_BLOCKED = 4;
114 static final int DISMISS_NOTIF_CANCEL = 5;
115 static final int DISMISS_ACCESSIBILITY_ACTION = 6;
Mady Melloraa8fef22019-04-11 13:36:40 -0700116 static final int DISMISS_NO_LONGER_BUBBLE = 7;
Mark Renouf08bc42a2019-03-07 13:01:59 -0500117
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400118 public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
Joshua Tsuji25a4b7b2019-03-22 14:11:06 -0400119
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800120 /** Flag to enable or disable the entire feature */
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800121 private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
Joshua Tsuji010c2b12019-02-25 18:11:25 -0500122
Ned Burns01e38212019-01-03 16:32:52 -0500123 private final Context mContext;
124 private final NotificationEntryManager mNotificationEntryManager;
Mark Renoufcecc77b2019-01-30 16:32:24 -0500125 private final BubbleTaskStackListener mTaskStackListener;
Mady Mellord1c78b262018-11-06 18:04:40 -0800126 private BubbleStateChangeListener mStateChangeListener;
Mady Mellorcd9b1302018-11-06 18:08:04 -0800127 private BubbleExpandListener mExpandListener;
Issei Suzukic0387542019-03-08 17:31:14 +0100128 @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800129
Mady Mellor3dff9e62019-02-05 18:12:53 -0800130 private BubbleData mBubbleData;
Joshua Tsujic650a142019-05-22 11:31:19 -0400131 @Nullable private BubbleStackView mStackView;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800132
133 // Bubbles get added to the status bar view
Ned Burns01e38212019-01-03 16:32:52 -0500134 private final StatusBarWindowController mStatusBarWindowController;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400135 private final ZenModeController mZenModeController;
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800136 private StatusBarStateListener mStatusBarStateListener;
137
Mady Melloraa8fef22019-04-11 13:36:40 -0700138 private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
Mady Mellora54e9fa2019-04-18 13:26:18 -0700139 private IStatusBarService mBarService;
Mady Mellorb4991e62019-01-10 15:14:51 -0800140
Mady Mellord1c78b262018-11-06 18:04:40 -0800141 // Used for determining view rect for touch interaction
142 private Rect mTempRect = new Rect();
143
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400144 /** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
145 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
146
Mady Mellor5549dd22018-11-06 18:07:34 -0800147 /**
Mady Mellord1c78b262018-11-06 18:04:40 -0800148 * Listener to be notified when some states of the bubbles change.
149 */
150 public interface BubbleStateChangeListener {
151 /**
152 * Called when the stack has bubbles or no longer has bubbles.
153 */
154 void onHasBubblesChanged(boolean hasBubbles);
155 }
156
Mady Mellorcd9b1302018-11-06 18:08:04 -0800157 /**
158 * Listener to find out about stack expansion / collapse events.
159 */
160 public interface BubbleExpandListener {
161 /**
162 * Called when the expansion state of the bubble stack changes.
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700163 *
Mady Mellorcd9b1302018-11-06 18:08:04 -0800164 * @param isExpanding whether it's expanding or collapsing
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800165 * @param key the notification key associated with bubble being expanded
Mady Mellorcd9b1302018-11-06 18:08:04 -0800166 */
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800167 void onBubbleExpandChanged(boolean isExpanding, String key);
168 }
169
170 /**
171 * Listens for the current state of the status bar and updates the visibility state
172 * of bubbles as needed.
173 */
174 private class StatusBarStateListener implements StatusBarStateController.StateListener {
175 private int mState;
176 /**
177 * Returns the current status bar state.
178 */
179 public int getCurrentState() {
180 return mState;
181 }
182
183 @Override
184 public void onStateChanged(int newState) {
185 mState = newState;
Mark Renouf71a3af62019-04-08 15:02:54 -0400186 boolean shouldCollapse = (mState != SHADE);
187 if (shouldCollapse) {
188 collapseStack();
189 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700190 updateStack();
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800191 }
Mady Mellorcd9b1302018-11-06 18:08:04 -0800192 }
193
Jason Monk27d01a622018-12-10 15:57:09 -0500194 @Inject
Mady Mellorcfd06c12019-02-13 14:32:12 -0800195 public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
Mady Melloraa8fef22019-04-11 13:36:40 -0700196 BubbleData data, ConfigurationController configurationController,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400197 NotificationInterruptionStateProvider interruptionStateProvider,
198 ZenModeController zenModeController) {
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700199 this(context, statusBarWindowController, data, null /* synchronizer */,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400200 configurationController, interruptionStateProvider, zenModeController);
Issei Suzukic0387542019-03-08 17:31:14 +0100201 }
202
203 public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700204 BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
Mady Melloraa8fef22019-04-11 13:36:40 -0700205 ConfigurationController configurationController,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400206 NotificationInterruptionStateProvider interruptionStateProvider,
207 ZenModeController zenModeController) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800208 mContext = context;
Mady Melloraa8fef22019-04-11 13:36:40 -0700209 mNotificationInterruptionStateProvider = interruptionStateProvider;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400210 mZenModeController = zenModeController;
211 mZenModeController.addCallback(new ZenModeController.Callback() {
212 @Override
213 public void onZenChanged(int zen) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700214 if (mStackView != null) {
215 mStackView.updateDots();
216 }
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400217 }
218
219 @Override
220 public void onConfigChanged(ZenModeConfig config) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700221 if (mStackView != null) {
222 mStackView.updateDots();
223 }
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400224 }
225 });
Mady Melloraa8fef22019-04-11 13:36:40 -0700226
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700227 configurationController.addCallback(this /* configurationListener */);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800228
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400229 mBubbleData = data;
230 mBubbleData.setListener(mBubbleDataListener);
231
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800232 mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
Ned Burns01e38212019-01-03 16:32:52 -0500233 mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700234 mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
Mady Mellorb4991e62019-01-10 15:14:51 -0800235
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800236 mStatusBarWindowController = statusBarWindowController;
237 mStatusBarStateListener = new StatusBarStateListener();
238 Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500239
Mark Renoufcecc77b2019-01-30 16:32:24 -0500240 mTaskStackListener = new BubbleTaskStackListener();
241 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800242
Joshua Tsujia19515f2019-02-13 18:02:29 -0500243 try {
244 WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener());
245 } catch (RemoteException e) {
246 e.printStackTrace();
247 }
Issei Suzukic0387542019-03-08 17:31:14 +0100248 mSurfaceSynchronizer = synchronizer;
Mady Mellora54e9fa2019-04-18 13:26:18 -0700249
250 mBarService = IStatusBarService.Stub.asInterface(
251 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Mady Mellor5549dd22018-11-06 18:07:34 -0800252 }
253
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400254 /**
255 * BubbleStackView is lazily created by this method the first time a Bubble is added. This
256 * method initializes the stack view and adds it to the StatusBar just above the scrim.
257 */
258 private void ensureStackViewCreated() {
259 if (mStackView == null) {
260 mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
261 ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
Lyn Hanbde48202019-05-29 19:18:29 -0700262 int bubbleScrimIndex = sbv.indexOfChild(sbv.findViewById(R.id.scrim_for_bubble));
263 int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
264 sbv.addView(mStackView, stackIndex,
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400265 new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
266 if (mExpandListener != null) {
267 mStackView.setExpandListener(mExpandListener);
268 }
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400269 }
270 }
271
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700272 @Override
273 public void onUiModeChanged() {
274 if (mStackView != null) {
Lyn Han02cca812019-04-02 16:27:32 -0700275 mStackView.onThemeChanged();
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700276 }
277 }
278
279 @Override
280 public void onOverlayChanged() {
281 if (mStackView != null) {
Lyn Han02cca812019-04-02 16:27:32 -0700282 mStackView.onThemeChanged();
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700283 }
284 }
285
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400286 @Override
287 public void onConfigChanged(Configuration newConfig) {
288 if (mStackView != null && newConfig != null && newConfig.orientation != mOrientation) {
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400289 mOrientation = newConfig.orientation;
Lyn Hanf4730312019-06-18 11:18:58 -0700290 mStackView.onOrientationChanged(newConfig.orientation);
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400291 }
292 }
293
Mady Mellor5549dd22018-11-06 18:07:34 -0800294 /**
Mady Mellord1c78b262018-11-06 18:04:40 -0800295 * Set a listener to be notified when some states of the bubbles change.
296 */
297 public void setBubbleStateChangeListener(BubbleStateChangeListener listener) {
298 mStateChangeListener = listener;
299 }
300
301 /**
Mady Mellorcd9b1302018-11-06 18:08:04 -0800302 * Set a listener to be notified of bubble expand events.
303 */
304 public void setExpandListener(BubbleExpandListener listener) {
Issei Suzukiac9fcb72019-02-04 17:45:57 +0100305 mExpandListener = ((isExpanding, key) -> {
306 if (listener != null) {
307 listener.onBubbleExpandChanged(isExpanding, key);
308 }
309 mStatusBarWindowController.setBubbleExpanded(isExpanding);
310 });
Mady Mellorcd9b1302018-11-06 18:08:04 -0800311 if (mStackView != null) {
312 mStackView.setExpandListener(mExpandListener);
313 }
314 }
315
316 /**
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800317 * Whether or not there are bubbles present, regardless of them being visible on the
318 * screen (e.g. if on AOD).
319 */
320 public boolean hasBubbles() {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800321 if (mStackView == null) {
322 return false;
323 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400324 return mBubbleData.hasBubbles();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800325 }
326
327 /**
328 * Whether the stack of bubbles is expanded or not.
329 */
330 public boolean isStackExpanded() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400331 return mBubbleData.isExpanded();
332 }
333
334 /**
335 * Tell the stack of bubbles to expand.
336 */
337 public void expandStack() {
338 mBubbleData.setExpanded(true);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800339 }
340
341 /**
342 * Tell the stack of bubbles to collapse.
343 */
344 public void collapseStack() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400345 mBubbleData.setExpanded(false /* expanded */);
346 }
347
Mady Mellorce23c462019-06-17 17:30:07 -0700348 /**
349 * Whether (1) there is a bubble associated with the provided key and
350 * (2) if the notification for that bubble is hidden from the shade.
351 *
352 * False if there isn't a bubble or if the notification for that bubble appears in the shade.
353 */
354 public boolean isBubbleNotificationSuppressedFromShade(String key) {
355 return mBubbleData.hasBubbleWithKey(key)
356 && !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
357 }
358
Mark Renouf71a3af62019-04-08 15:02:54 -0400359 void selectBubble(Bubble bubble) {
360 mBubbleData.setSelectedBubble(bubble);
361 }
362
363 @VisibleForTesting
364 void selectBubble(String key) {
365 Bubble bubble = mBubbleData.getBubbleWithKey(key);
366 selectBubble(bubble);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800367 }
368
369 /**
Mark Renouffec45da2019-03-13 13:24:27 -0400370 * Request the stack expand if needed, then select the specified Bubble as current.
371 *
372 * @param notificationKey the notification key for the bubble to be selected
373 */
374 public void expandStackAndSelectBubble(String notificationKey) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400375 Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
376 if (bubble != null) {
377 mBubbleData.setSelectedBubble(bubble);
378 mBubbleData.setExpanded(true);
Mark Renouffec45da2019-03-13 13:24:27 -0400379 }
380 }
381
382 /**
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800383 * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
384 */
Mark Renouf08bc42a2019-03-07 13:01:59 -0500385 void dismissStack(@DismissReason int reason) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400386 mBubbleData.dismissAll(reason);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800387 }
388
389 /**
Mark Renouf041d7262019-02-06 12:09:41 -0500390 * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
391 * is forwarded a back key down/up pair.
392 */
393 public void performBackPressIfNeeded() {
394 if (mStackView != null) {
395 mStackView.performBackPressIfNeeded();
396 }
397 }
398
399 /**
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800400 * Adds or updates a bubble associated with the provided notification entry.
401 *
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400402 * @param notif the notification associated with this bubble.
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800403 */
Mark Renouff97ed462019-04-05 13:46:24 -0400404 void updateBubble(NotificationEntry notif) {
Mady Mellor66efd5e2019-05-15 13:38:11 -0700405 // If this is an interruptive notif, mark that it's interrupted
406 if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
407 notif.setInterruption();
408 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400409 mBubbleData.notificationEntryUpdated(notif);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800410 }
411
412 /**
413 * Removes the bubble associated with the {@param uri}.
Mark Renouf658c6bc2019-01-30 10:26:54 -0500414 * <p>
415 * Must be called from the main thread.
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800416 */
Mark Renouf658c6bc2019-01-30 10:26:54 -0500417 @MainThread
Mark Renouf08bc42a2019-03-07 13:01:59 -0500418 void removeBubble(String key, int reason) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400419 // TEMP: refactor to change this to pass entry
420 Bubble bubble = mBubbleData.getBubbleWithKey(key);
421 if (bubble != null) {
Mady Mellored99c272019-06-13 15:58:30 -0700422 mBubbleData.notificationEntryRemoved(bubble.getEntry(), reason);
Mady Mellore8e07712019-01-23 12:45:33 -0800423 }
424 }
425
Ned Burns01e38212019-01-03 16:32:52 -0500426 @SuppressWarnings("FieldCanBeLocal")
Mady Mellorc2ff0112019-03-28 14:18:06 -0700427 private final NotificationRemoveInterceptor mRemoveInterceptor =
428 new NotificationRemoveInterceptor() {
429 @Override
430 public boolean onNotificationRemoveRequested(String key, int reason) {
431 if (!mBubbleData.hasBubbleWithKey(key)) {
432 return false;
433 }
Mady Mellored99c272019-06-13 15:58:30 -0700434 Bubble bubble = mBubbleData.getBubbleWithKey(key);
435 NotificationEntry entry = bubble.getEntry();
Mady Mellorc2ff0112019-03-28 14:18:06 -0700436
437 final boolean isClearAll = reason == REASON_CANCEL_ALL;
438 final boolean isUserDimiss = reason == REASON_CANCEL;
439 final boolean isAppCancel = reason == REASON_APP_CANCEL
440 || reason == REASON_APP_CANCEL_ALL;
441
442 // Need to check for !appCancel here because the notification may have
443 // previously been dismissed & entry.isRowDismissed would still be true
444 boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
445 || isClearAll || isUserDimiss;
446
447 // The bubble notification sticks around in the data as long as the bubble is
448 // not dismissed and the app hasn't cancelled the notification.
Mady Mellored99c272019-06-13 15:58:30 -0700449 boolean bubbleExtended = entry.isBubble() && !bubble.isRemoved()
Mady Mellorc2ff0112019-03-28 14:18:06 -0700450 && userRemovedNotif;
451 if (bubbleExtended) {
Mady Mellorce23c462019-06-17 17:30:07 -0700452 bubble.setShowInShadeWhenBubble(false);
Mady Mellordf48d0a2019-06-25 18:26:46 -0700453 bubble.setShowBubbleDot(false);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700454 if (mStackView != null) {
455 mStackView.updateDotVisibility(entry.key);
456 }
457 mNotificationEntryManager.updateNotifications();
458 return true;
Mady Mellored99c272019-06-13 15:58:30 -0700459 } else if (!userRemovedNotif && !bubble.isRemoved()) {
Mady Mellorc2ff0112019-03-28 14:18:06 -0700460 // This wasn't a user removal so we should remove the bubble as well
461 mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
462 return false;
463 }
464 return false;
465 }
466 };
467
468 @SuppressWarnings("FieldCanBeLocal")
Ned Burns01e38212019-01-03 16:32:52 -0500469 private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
470 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500471 public void onPendingEntryAdded(NotificationEntry entry) {
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800472 if (!areBubblesEnabled(mContext)) {
473 return;
474 }
Mady Mellorca0c24c2019-05-16 16:14:32 -0700475 if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
476 && canLaunchInActivityView(mContext, entry)) {
Mark Renouff97ed462019-04-05 13:46:24 -0400477 updateBubble(entry);
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800478 }
479 }
480
481 @Override
482 public void onPreEntryUpdated(NotificationEntry entry) {
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800483 if (!areBubblesEnabled(mContext)) {
484 return;
485 }
Mady Mellorca0c24c2019-05-16 16:14:32 -0700486 boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
487 && canLaunchInActivityView(mContext, entry);
Mady Melloraa8fef22019-04-11 13:36:40 -0700488 if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
489 // It was previously a bubble but no longer a bubble -- lets remove it
490 removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE);
Mady Mellorff40e012019-05-03 15:34:41 -0700491 } else if (shouldBubble) {
Mady Mellored99c272019-06-13 15:58:30 -0700492 Bubble b = mBubbleData.getBubbleWithKey(entry.key);
493 b.setRemoved(false); // updates come back as bubbles even if dismissed
Mark Renouff97ed462019-04-05 13:46:24 -0400494 updateBubble(entry);
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800495 }
496 }
Mark Renoufbbcf07f2019-05-09 10:42:43 -0400497
498 @Override
499 public void onNotificationRankingUpdated(RankingMap rankingMap) {
500 // Forward to BubbleData to block any bubbles which should no longer be shown
501 mBubbleData.notificationRankingUpdated(rankingMap);
502 }
Ned Burns01e38212019-01-03 16:32:52 -0500503 };
504
Mark Renouf71a3af62019-04-08 15:02:54 -0400505 @SuppressWarnings("FieldCanBeLocal")
Mark Renouf3bc5b362019-04-05 14:37:59 -0400506 private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400507
Mark Renouf3bc5b362019-04-05 14:37:59 -0400508 @Override
Mark Renouf82a40e62019-05-23 16:16:24 -0400509 public void applyUpdate(BubbleData.Update update) {
510 if (mStackView == null && update.addedBubble != null) {
511 // Lazy init stack view when the first bubble is added.
512 ensureStackViewCreated();
Mark Renouf71a3af62019-04-08 15:02:54 -0400513 }
Mark Renouf82a40e62019-05-23 16:16:24 -0400514
515 // If not yet initialized, ignore all other changes.
516 if (mStackView == null) {
517 return;
518 }
519
520 if (update.addedBubble != null) {
521 mStackView.addBubble(update.addedBubble);
522 }
523
524 // Collapsing? Do this first before remaining steps.
525 if (update.expandedChanged && !update.expanded) {
526 mStackView.setExpanded(false);
527 }
528
529 // Do removals, if any.
530 for (Pair<Bubble, Integer> removed : update.removedBubbles) {
531 final Bubble bubble = removed.first;
532 @DismissReason final int reason = removed.second;
533 mStackView.removeBubble(bubble);
534
535 if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
Mady Mellor99a302602019-06-14 11:39:56 -0700536 && !bubble.showInShadeWhenBubble()) {
Mark Renouf82a40e62019-05-23 16:16:24 -0400537 // The bubble is gone & the notification is gone, time to actually remove it
Mady Mellored99c272019-06-13 15:58:30 -0700538 mNotificationEntryManager.performRemoveNotification(
539 bubble.getEntry().notification, UNDEFINED_DISMISS_REASON);
Mark Renouf82a40e62019-05-23 16:16:24 -0400540 } else {
Mady Mellor3a0a1b42019-05-23 06:40:21 -0700541 // Update the flag for SysUI
Mady Mellored99c272019-06-13 15:58:30 -0700542 bubble.getEntry().notification.getNotification().flags &= ~FLAG_BUBBLE;
Mady Mellor3a0a1b42019-05-23 06:40:21 -0700543
544 // Make sure NoMan knows it's not a bubble anymore so anyone querying it will
545 // get right result back
Mark Renouf82a40e62019-05-23 16:16:24 -0400546 try {
547 mBarService.onNotificationBubbleChanged(bubble.getKey(),
548 false /* isBubble */);
549 } catch (RemoteException e) {
550 // Bad things have happened
551 }
Mady Mellora54e9fa2019-04-18 13:26:18 -0700552 }
553 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400554
Mark Renouf82a40e62019-05-23 16:16:24 -0400555 if (update.updatedBubble != null) {
556 mStackView.updateBubble(update.updatedBubble);
Mark Renouf71a3af62019-04-08 15:02:54 -0400557 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400558
Mark Renouf82a40e62019-05-23 16:16:24 -0400559 if (update.orderChanged) {
560 mStackView.updateBubbleOrder(update.bubbles);
Mark Renoufba5ab512019-05-02 15:21:01 -0400561 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400562
Mark Renouf82a40e62019-05-23 16:16:24 -0400563 if (update.selectionChanged) {
564 mStackView.setSelectedBubble(update.selectedBubble);
Mark Renouf71a3af62019-04-08 15:02:54 -0400565 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400566
Mark Renouf82a40e62019-05-23 16:16:24 -0400567 // Expanding? Apply this last.
568 if (update.expandedChanged && update.expanded) {
569 mStackView.setExpanded(true);
Mark Renouf71a3af62019-04-08 15:02:54 -0400570 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400571
Mark Renouf71a3af62019-04-08 15:02:54 -0400572 mNotificationEntryManager.updateNotifications();
Lyn Han6c40fe72019-05-08 14:06:33 -0700573 updateStack();
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400574
Issei Suzukia8d07312019-06-07 12:56:19 +0200575 if (DEBUG_BUBBLE_CONTROLLER) {
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400576 Log.d(TAG, "[BubbleData]");
577 Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
578 mBubbleData.getSelectedBubble()));
579
580 if (mStackView != null) {
581 Log.d(TAG, "[BubbleStackView]");
582 Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
583 mStackView.getExpandedBubble()));
584 }
585 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400586 }
587 };
588
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800589 /**
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400590 * Lets any listeners know if bubble state has changed.
Lyn Han6c40fe72019-05-08 14:06:33 -0700591 * Updates the visibility of the bubbles based on current state.
592 * Does not un-bubble, just hides or un-hides. Notifies any
593 * {@link BubbleStateChangeListener}s of visibility changes.
594 * Updates stack description for TalkBack focus.
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800595 */
Lyn Han6c40fe72019-05-08 14:06:33 -0700596 public void updateStack() {
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800597 if (mStackView == null) {
598 return;
Mady Mellord1c78b262018-11-06 18:04:40 -0800599 }
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800600 if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
601 // Bubbles only appear in unlocked shade
602 mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
603 } else if (mStackView != null) {
604 mStackView.setVisibility(INVISIBLE);
Mady Mellor5549dd22018-11-06 18:07:34 -0800605 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700606
607 // Let listeners know if bubble state changed.
608 boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
609 boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
610 mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
611 if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
612 mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
613 }
614
615 mStackView.updateContentDescription();
Mady Mellord1c78b262018-11-06 18:04:40 -0800616 }
617
618 /**
619 * Rect indicating the touchable region for the bubble stack / expanded stack.
620 */
621 public Rect getTouchableRegion() {
622 if (mStackView == null || mStackView.getVisibility() != VISIBLE) {
623 return null;
624 }
625 mStackView.getBoundsOnScreen(mTempRect);
626 return mTempRect;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800627 }
628
Mady Mellor390bff42019-04-05 15:09:01 -0700629 /**
630 * The display id of the expanded view, if the stack is expanded and not occluded by the
631 * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
632 */
633 public int getExpandedDisplayId(Context context) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200634 final Bubble bubble = getExpandedBubble(context);
635 return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
636 }
637
638 @Nullable
639 private Bubble getExpandedBubble(Context context) {
Joel Galenson4071ddb2019-04-18 13:30:45 -0700640 if (mStackView == null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200641 return null;
Joel Galenson4071ddb2019-04-18 13:30:45 -0700642 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200643 final boolean defaultDisplay = context.getDisplay() != null
Mady Mellor390bff42019-04-05 15:09:01 -0700644 && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
Issei Suzukicac2a502019-04-16 16:52:50 +0200645 final Bubble expandedBubble = mStackView.getExpandedBubble();
646 if (defaultDisplay && expandedBubble != null && isStackExpanded()
Mady Mellor390bff42019-04-05 15:09:01 -0700647 && !mStatusBarWindowController.getPanelExpanded()) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200648 return expandedBubble;
Mady Mellor390bff42019-04-05 15:09:01 -0700649 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200650 return null;
Mady Mellor390bff42019-04-05 15:09:01 -0700651 }
652
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800653 @VisibleForTesting
654 BubbleStackView getStackView() {
655 return mStackView;
656 }
657
Mady Mellor70cba7bb2019-07-02 15:06:07 -0700658 /**
659 * Description of current bubble state.
660 */
661 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
662 pw.println("BubbleController state:");
663 mBubbleData.dump(fd, pw, args);
664 pw.println();
665 }
666
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400667 static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
668 StringBuilder sb = new StringBuilder();
669 for (Bubble bubble : bubbles) {
670 if (bubble == null) {
671 sb.append(" <null> !!!!!\n");
672 } else {
673 boolean isSelected = (bubble == selected);
674 sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
675 ((isSelected) ? "->" : " "),
676 bubble.getLastActivity(),
677 (bubble.isOngoing() ? 1 : 0),
678 bubble.getKey()));
679 }
680 }
681 return sb.toString();
682 }
683
Mady Mellore80930e2019-03-21 16:00:45 -0700684 /**
Mark Renoufcecc77b2019-01-30 16:32:24 -0500685 * This task stack listener is responsible for responding to tasks moved to the front
686 * which are on the default (main) display. When this happens, expanded bubbles must be
687 * collapsed so the user may interact with the app which was just moved to the front.
688 * <p>
689 * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches
690 * these calls via a main thread Handler.
691 */
692 @MainThread
693 private class BubbleTaskStackListener extends TaskStackChangeListener {
694
Mark Renoufcecc77b2019-01-30 16:32:24 -0500695 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500696 public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
697 if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400698 mBubbleData.setExpanded(false);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500699 }
700 }
701
Mark Renoufcecc77b2019-01-30 16:32:24 -0500702 @Override
Mark Renoufa12e8762019-03-07 15:43:01 -0500703 public void onActivityLaunchOnSecondaryDisplayRerouted() {
Mark Renoufcecc77b2019-01-30 16:32:24 -0500704 if (mStackView != null) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400705 mBubbleData.setExpanded(false);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500706 }
707 }
Mark Renouf446251d2019-04-26 10:22:41 -0400708
709 @Override
710 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
711 if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
712 mBubbleData.setExpanded(false);
713 }
714 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200715
716 @Override
717 public void onSingleTaskDisplayDrawn(int displayId) {
718 final Bubble expandedBubble = getExpandedBubble(mContext);
719 if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
720 expandedBubble.setContentVisibility(true);
721 }
722 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200723
724 @Override
725 public void onSingleTaskDisplayEmpty(int displayId) {
726 final Bubble expandedBubble = getExpandedBubble(mContext);
727 if (expandedBubble == null) {
728 return;
729 }
730 if (expandedBubble.getDisplayId() == displayId) {
731 mBubbleData.setExpanded(false);
732 }
Mady Mellored99c272019-06-13 15:58:30 -0700733 if (expandedBubble.getExpandedView() != null) {
734 expandedBubble.getExpandedView().notifyDisplayEmpty();
Issei Suzuki734bc942019-06-05 13:59:52 +0200735 }
736 }
Mark Renoufcecc77b2019-01-30 16:32:24 -0500737 }
738
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800739 private static boolean areBubblesEnabled(Context context) {
740 return Settings.Secure.getInt(context.getContentResolver(),
741 ENABLE_BUBBLES, 1) != 0;
742 }
Joshua Tsujia19515f2019-02-13 18:02:29 -0500743
Mady Mellorca0c24c2019-05-16 16:14:32 -0700744 /**
745 * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
746 *
747 * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
748 * that should filter out any invalid bubbles, but should protect SysUI side just in case.
749 *
750 * @param context the context to use.
751 * @param entry the entry to bubble.
752 */
753 static boolean canLaunchInActivityView(Context context, NotificationEntry entry) {
754 PendingIntent intent = entry.getBubbleMetadata() != null
755 ? entry.getBubbleMetadata().getIntent()
756 : null;
757 if (intent == null) {
758 Log.w(TAG, "Unable to create bubble -- no intent");
759 return false;
760 }
761 ActivityInfo info =
762 intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
763 if (info == null) {
764 Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
765 + intent);
766 return false;
767 }
768 if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
769 Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
770 + intent);
771 return false;
772 }
773 if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
774 Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
775 + "for intent: " + intent);
776 return false;
777 }
778 if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
779 Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
780 + intent);
781 return false;
782 }
783 return true;
784 }
785
Joshua Tsujia19515f2019-02-13 18:02:29 -0500786 /** PinnedStackListener that dispatches IME visibility updates to the stack. */
787 private class BubblesImeListener extends IPinnedStackListener.Stub {
788
789 @Override
790 public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
791 }
792
793 @Override
794 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
795 Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
796 int displayRotation) throws RemoteException {}
797
798 @Override
Joshua Tsujid9422832019-03-05 13:32:37 -0500799 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
800 if (mStackView != null && mStackView.getBubbleCount() > 0) {
801 mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
Joshua Tsujia19515f2019-02-13 18:02:29 -0500802 }
803 }
804
805 @Override
806 public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
807 throws RemoteException {}
808
809 @Override
810 public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {}
811
812 @Override
813 public void onActionsChanged(ParceledListSlice actions) throws RemoteException {}
814 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800815}