1 /*
2  * Copyright (C) 2020 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 
17 package com.android.wm.shell.bubbles;
18 
19 import static java.lang.annotation.ElementType.FIELD;
20 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
21 import static java.lang.annotation.ElementType.PARAMETER;
22 import static java.lang.annotation.RetentionPolicy.SOURCE;
23 
24 import android.app.NotificationChannel;
25 import android.content.Intent;
26 import android.content.pm.UserInfo;
27 import android.graphics.drawable.Icon;
28 import android.hardware.HardwareBuffer;
29 import android.os.UserHandle;
30 import android.service.notification.NotificationListenerService;
31 import android.service.notification.NotificationListenerService.RankingMap;
32 import android.util.Pair;
33 import android.util.SparseArray;
34 import android.window.ScreenCapture.ScreenshotHardwareBuffer;
35 import android.window.ScreenCapture.SynchronousScreenCaptureListener;
36 
37 import androidx.annotation.IntDef;
38 import androidx.annotation.Nullable;
39 
40 import com.android.wm.shell.common.annotations.ExternalThread;
41 import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.Target;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.concurrent.Executor;
49 import java.util.function.Consumer;
50 import java.util.function.IntConsumer;
51 
52 /**
53  * Interface to engage bubbles feature.
54  */
55 @ExternalThread
56 public interface Bubbles {
57 
58     @Retention(SOURCE)
59     @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
60             DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
61             DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
62             DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
63             DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED,
64             DISMISS_SWITCH_TO_STACK})
65     @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
66     @interface DismissReason {
67     }
68 
69     int DISMISS_USER_GESTURE = 1;
70     int DISMISS_AGED = 2;
71     int DISMISS_TASK_FINISHED = 3;
72     int DISMISS_BLOCKED = 4;
73     int DISMISS_NOTIF_CANCEL = 5;
74     int DISMISS_ACCESSIBILITY_ACTION = 6;
75     int DISMISS_NO_LONGER_BUBBLE = 7;
76     int DISMISS_USER_CHANGED = 8;
77     int DISMISS_GROUP_CANCELLED = 9;
78     int DISMISS_INVALID_INTENT = 10;
79     int DISMISS_OVERFLOW_MAX_REACHED = 11;
80     int DISMISS_SHORTCUT_REMOVED = 12;
81     int DISMISS_PACKAGE_REMOVED = 13;
82     int DISMISS_NO_BUBBLE_UP = 14;
83     int DISMISS_RELOAD_FROM_DISK = 15;
84     int DISMISS_USER_REMOVED = 16;
85     int DISMISS_SWITCH_TO_STACK = 17;
86 
87     /** Returns a binder that can be passed to an external process to manipulate Bubbles. */
createExternalInterface()88     default IBubbles createExternalInterface() {
89         return null;
90     }
91 
92     /**
93      * @return {@code true} if there is a bubble associated with the provided key and if its
94      * notification is hidden from the shade or there is a group summary associated with the
95      * provided key that is hidden from the shade because it has been dismissed but still has child
96      * bubbles active.
97      */
isBubbleNotificationSuppressedFromShade(String key, String groupKey)98     boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey);
99 
100     /**
101      * @return {@code true} if the current notification entry same as selected bubble
102      * notification entry and the stack is currently expanded.
103      */
isBubbleExpanded(String key)104     boolean isBubbleExpanded(String key);
105 
106     /** Tell the stack of bubbles to collapse. */
collapseStack()107     void collapseStack();
108 
109     /**
110      * Request the stack expand if needed, then select the specified Bubble as current.
111      * If no bubble exists for this entry, one is created.
112      *
113      * @param entry the notification for the bubble to be selected
114      */
expandStackAndSelectBubble(BubbleEntry entry)115     void expandStackAndSelectBubble(BubbleEntry entry);
116 
117     /**
118      * Request the stack expand if needed, then select the specified Bubble as current.
119      *
120      * @param bubble the bubble to be selected
121      */
expandStackAndSelectBubble(Bubble bubble)122     void expandStackAndSelectBubble(Bubble bubble);
123 
124     /**
125      * This method has different behavior depending on:
126      * - if an app bubble exists
127      * - if an app bubble is expanded
128      *
129      * If no app bubble exists, this will add and expand a bubble with the provided intent. The
130      * intent must be explicit (i.e. include a package name or fully qualified component class name)
131      * and the activity for it should be resizable.
132      *
133      * If an app bubble exists, this will toggle the visibility of it, i.e. if the app bubble is
134      * expanded, calling this method will collapse it. If the app bubble is not expanded, calling
135      * this method will expand it.
136      *
137      * These bubbles are <b>not</b> backed by a notification and remain until the user dismisses
138      * the bubble or bubble stack.
139      *
140      * Some notes:
141      * - Only one app bubble is supported at a time, regardless of users. Multi-users support is
142      * tracked in b/273533235.
143      * - Calling this method with a different intent than the existing app bubble will do nothing
144      *
145      * @param intent the intent to display in the bubble expanded view.
146      * @param user   the {@link UserHandle} of the user to start this activity for.
147      * @param icon   the {@link Icon} to use for the bubble view.
148      */
showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon)149     void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon);
150 
151     /** @return true if the specified {@code taskId} corresponds to app bubble's taskId. */
isAppBubbleTaskId(int taskId)152     boolean isAppBubbleTaskId(int taskId);
153 
154     /**
155 `    * @return a {@link SynchronousScreenCaptureListener} after performing a screenshot that may
156      * exclude the bubble layer, if one is present. The underlying
157      * {@link ScreenshotHardwareBuffer} can be accessed via
158      * {@link SynchronousScreenCaptureListener#getBuffer()} asynchronously and care should be taken
159      * to {@link HardwareBuffer#close()} the associated
160      * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.`
161      */
getScreenshotExcludingBubble(int displayId)162     SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId);
163 
164     /**
165      * @return a bubble that matches the provided shortcutId, if one exists.
166      */
167     @Nullable
getBubbleWithShortcutId(String shortcutId)168     Bubble getBubbleWithShortcutId(String shortcutId);
169 
170     /**
171      * We intercept notification entries (including group summaries) dismissed by the user when
172      * there is an active bubble associated with it. We do this so that developers can still
173      * cancel it (and hence the bubbles associated with it). However, these intercepted
174      * notifications should then be hidden from the shade since the user has cancelled them, so we
175      * {@link Bubble#setSuppressNotification}.  For the case of suppressed summaries, we also add
176      * {@link BubbleData#addSummaryToSuppress}.
177      *
178      * @param entry          the notification of the BubbleEntry should be removed.
179      * @param children       the list of child notification of the BubbleEntry from 1st param entry,
180      *                       this will be null if entry does have no children.
181      * @param removeCallback the remove callback for SystemUI side to remove notification, the int
182      *                       number should be list position of children list and use -1 for
183      *                       removing the parent notification.
184      * @return true if we want to intercept the dismissal of the entry, else false.
185      */
handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children, IntConsumer removeCallback, Executor callbackExecutor)186     boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
187             IntConsumer removeCallback, Executor callbackExecutor);
188 
189     /** Set the proxy to commnuicate with SysUi side components. */
setSysuiProxy(SysuiProxy proxy)190     void setSysuiProxy(SysuiProxy proxy);
191 
192     /** Set a listener to be notified of bubble expand events. */
setExpandListener(BubbleExpandListener listener)193     void setExpandListener(BubbleExpandListener listener);
194 
195     /**
196      * Called when new notification entry added.
197      *
198      * @param entry the {@link BubbleEntry} by the notification.
199      */
onEntryAdded(BubbleEntry entry)200     void onEntryAdded(BubbleEntry entry);
201 
202     /**
203      * Called when new notification entry updated.
204      *
205      * @param entry          the {@link BubbleEntry} by the notification.
206      * @param shouldBubbleUp {@code true} if this notification should bubble up.
207      * @param fromSystem     {@code true} if this update is from NotificationManagerService.
208      */
onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem)209     void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
210 
211     /**
212      * Called when new notification entry removed.
213      *
214      * @param entry the {@link BubbleEntry} by the notification.
215      */
onEntryRemoved(BubbleEntry entry)216     void onEntryRemoved(BubbleEntry entry);
217 
218     /**
219      * Called when NotificationListener has received adjusted notification rank and reapplied
220      * filtering and sorting. This is used to dismiss or create bubbles based on changes in
221      * permissions on the notification channel or the global setting.
222      *
223      * @param rankingMap     the updated ranking map from NotificationListenerService
224      * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
225      *                       bubble up
226      */
onRankingUpdated( RankingMap rankingMap, HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey)227     void onRankingUpdated(
228             RankingMap rankingMap,
229             HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
230 
231     /**
232      * Called when a notification channel is modified, in response to
233      * {@link NotificationListenerService#onNotificationChannelModified}.
234      *
235      * @param pkg              the package the notification channel belongs to.
236      * @param user             the user the notification channel belongs to.
237      * @param channel          the channel being modified.
238      * @param modificationType the type of modification that occurred to the channel.
239      */
onNotificationChannelModified( String pkg, UserHandle user, NotificationChannel channel, int modificationType)240     void onNotificationChannelModified(
241             String pkg,
242             UserHandle user,
243             NotificationChannel channel,
244             int modificationType);
245 
246     /**
247      * Called when notification panel is expanded or collapsed
248      */
onNotificationPanelExpandedChanged(boolean expanded)249     void onNotificationPanelExpandedChanged(boolean expanded);
250 
251     /**
252      * Called when the status bar has become visible or invisible (either permanently or
253      * temporarily).
254      */
onStatusBarVisibilityChanged(boolean visible)255     void onStatusBarVisibilityChanged(boolean visible);
256 
257     /** Called when system zen mode state changed. */
onZenStateChanged()258     void onZenStateChanged();
259 
260     /**
261      * Called when statusBar state changed.
262      *
263      * @param isShade {@code true} is state is SHADE.
264      */
onStatusBarStateChanged(boolean isShade)265     void onStatusBarStateChanged(boolean isShade);
266 
267     /**
268      * Called when the current user changed.
269      *
270      * @param newUserId the new user's id.
271      */
onUserChanged(int newUserId)272     void onUserChanged(int newUserId);
273 
274     /**
275      * Called when the current user profiles change.
276      *
277      * @param currentProfiles the user infos for the current profile.
278      */
onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles)279     void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles);
280 
281     /**
282      * Called when a user is removed.
283      *
284      * @param removedUserId the id of the removed user.
285      */
onUserRemoved(int removedUserId)286     void onUserRemoved(int removedUserId);
287 
288     /**
289      * A listener to be notified of bubble state changes, used by launcher to render bubbles in
290      * its process.
291      */
292     interface BubbleStateListener {
293         /**
294          * Called when the bubbles state changes.
295          */
onBubbleStateChange(BubbleBarUpdate update)296         void onBubbleStateChange(BubbleBarUpdate update);
297     }
298 
299     /** Listener to find out about stack expansion / collapse events. */
300     interface BubbleExpandListener {
301         /**
302          * Called when the expansion state of the bubble stack changes.
303          *
304          * @param isExpanding whether it's expanding or collapsing
305          * @param key         the notification key associated with bubble being expanded
306          */
onBubbleExpandChanged(boolean isExpanding, String key)307         void onBubbleExpandChanged(boolean isExpanding, String key);
308     }
309 
310     /** Listener to be notified when the flags on BubbleMetadata have changed. */
311     interface BubbleMetadataFlagListener {
312         /** Called when the flags on BubbleMetadata have changed for the provided bubble. */
onBubbleMetadataFlagChanged(Bubble bubble)313         void onBubbleMetadataFlagChanged(Bubble bubble);
314     }
315 
316     /** Listener to be notified when a pending intent has been canceled for a bubble. */
317     interface PendingIntentCanceledListener {
318         /** Called when the pending intent for a bubble has been canceled. */
onPendingIntentCanceled(Bubble bubble)319         void onPendingIntentCanceled(Bubble bubble);
320     }
321 
322     /** Callback to tell SysUi components execute some methods. */
323     interface SysuiProxy {
isNotificationPanelExpand(Consumer<Boolean> callback)324         void isNotificationPanelExpand(Consumer<Boolean> callback);
325 
getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback)326         void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
327 
getShouldRestoredEntries(Set<String> savedBubbleKeys, Consumer<List<BubbleEntry>> callback)328         void getShouldRestoredEntries(Set<String> savedBubbleKeys,
329                 Consumer<List<BubbleEntry>> callback);
330 
setNotificationInterruption(String key)331         void setNotificationInterruption(String key);
332 
requestNotificationShadeTopUi(boolean requestTopUi, String componentTag)333         void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag);
334 
notifyRemoveNotification(String key, int reason)335         void notifyRemoveNotification(String key, int reason);
336 
notifyInvalidateNotifications(String reason)337         void notifyInvalidateNotifications(String reason);
338 
updateNotificationBubbleButton(String key)339         void updateNotificationBubbleButton(String key);
340 
onStackExpandChanged(boolean shouldExpand)341         void onStackExpandChanged(boolean shouldExpand);
342 
onManageMenuExpandChanged(boolean menuExpanded)343         void onManageMenuExpandChanged(boolean menuExpanded);
344 
onUnbubbleConversation(String key)345         void onUnbubbleConversation(String key);
346     }
347 }
348