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