blob: 6e3c4103a6f0bd94c28591f8d53c6d67172344dc [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
Mark Renouf08bc42a2019-03-07 13:01:59 -050084import java.lang.annotation.Retention;
Mark Renoufba5ab512019-05-02 15:21:01 -040085import java.lang.annotation.Target;
Mady Mellore80930e2019-03-21 16:00:45 -070086import java.util.List;
Mark Renouf08bc42a2019-03-07 13:01:59 -050087
Jason Monk27d01a622018-12-10 15:57:09 -050088import javax.inject.Inject;
89import javax.inject.Singleton;
90
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080091/**
92 * Bubbles are a special type of content that can "float" on top of other apps or System UI.
93 * Bubbles can be expanded to show more content.
94 *
95 * The controller manages addition, removal, and visible state of bubbles on screen.
96 */
Jason Monk27d01a622018-12-10 15:57:09 -050097@Singleton
Mark Renouf71a3af62019-04-08 15:02:54 -040098public class BubbleController implements ConfigurationController.ConfigurationListener {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -080099
Issei Suzukia8d07312019-06-07 12:56:19 +0200100 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800101
Mark Renouf08bc42a2019-03-07 13:01:59 -0500102 @Retention(SOURCE)
103 @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
Mady Melloraa8fef22019-04-11 13:36:40 -0700104 DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE})
Mark Renoufba5ab512019-05-02 15:21:01 -0400105 @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
Mark Renouf08bc42a2019-03-07 13:01:59 -0500106 @interface DismissReason {}
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700107
Mark Renouf08bc42a2019-03-07 13:01:59 -0500108 static final int DISMISS_USER_GESTURE = 1;
109 static final int DISMISS_AGED = 2;
110 static final int DISMISS_TASK_FINISHED = 3;
111 static final int DISMISS_BLOCKED = 4;
112 static final int DISMISS_NOTIF_CANCEL = 5;
113 static final int DISMISS_ACCESSIBILITY_ACTION = 6;
Mady Melloraa8fef22019-04-11 13:36:40 -0700114 static final int DISMISS_NO_LONGER_BUBBLE = 7;
Mark Renouf08bc42a2019-03-07 13:01:59 -0500115
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400116 public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
Joshua Tsuji25a4b7b2019-03-22 14:11:06 -0400117
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800118 /** Flag to enable or disable the entire feature */
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800119 private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
Joshua Tsuji010c2b12019-02-25 18:11:25 -0500120
Ned Burns01e38212019-01-03 16:32:52 -0500121 private final Context mContext;
122 private final NotificationEntryManager mNotificationEntryManager;
Mark Renoufcecc77b2019-01-30 16:32:24 -0500123 private final BubbleTaskStackListener mTaskStackListener;
Mady Mellord1c78b262018-11-06 18:04:40 -0800124 private BubbleStateChangeListener mStateChangeListener;
Mady Mellorcd9b1302018-11-06 18:08:04 -0800125 private BubbleExpandListener mExpandListener;
Issei Suzukic0387542019-03-08 17:31:14 +0100126 @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800127
Mady Mellor3dff9e62019-02-05 18:12:53 -0800128 private BubbleData mBubbleData;
Joshua Tsujic650a142019-05-22 11:31:19 -0400129 @Nullable private BubbleStackView mStackView;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800130
131 // Bubbles get added to the status bar view
Ned Burns01e38212019-01-03 16:32:52 -0500132 private final StatusBarWindowController mStatusBarWindowController;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400133 private final ZenModeController mZenModeController;
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800134 private StatusBarStateListener mStatusBarStateListener;
135
Mady Melloraa8fef22019-04-11 13:36:40 -0700136 private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
Mady Mellora54e9fa2019-04-18 13:26:18 -0700137 private IStatusBarService mBarService;
Mady Mellorb4991e62019-01-10 15:14:51 -0800138
Mady Mellord1c78b262018-11-06 18:04:40 -0800139 // Used for determining view rect for touch interaction
140 private Rect mTempRect = new Rect();
141
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400142 /** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
143 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
144
Mady Mellor5549dd22018-11-06 18:07:34 -0800145 /**
Mady Mellord1c78b262018-11-06 18:04:40 -0800146 * Listener to be notified when some states of the bubbles change.
147 */
148 public interface BubbleStateChangeListener {
149 /**
150 * Called when the stack has bubbles or no longer has bubbles.
151 */
152 void onHasBubblesChanged(boolean hasBubbles);
153 }
154
Mady Mellorcd9b1302018-11-06 18:08:04 -0800155 /**
156 * Listener to find out about stack expansion / collapse events.
157 */
158 public interface BubbleExpandListener {
159 /**
160 * Called when the expansion state of the bubble stack changes.
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700161 *
Mady Mellorcd9b1302018-11-06 18:08:04 -0800162 * @param isExpanding whether it's expanding or collapsing
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800163 * @param key the notification key associated with bubble being expanded
Mady Mellorcd9b1302018-11-06 18:08:04 -0800164 */
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800165 void onBubbleExpandChanged(boolean isExpanding, String key);
166 }
167
168 /**
169 * Listens for the current state of the status bar and updates the visibility state
170 * of bubbles as needed.
171 */
172 private class StatusBarStateListener implements StatusBarStateController.StateListener {
173 private int mState;
174 /**
175 * Returns the current status bar state.
176 */
177 public int getCurrentState() {
178 return mState;
179 }
180
181 @Override
182 public void onStateChanged(int newState) {
183 mState = newState;
Mark Renouf71a3af62019-04-08 15:02:54 -0400184 boolean shouldCollapse = (mState != SHADE);
185 if (shouldCollapse) {
186 collapseStack();
187 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700188 updateStack();
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800189 }
Mady Mellorcd9b1302018-11-06 18:08:04 -0800190 }
191
Jason Monk27d01a622018-12-10 15:57:09 -0500192 @Inject
Mady Mellorcfd06c12019-02-13 14:32:12 -0800193 public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
Mady Melloraa8fef22019-04-11 13:36:40 -0700194 BubbleData data, ConfigurationController configurationController,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400195 NotificationInterruptionStateProvider interruptionStateProvider,
196 ZenModeController zenModeController) {
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700197 this(context, statusBarWindowController, data, null /* synchronizer */,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400198 configurationController, interruptionStateProvider, zenModeController);
Issei Suzukic0387542019-03-08 17:31:14 +0100199 }
200
201 public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700202 BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
Mady Melloraa8fef22019-04-11 13:36:40 -0700203 ConfigurationController configurationController,
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400204 NotificationInterruptionStateProvider interruptionStateProvider,
205 ZenModeController zenModeController) {
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800206 mContext = context;
Mady Melloraa8fef22019-04-11 13:36:40 -0700207 mNotificationInterruptionStateProvider = interruptionStateProvider;
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400208 mZenModeController = zenModeController;
209 mZenModeController.addCallback(new ZenModeController.Callback() {
210 @Override
211 public void onZenChanged(int zen) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700212 if (mStackView != null) {
213 mStackView.updateDots();
214 }
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400215 }
216
217 @Override
218 public void onConfigChanged(ZenModeConfig config) {
Mady Mellordf48d0a2019-06-25 18:26:46 -0700219 if (mStackView != null) {
220 mStackView.updateDots();
221 }
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400222 }
223 });
Mady Melloraa8fef22019-04-11 13:36:40 -0700224
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700225 configurationController.addCallback(this /* configurationListener */);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800226
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400227 mBubbleData = data;
228 mBubbleData.setListener(mBubbleDataListener);
229
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800230 mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
Ned Burns01e38212019-01-03 16:32:52 -0500231 mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700232 mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
Mady Mellorb4991e62019-01-10 15:14:51 -0800233
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800234 mStatusBarWindowController = statusBarWindowController;
235 mStatusBarStateListener = new StatusBarStateListener();
236 Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500237
Mark Renoufcecc77b2019-01-30 16:32:24 -0500238 mTaskStackListener = new BubbleTaskStackListener();
239 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800240
Joshua Tsujia19515f2019-02-13 18:02:29 -0500241 try {
242 WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener());
243 } catch (RemoteException e) {
244 e.printStackTrace();
245 }
Issei Suzukic0387542019-03-08 17:31:14 +0100246 mSurfaceSynchronizer = synchronizer;
Mady Mellora54e9fa2019-04-18 13:26:18 -0700247
248 mBarService = IStatusBarService.Stub.asInterface(
249 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Mady Mellor5549dd22018-11-06 18:07:34 -0800250 }
251
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400252 /**
253 * BubbleStackView is lazily created by this method the first time a Bubble is added. This
254 * method initializes the stack view and adds it to the StatusBar just above the scrim.
255 */
256 private void ensureStackViewCreated() {
257 if (mStackView == null) {
258 mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
259 ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
Lyn Hanbde48202019-05-29 19:18:29 -0700260 int bubbleScrimIndex = sbv.indexOfChild(sbv.findViewById(R.id.scrim_for_bubble));
261 int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
262 sbv.addView(mStackView, stackIndex,
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400263 new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
264 if (mExpandListener != null) {
265 mStackView.setExpandListener(mExpandListener);
266 }
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400267 }
268 }
269
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700270 @Override
271 public void onUiModeChanged() {
272 if (mStackView != null) {
Lyn Han02cca812019-04-02 16:27:32 -0700273 mStackView.onThemeChanged();
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700274 }
275 }
276
277 @Override
278 public void onOverlayChanged() {
279 if (mStackView != null) {
Lyn Han02cca812019-04-02 16:27:32 -0700280 mStackView.onThemeChanged();
Lyn Hanf1c9b8b2019-03-14 16:49:48 -0700281 }
282 }
283
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400284 @Override
285 public void onConfigChanged(Configuration newConfig) {
286 if (mStackView != null && newConfig != null && newConfig.orientation != mOrientation) {
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400287 mOrientation = newConfig.orientation;
Lyn Hanf4730312019-06-18 11:18:58 -0700288 mStackView.onOrientationChanged(newConfig.orientation);
Joshua Tsujif418f9e2019-04-04 17:09:53 -0400289 }
290 }
291
Mady Mellor5549dd22018-11-06 18:07:34 -0800292 /**
Mady Mellord1c78b262018-11-06 18:04:40 -0800293 * Set a listener to be notified when some states of the bubbles change.
294 */
295 public void setBubbleStateChangeListener(BubbleStateChangeListener listener) {
296 mStateChangeListener = listener;
297 }
298
299 /**
Mady Mellorcd9b1302018-11-06 18:08:04 -0800300 * Set a listener to be notified of bubble expand events.
301 */
302 public void setExpandListener(BubbleExpandListener listener) {
Issei Suzukiac9fcb72019-02-04 17:45:57 +0100303 mExpandListener = ((isExpanding, key) -> {
304 if (listener != null) {
305 listener.onBubbleExpandChanged(isExpanding, key);
306 }
307 mStatusBarWindowController.setBubbleExpanded(isExpanding);
308 });
Mady Mellorcd9b1302018-11-06 18:08:04 -0800309 if (mStackView != null) {
310 mStackView.setExpandListener(mExpandListener);
311 }
312 }
313
314 /**
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800315 * Whether or not there are bubbles present, regardless of them being visible on the
316 * screen (e.g. if on AOD).
317 */
318 public boolean hasBubbles() {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800319 if (mStackView == null) {
320 return false;
321 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400322 return mBubbleData.hasBubbles();
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800323 }
324
325 /**
326 * Whether the stack of bubbles is expanded or not.
327 */
328 public boolean isStackExpanded() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400329 return mBubbleData.isExpanded();
330 }
331
332 /**
333 * Tell the stack of bubbles to expand.
334 */
335 public void expandStack() {
336 mBubbleData.setExpanded(true);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800337 }
338
339 /**
340 * Tell the stack of bubbles to collapse.
341 */
342 public void collapseStack() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400343 mBubbleData.setExpanded(false /* expanded */);
344 }
345
Mady Mellorce23c462019-06-17 17:30:07 -0700346 /**
347 * Whether (1) there is a bubble associated with the provided key and
348 * (2) if the notification for that bubble is hidden from the shade.
349 *
350 * False if there isn't a bubble or if the notification for that bubble appears in the shade.
351 */
352 public boolean isBubbleNotificationSuppressedFromShade(String key) {
353 return mBubbleData.hasBubbleWithKey(key)
354 && !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
355 }
356
Mark Renouf71a3af62019-04-08 15:02:54 -0400357 void selectBubble(Bubble bubble) {
358 mBubbleData.setSelectedBubble(bubble);
359 }
360
361 @VisibleForTesting
362 void selectBubble(String key) {
363 Bubble bubble = mBubbleData.getBubbleWithKey(key);
364 selectBubble(bubble);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800365 }
366
367 /**
Mark Renouffec45da2019-03-13 13:24:27 -0400368 * Request the stack expand if needed, then select the specified Bubble as current.
369 *
370 * @param notificationKey the notification key for the bubble to be selected
371 */
372 public void expandStackAndSelectBubble(String notificationKey) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400373 Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
374 if (bubble != null) {
375 mBubbleData.setSelectedBubble(bubble);
376 mBubbleData.setExpanded(true);
Mark Renouffec45da2019-03-13 13:24:27 -0400377 }
378 }
379
380 /**
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800381 * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
382 */
Mark Renouf08bc42a2019-03-07 13:01:59 -0500383 void dismissStack(@DismissReason int reason) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400384 mBubbleData.dismissAll(reason);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800385 }
386
387 /**
Mark Renouf041d7262019-02-06 12:09:41 -0500388 * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
389 * is forwarded a back key down/up pair.
390 */
391 public void performBackPressIfNeeded() {
392 if (mStackView != null) {
393 mStackView.performBackPressIfNeeded();
394 }
395 }
396
397 /**
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800398 * Adds or updates a bubble associated with the provided notification entry.
399 *
Mark Renouf8b6a3c62019-04-09 10:17:40 -0400400 * @param notif the notification associated with this bubble.
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800401 */
Mark Renouff97ed462019-04-05 13:46:24 -0400402 void updateBubble(NotificationEntry notif) {
Mady Mellor66efd5e2019-05-15 13:38:11 -0700403 // If this is an interruptive notif, mark that it's interrupted
404 if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
405 notif.setInterruption();
406 }
Mark Renouf71a3af62019-04-08 15:02:54 -0400407 mBubbleData.notificationEntryUpdated(notif);
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800408 }
409
410 /**
411 * Removes the bubble associated with the {@param uri}.
Mark Renouf658c6bc2019-01-30 10:26:54 -0500412 * <p>
413 * Must be called from the main thread.
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800414 */
Mark Renouf658c6bc2019-01-30 10:26:54 -0500415 @MainThread
Mark Renouf08bc42a2019-03-07 13:01:59 -0500416 void removeBubble(String key, int reason) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400417 // TEMP: refactor to change this to pass entry
418 Bubble bubble = mBubbleData.getBubbleWithKey(key);
419 if (bubble != null) {
Mady Mellored99c272019-06-13 15:58:30 -0700420 mBubbleData.notificationEntryRemoved(bubble.getEntry(), reason);
Mady Mellore8e07712019-01-23 12:45:33 -0800421 }
422 }
423
Ned Burns01e38212019-01-03 16:32:52 -0500424 @SuppressWarnings("FieldCanBeLocal")
Mady Mellorc2ff0112019-03-28 14:18:06 -0700425 private final NotificationRemoveInterceptor mRemoveInterceptor =
426 new NotificationRemoveInterceptor() {
427 @Override
428 public boolean onNotificationRemoveRequested(String key, int reason) {
429 if (!mBubbleData.hasBubbleWithKey(key)) {
430 return false;
431 }
Mady Mellored99c272019-06-13 15:58:30 -0700432 Bubble bubble = mBubbleData.getBubbleWithKey(key);
433 NotificationEntry entry = bubble.getEntry();
Mady Mellorc2ff0112019-03-28 14:18:06 -0700434
435 final boolean isClearAll = reason == REASON_CANCEL_ALL;
436 final boolean isUserDimiss = reason == REASON_CANCEL;
437 final boolean isAppCancel = reason == REASON_APP_CANCEL
438 || reason == REASON_APP_CANCEL_ALL;
439
440 // Need to check for !appCancel here because the notification may have
441 // previously been dismissed & entry.isRowDismissed would still be true
442 boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
443 || isClearAll || isUserDimiss;
444
445 // The bubble notification sticks around in the data as long as the bubble is
446 // not dismissed and the app hasn't cancelled the notification.
Mady Mellored99c272019-06-13 15:58:30 -0700447 boolean bubbleExtended = entry.isBubble() && !bubble.isRemoved()
Mady Mellorc2ff0112019-03-28 14:18:06 -0700448 && userRemovedNotif;
449 if (bubbleExtended) {
Mady Mellorce23c462019-06-17 17:30:07 -0700450 bubble.setShowInShadeWhenBubble(false);
Mady Mellordf48d0a2019-06-25 18:26:46 -0700451 bubble.setShowBubbleDot(false);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700452 if (mStackView != null) {
453 mStackView.updateDotVisibility(entry.key);
454 }
455 mNotificationEntryManager.updateNotifications();
456 return true;
Mady Mellored99c272019-06-13 15:58:30 -0700457 } else if (!userRemovedNotif && !bubble.isRemoved()) {
Mady Mellorc2ff0112019-03-28 14:18:06 -0700458 // This wasn't a user removal so we should remove the bubble as well
459 mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
460 return false;
461 }
462 return false;
463 }
464 };
465
466 @SuppressWarnings("FieldCanBeLocal")
Ned Burns01e38212019-01-03 16:32:52 -0500467 private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
468 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500469 public void onPendingEntryAdded(NotificationEntry entry) {
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800470 if (!areBubblesEnabled(mContext)) {
471 return;
472 }
Mady Mellorca0c24c2019-05-16 16:14:32 -0700473 if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
474 && canLaunchInActivityView(mContext, entry)) {
Mark Renouff97ed462019-04-05 13:46:24 -0400475 updateBubble(entry);
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800476 }
477 }
478
479 @Override
480 public void onPreEntryUpdated(NotificationEntry entry) {
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800481 if (!areBubblesEnabled(mContext)) {
482 return;
483 }
Mady Mellorca0c24c2019-05-16 16:14:32 -0700484 boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
485 && canLaunchInActivityView(mContext, entry);
Mady Melloraa8fef22019-04-11 13:36:40 -0700486 if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
487 // It was previously a bubble but no longer a bubble -- lets remove it
488 removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE);
Mady Mellorff40e012019-05-03 15:34:41 -0700489 } else if (shouldBubble) {
Mady Mellored99c272019-06-13 15:58:30 -0700490 Bubble b = mBubbleData.getBubbleWithKey(entry.key);
491 b.setRemoved(false); // updates come back as bubbles even if dismissed
Mark Renouff97ed462019-04-05 13:46:24 -0400492 updateBubble(entry);
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800493 }
494 }
Mark Renoufbbcf07f2019-05-09 10:42:43 -0400495
496 @Override
497 public void onNotificationRankingUpdated(RankingMap rankingMap) {
498 // Forward to BubbleData to block any bubbles which should no longer be shown
499 mBubbleData.notificationRankingUpdated(rankingMap);
500 }
Ned Burns01e38212019-01-03 16:32:52 -0500501 };
502
Mark Renouf71a3af62019-04-08 15:02:54 -0400503 @SuppressWarnings("FieldCanBeLocal")
Mark Renouf3bc5b362019-04-05 14:37:59 -0400504 private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
Mark Renouf71a3af62019-04-08 15:02:54 -0400505
Mark Renouf3bc5b362019-04-05 14:37:59 -0400506 @Override
Mark Renouf82a40e62019-05-23 16:16:24 -0400507 public void applyUpdate(BubbleData.Update update) {
508 if (mStackView == null && update.addedBubble != null) {
509 // Lazy init stack view when the first bubble is added.
510 ensureStackViewCreated();
Mark Renouf71a3af62019-04-08 15:02:54 -0400511 }
Mark Renouf82a40e62019-05-23 16:16:24 -0400512
513 // If not yet initialized, ignore all other changes.
514 if (mStackView == null) {
515 return;
516 }
517
518 if (update.addedBubble != null) {
519 mStackView.addBubble(update.addedBubble);
520 }
521
522 // Collapsing? Do this first before remaining steps.
523 if (update.expandedChanged && !update.expanded) {
524 mStackView.setExpanded(false);
525 }
526
527 // Do removals, if any.
528 for (Pair<Bubble, Integer> removed : update.removedBubbles) {
529 final Bubble bubble = removed.first;
530 @DismissReason final int reason = removed.second;
531 mStackView.removeBubble(bubble);
532
533 if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
Mady Mellor99a302602019-06-14 11:39:56 -0700534 && !bubble.showInShadeWhenBubble()) {
Mark Renouf82a40e62019-05-23 16:16:24 -0400535 // The bubble is gone & the notification is gone, time to actually remove it
Mady Mellored99c272019-06-13 15:58:30 -0700536 mNotificationEntryManager.performRemoveNotification(
537 bubble.getEntry().notification, UNDEFINED_DISMISS_REASON);
Mark Renouf82a40e62019-05-23 16:16:24 -0400538 } else {
Mady Mellor3a0a1b42019-05-23 06:40:21 -0700539 // Update the flag for SysUI
Mady Mellored99c272019-06-13 15:58:30 -0700540 bubble.getEntry().notification.getNotification().flags &= ~FLAG_BUBBLE;
Mady Mellor3a0a1b42019-05-23 06:40:21 -0700541
542 // Make sure NoMan knows it's not a bubble anymore so anyone querying it will
543 // get right result back
Mark Renouf82a40e62019-05-23 16:16:24 -0400544 try {
545 mBarService.onNotificationBubbleChanged(bubble.getKey(),
546 false /* isBubble */);
547 } catch (RemoteException e) {
548 // Bad things have happened
549 }
Mady Mellora54e9fa2019-04-18 13:26:18 -0700550 }
551 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400552
Mark Renouf82a40e62019-05-23 16:16:24 -0400553 if (update.updatedBubble != null) {
554 mStackView.updateBubble(update.updatedBubble);
Mark Renouf71a3af62019-04-08 15:02:54 -0400555 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400556
Mark Renouf82a40e62019-05-23 16:16:24 -0400557 if (update.orderChanged) {
558 mStackView.updateBubbleOrder(update.bubbles);
Mark Renoufba5ab512019-05-02 15:21:01 -0400559 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400560
Mark Renouf82a40e62019-05-23 16:16:24 -0400561 if (update.selectionChanged) {
562 mStackView.setSelectedBubble(update.selectedBubble);
Mark Renouf71a3af62019-04-08 15:02:54 -0400563 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400564
Mark Renouf82a40e62019-05-23 16:16:24 -0400565 // Expanding? Apply this last.
566 if (update.expandedChanged && update.expanded) {
567 mStackView.setExpanded(true);
Mark Renouf71a3af62019-04-08 15:02:54 -0400568 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400569
Mark Renouf71a3af62019-04-08 15:02:54 -0400570 mNotificationEntryManager.updateNotifications();
Lyn Han6c40fe72019-05-08 14:06:33 -0700571 updateStack();
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400572
Issei Suzukia8d07312019-06-07 12:56:19 +0200573 if (DEBUG_BUBBLE_CONTROLLER) {
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400574 Log.d(TAG, "[BubbleData]");
575 Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
576 mBubbleData.getSelectedBubble()));
577
578 if (mStackView != null) {
579 Log.d(TAG, "[BubbleStackView]");
580 Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
581 mStackView.getExpandedBubble()));
582 }
583 }
Mark Renouf3bc5b362019-04-05 14:37:59 -0400584 }
585 };
586
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800587 /**
Joshua Tsujidd4d9f92019-05-13 13:57:38 -0400588 * Lets any listeners know if bubble state has changed.
Lyn Han6c40fe72019-05-08 14:06:33 -0700589 * Updates the visibility of the bubbles based on current state.
590 * Does not un-bubble, just hides or un-hides. Notifies any
591 * {@link BubbleStateChangeListener}s of visibility changes.
592 * Updates stack description for TalkBack focus.
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800593 */
Lyn Han6c40fe72019-05-08 14:06:33 -0700594 public void updateStack() {
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800595 if (mStackView == null) {
596 return;
Mady Mellord1c78b262018-11-06 18:04:40 -0800597 }
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800598 if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
599 // Bubbles only appear in unlocked shade
600 mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
601 } else if (mStackView != null) {
602 mStackView.setVisibility(INVISIBLE);
Mady Mellor5549dd22018-11-06 18:07:34 -0800603 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700604
605 // Let listeners know if bubble state changed.
606 boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
607 boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
608 mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
609 if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
610 mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
611 }
612
613 mStackView.updateContentDescription();
Mady Mellord1c78b262018-11-06 18:04:40 -0800614 }
615
616 /**
617 * Rect indicating the touchable region for the bubble stack / expanded stack.
618 */
619 public Rect getTouchableRegion() {
620 if (mStackView == null || mStackView.getVisibility() != VISIBLE) {
621 return null;
622 }
623 mStackView.getBoundsOnScreen(mTempRect);
624 return mTempRect;
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800625 }
626
Mady Mellor390bff42019-04-05 15:09:01 -0700627 /**
628 * The display id of the expanded view, if the stack is expanded and not occluded by the
629 * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
630 */
631 public int getExpandedDisplayId(Context context) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200632 final Bubble bubble = getExpandedBubble(context);
633 return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
634 }
635
636 @Nullable
637 private Bubble getExpandedBubble(Context context) {
Joel Galenson4071ddb2019-04-18 13:30:45 -0700638 if (mStackView == null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200639 return null;
Joel Galenson4071ddb2019-04-18 13:30:45 -0700640 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200641 final boolean defaultDisplay = context.getDisplay() != null
Mady Mellor390bff42019-04-05 15:09:01 -0700642 && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
Issei Suzukicac2a502019-04-16 16:52:50 +0200643 final Bubble expandedBubble = mStackView.getExpandedBubble();
644 if (defaultDisplay && expandedBubble != null && isStackExpanded()
Mady Mellor390bff42019-04-05 15:09:01 -0700645 && !mStatusBarWindowController.getPanelExpanded()) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200646 return expandedBubble;
Mady Mellor390bff42019-04-05 15:09:01 -0700647 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200648 return null;
Mady Mellor390bff42019-04-05 15:09:01 -0700649 }
650
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800651 @VisibleForTesting
652 BubbleStackView getStackView() {
653 return mStackView;
654 }
655
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400656 static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
657 StringBuilder sb = new StringBuilder();
658 for (Bubble bubble : bubbles) {
659 if (bubble == null) {
660 sb.append(" <null> !!!!!\n");
661 } else {
662 boolean isSelected = (bubble == selected);
663 sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
664 ((isSelected) ? "->" : " "),
665 bubble.getLastActivity(),
666 (bubble.isOngoing() ? 1 : 0),
667 bubble.getKey()));
668 }
669 }
670 return sb.toString();
671 }
672
Mady Mellore80930e2019-03-21 16:00:45 -0700673 /**
Mark Renoufcecc77b2019-01-30 16:32:24 -0500674 * This task stack listener is responsible for responding to tasks moved to the front
675 * which are on the default (main) display. When this happens, expanded bubbles must be
676 * collapsed so the user may interact with the app which was just moved to the front.
677 * <p>
678 * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches
679 * these calls via a main thread Handler.
680 */
681 @MainThread
682 private class BubbleTaskStackListener extends TaskStackChangeListener {
683
Mark Renoufcecc77b2019-01-30 16:32:24 -0500684 @Override
Mark Renoufc808f062019-02-07 15:20:37 -0500685 public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
686 if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400687 mBubbleData.setExpanded(false);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500688 }
689 }
690
Mark Renoufcecc77b2019-01-30 16:32:24 -0500691 @Override
Mark Renoufa12e8762019-03-07 15:43:01 -0500692 public void onActivityLaunchOnSecondaryDisplayRerouted() {
Mark Renoufcecc77b2019-01-30 16:32:24 -0500693 if (mStackView != null) {
Mark Renouf71a3af62019-04-08 15:02:54 -0400694 mBubbleData.setExpanded(false);
Mark Renoufcecc77b2019-01-30 16:32:24 -0500695 }
696 }
Mark Renouf446251d2019-04-26 10:22:41 -0400697
698 @Override
699 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
700 if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
701 mBubbleData.setExpanded(false);
702 }
703 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200704
705 @Override
706 public void onSingleTaskDisplayDrawn(int displayId) {
707 final Bubble expandedBubble = getExpandedBubble(mContext);
708 if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
709 expandedBubble.setContentVisibility(true);
710 }
711 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200712
713 @Override
714 public void onSingleTaskDisplayEmpty(int displayId) {
715 final Bubble expandedBubble = getExpandedBubble(mContext);
716 if (expandedBubble == null) {
717 return;
718 }
719 if (expandedBubble.getDisplayId() == displayId) {
720 mBubbleData.setExpanded(false);
721 }
Mady Mellored99c272019-06-13 15:58:30 -0700722 if (expandedBubble.getExpandedView() != null) {
723 expandedBubble.getExpandedView().notifyDisplayEmpty();
Issei Suzuki734bc942019-06-05 13:59:52 +0200724 }
725 }
Mark Renoufcecc77b2019-01-30 16:32:24 -0500726 }
727
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800728 private static boolean areBubblesEnabled(Context context) {
729 return Settings.Secure.getInt(context.getContentResolver(),
730 ENABLE_BUBBLES, 1) != 0;
731 }
Joshua Tsujia19515f2019-02-13 18:02:29 -0500732
Mady Mellorca0c24c2019-05-16 16:14:32 -0700733 /**
734 * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
735 *
736 * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
737 * that should filter out any invalid bubbles, but should protect SysUI side just in case.
738 *
739 * @param context the context to use.
740 * @param entry the entry to bubble.
741 */
742 static boolean canLaunchInActivityView(Context context, NotificationEntry entry) {
743 PendingIntent intent = entry.getBubbleMetadata() != null
744 ? entry.getBubbleMetadata().getIntent()
745 : null;
746 if (intent == null) {
747 Log.w(TAG, "Unable to create bubble -- no intent");
748 return false;
749 }
750 ActivityInfo info =
751 intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
752 if (info == null) {
753 Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
754 + intent);
755 return false;
756 }
757 if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
758 Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
759 + intent);
760 return false;
761 }
762 if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
763 Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
764 + "for intent: " + intent);
765 return false;
766 }
767 if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
768 Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
769 + intent);
770 return false;
771 }
772 return true;
773 }
774
Joshua Tsujia19515f2019-02-13 18:02:29 -0500775 /** PinnedStackListener that dispatches IME visibility updates to the stack. */
776 private class BubblesImeListener extends IPinnedStackListener.Stub {
777
778 @Override
779 public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
780 }
781
782 @Override
783 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
784 Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
785 int displayRotation) throws RemoteException {}
786
787 @Override
Joshua Tsujid9422832019-03-05 13:32:37 -0500788 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
789 if (mStackView != null && mStackView.getBubbleCount() > 0) {
790 mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
Joshua Tsujia19515f2019-02-13 18:02:29 -0500791 }
792 }
793
794 @Override
795 public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
796 throws RemoteException {}
797
798 @Override
799 public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {}
800
801 @Override
802 public void onActionsChanged(ParceledListSlice actions) throws RemoteException {}
803 }
Mady Mellorc3d6f7d2018-11-07 09:36:56 -0800804}