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