Notification Bubble Button
Button is visible on expanded notifications that are in the conversation section. Always visible if the notification has metadata & the user hasn't turned off bubbles for the device. Button is added to end of the template actions, making it visible if there were no other actions. Tapping it to make a button will change the app settings to "selected" and enable bubbles for that channel, if the bubble wasn't allowed otherwise. Tapping it to stop the bubble will disallow that channel from bubbling. Test: manual: - check bubble button on notif with long action text - check button is hidden when inline reply is focused - check no bubble button on non-conversation notifs - check create bubble from HUN - check create bubble from expanded notif - check that dismissing a bubble updates state of the button - check that getting new notif after dismissing bubble updates state of the button Bug: 138116133 Change-Id: Ic12204bd191530e94ea0400e16a9315a0d149252
This commit is contained in:
@@ -77,7 +77,7 @@ interface IStatusBarService
|
||||
void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply,
|
||||
in int notificationLocation, boolean modifiedBeforeSending);
|
||||
void onNotificationSettingsViewed(String key);
|
||||
void onNotificationBubbleChanged(String key, boolean isBubble);
|
||||
void onNotificationBubbleChanged(String key, boolean isBubble, int flags);
|
||||
void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
|
||||
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
|
||||
void clearInlineReplyUriPermissions(String key);
|
||||
|
||||
@@ -20,16 +20,34 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/notification_action_list_margin_top"
|
||||
android:layout_gravity="bottom">
|
||||
<com.android.internal.widget.NotificationActionListLayout
|
||||
android:id="@+id/actions"
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_action_list_height"
|
||||
android:paddingEnd="12dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
android:background="@color/notification_action_list_background_color"
|
||||
android:paddingEnd="12dp"
|
||||
>
|
||||
<!-- actions will be added here -->
|
||||
</com.android.internal.widget.NotificationActionListLayout>
|
||||
|
||||
<com.android.internal.widget.NotificationActionListLayout
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="@dimen/notification_action_list_height"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
android:background="@color/notification_action_list_background_color"
|
||||
>
|
||||
<!-- actions will be added here -->
|
||||
</com.android.internal.widget.NotificationActionListLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bubble_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:visibility="gone"
|
||||
android:scaleType="centerInside"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -3495,6 +3495,7 @@
|
||||
<java-symbol type="id" name="clip_children_set_tag" />
|
||||
<java-symbol type="id" name="clip_to_padding_tag" />
|
||||
<java-symbol type="id" name="clip_children_tag" />
|
||||
<java-symbol type="id" name="bubble_button" />
|
||||
<java-symbol type="dimen" name="messaging_avatar_size" />
|
||||
<java-symbol type="dimen" name="messaging_group_sending_progress_size" />
|
||||
<java-symbol type="dimen" name="messaging_image_rounding" />
|
||||
|
||||
@@ -14,16 +14,12 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9c0,1.39 0.32,2.69 0.88,3.86l1.53,-1.53C5.15,13.6 5,12.82 5,12c0,-3.86 3.14,-7 7,-7s7,3.14 7,7s-3.14,7 -7,7c-0.83,0 -1.62,-0.15 -2.35,-0.42l-1.53,1.53C9.3,20.67 10.61,21 12,21c4.97,0 9,-4.03 9,-9C21,7.03 16.97,3 12,3z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M12.99,15.99l2,0l0,-7l-7,0l0,2l3.59,0l-8.79,8.8l1.41,1.41l8.79,-8.79z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M22,12C22,12 22,12 22,12C22,12 22,12 22,12c0,0.56 -0.06,1.1 -0.15,1.64l-1.97,-0.33c0.15,-0.91 0.15,-1.84 -0.02,-2.75c-0.01,-0.03 -0.01,-0.07 -0.02,-0.1c-0.03,-0.18 -0.08,-0.36 -0.13,-0.54c-0.02,-0.08 -0.04,-0.16 -0.06,-0.24c-0.04,-0.14 -0.09,-0.27 -0.14,-0.41c-0.04,-0.12 -0.08,-0.24 -0.13,-0.35c-0.04,-0.09 -0.08,-0.18 -0.13,-0.27c-0.07,-0.15 -0.14,-0.3 -0.22,-0.45c-0.03,-0.05 -0.06,-0.09 -0.08,-0.14c-0.72,-1.26 -1.77,-2.31 -3.03,-3.03c-0.05,-0.03 -0.09,-0.06 -0.14,-0.08c-0.15,-0.08 -0.3,-0.15 -0.45,-0.22c-0.09,-0.04 -0.18,-0.09 -0.27,-0.13c-0.11,-0.05 -0.23,-0.09 -0.35,-0.13c-0.14,-0.05 -0.27,-0.1 -0.41,-0.14c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.18,-0.05 -0.36,-0.1 -0.54,-0.13c-0.03,-0.01 -0.07,-0.01 -0.1,-0.01c-0.95,-0.17 -1.93,-0.17 -2.88,0c-0.03,0.01 -0.07,0.01 -0.1,0.01c-0.18,0.04 -0.36,0.08 -0.54,0.13C9.85,4.3 9.77,4.32 9.69,4.34C9.55,4.38 9.42,4.44 9.28,4.49C9.17,4.53 9.05,4.57 8.93,4.61C8.84,4.65 8.75,4.7 8.66,4.74c-0.15,0.07 -0.3,0.14 -0.45,0.22C8.16,4.98 8.12,5.01 8.07,5.04C5.64,6.42 4,9.02 4,12c0,2.74 1.39,5.16 3.49,6.6c0.01,0.01 0.03,0.02 0.04,0.03c0.16,0.11 0.33,0.2 0.49,0.3c0.06,0.04 0.12,0.08 0.19,0.11c0.13,0.07 0.27,0.13 0.4,0.19c0.11,0.05 0.21,0.1 0.32,0.15c0.1,0.04 0.2,0.07 0.29,0.11c0.15,0.06 0.31,0.11 0.46,0.16c0.05,0.02 0.11,0.03 0.17,0.04c1.11,0.31 2.27,0.35 3.4,0.18l0.35,1.98c-0.54,0.09 -1.08,0.14 -1.62,0.14V22c-0.65,0 -1.28,-0.07 -1.9,-0.19c-0.01,0 -0.01,0 -0.02,0c-0.25,-0.05 -0.49,-0.11 -0.73,-0.18c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.19,-0.06 -0.37,-0.13 -0.55,-0.19c-0.13,-0.05 -0.26,-0.09 -0.39,-0.14c-0.13,-0.05 -0.25,-0.12 -0.38,-0.18c-0.18,-0.08 -0.35,-0.16 -0.53,-0.25c-0.07,-0.04 -0.14,-0.08 -0.21,-0.13c-0.22,-0.12 -0.43,-0.25 -0.64,-0.39c-0.01,-0.01 -0.02,-0.02 -0.04,-0.03c-0.51,-0.35 -1,-0.74 -1.45,-1.2l0,0C3.12,17.26 2,14.76 2,12c0,-2.76 1.12,-5.26 2.93,-7.07l0,0c0.45,-0.45 0.93,-0.84 1.44,-1.19C6.39,3.73 6.4,3.72 6.42,3.71c0.2,-0.14 0.41,-0.26 0.62,-0.38c0.08,-0.05 0.15,-0.09 0.23,-0.14c0.17,-0.09 0.33,-0.16 0.5,-0.24c0.13,-0.06 0.27,-0.13 0.4,-0.19C8.3,2.71 8.42,2.67 8.55,2.63c0.19,-0.07 0.38,-0.14 0.58,-0.2c0.07,-0.02 0.14,-0.03 0.21,-0.05C10.18,2.14 11.07,2 12,2c0.65,0 1.29,0.07 1.91,0.19c0,0 0,0 0,0c0.25,0.05 0.5,0.11 0.75,0.18c0.07,0.02 0.14,0.03 0.22,0.06c0.19,0.06 0.38,0.13 0.57,0.2c0.12,0.05 0.25,0.09 0.37,0.14c0.14,0.06 0.27,0.12 0.4,0.18c0.17,0.08 0.34,0.16 0.51,0.25c0.08,0.04 0.15,0.09 0.23,0.14c0.21,0.12 0.42,0.24 0.62,0.38c0.01,0.01 0.03,0.02 0.04,0.03c0.51,0.35 0.99,0.74 1.45,1.19c0.24,0.24 0.47,0.49 0.68,0.75c0.04,0.04 0.06,0.09 0.1,0.13c0.17,0.22 0.34,0.45 0.5,0.68c0.01,0.01 0.02,0.03 0.03,0.04c0.69,1.05 1.17,2.21 1.42,3.44c0,0 0,0.01 0,0.01c0.06,0.29 0.1,0.58 0.13,0.87c0.01,0.04 0.01,0.09 0.02,0.13C21.98,11.32 22,11.66 22,12zM18.5,15c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S20.43,15 18.5,15z"/>
|
||||
</vector>
|
||||
25
packages/SystemUI/res/drawable/ic_stop_bubble.xml
Normal file
25
packages/SystemUI/res/drawable/ic_stop_bubble.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21.98,18.32l-3.3,-3.3C20.47,15.11 21.89,16.53 21.98,18.32zM8.66,4.74C8.75,4.7 8.84,4.65 8.93,4.61c0.11,-0.05 0.23,-0.09 0.35,-0.13c0.14,-0.05 0.27,-0.1 0.41,-0.14C9.77,4.32 9.85,4.3 9.92,4.28c0.18,-0.05 0.36,-0.1 0.54,-0.13c0.03,-0.01 0.07,-0.01 0.1,-0.01c0.95,-0.17 1.93,-0.17 2.88,0c0.03,0.01 0.07,0.01 0.1,0.01c0.18,0.04 0.36,0.08 0.54,0.13c0.08,0.02 0.16,0.04 0.23,0.06c0.14,0.04 0.27,0.09 0.41,0.14c0.12,0.04 0.23,0.08 0.35,0.13c0.09,0.04 0.18,0.09 0.27,0.13c0.15,0.07 0.3,0.14 0.45,0.22c0.05,0.03 0.09,0.06 0.14,0.08c1.26,0.72 2.31,1.77 3.03,3.03c0.03,0.05 0.06,0.09 0.08,0.14c0.08,0.15 0.15,0.3 0.22,0.45c0.04,0.09 0.09,0.18 0.13,0.27c0.05,0.11 0.09,0.23 0.13,0.35c0.05,0.13 0.1,0.27 0.14,0.41c0.02,0.08 0.04,0.16 0.06,0.24c0.05,0.18 0.1,0.36 0.13,0.54c0.01,0.03 0.01,0.07 0.02,0.1c0.16,0.91 0.17,1.84 0.02,2.75l1.97,0.33C21.94,13.1 22,12.56 22,12c0,0 0,0 0,0s0,0 0,0c0,-0.34 -0.02,-0.68 -0.05,-1.01c0,-0.04 -0.01,-0.09 -0.02,-0.13c-0.03,-0.29 -0.07,-0.58 -0.13,-0.87c0,0 0,-0.01 0,-0.01c-0.25,-1.23 -0.73,-2.39 -1.42,-3.44c-0.01,-0.01 -0.02,-0.03 -0.03,-0.04c-0.15,-0.23 -0.32,-0.46 -0.5,-0.68c-0.03,-0.04 -0.06,-0.09 -0.1,-0.13c-0.21,-0.26 -0.44,-0.51 -0.68,-0.75c-0.45,-0.45 -0.94,-0.84 -1.45,-1.19c-0.01,-0.01 -0.03,-0.02 -0.04,-0.03c-0.2,-0.14 -0.41,-0.26 -0.62,-0.38c-0.08,-0.04 -0.15,-0.09 -0.23,-0.14c-0.17,-0.09 -0.34,-0.17 -0.51,-0.25c-0.13,-0.06 -0.26,-0.13 -0.4,-0.18c-0.12,-0.05 -0.25,-0.09 -0.37,-0.14c-0.19,-0.07 -0.38,-0.14 -0.57,-0.2c-0.07,-0.02 -0.14,-0.04 -0.22,-0.06c-0.25,-0.07 -0.5,-0.13 -0.75,-0.18c0,0 0,0 0,0C13.29,2.07 12.65,2 12,2c-0.93,0 -1.82,0.14 -2.67,0.37C9.26,2.39 9.19,2.41 9.12,2.43c-0.2,0.06 -0.39,0.13 -0.58,0.2C8.42,2.67 8.3,2.71 8.18,2.76c-0.14,0.06 -0.27,0.12 -0.4,0.19C7.61,3.03 7.44,3.1 7.27,3.19C7.19,3.24 7.12,3.29 7.04,3.33C7.03,3.34 7.02,3.34 7.01,3.35l1.48,1.48C8.55,4.8 8.6,4.77 8.66,4.74zM2.71,1.29L1.29,2.71l2.97,2.97C2.85,7.4 2,9.6 2,12c0,2.76 1.12,5.26 2.93,7.07l0,0c0.45,0.45 0.94,0.85 1.45,1.2c0.01,0.01 0.02,0.02 0.04,0.03c0.21,0.14 0.42,0.27 0.64,0.39c0.07,0.04 0.14,0.09 0.21,0.13c0.17,0.09 0.35,0.17 0.53,0.25c0.13,0.06 0.25,0.12 0.38,0.18c0.13,0.05 0.26,0.1 0.39,0.14c0.18,0.07 0.36,0.14 0.55,0.19c0.08,0.02 0.16,0.04 0.23,0.06c0.24,0.07 0.48,0.13 0.73,0.18c0.01,0 0.01,0 0.02,0C10.72,21.93 11.35,22 12,22v-0.01c0.54,0 1.08,-0.05 1.62,-0.14l-0.35,-1.98c-1.13,0.18 -2.29,0.13 -3.4,-0.18c-0.06,-0.02 -0.11,-0.03 -0.17,-0.04c-0.16,-0.05 -0.31,-0.11 -0.46,-0.16c-0.1,-0.04 -0.2,-0.07 -0.29,-0.11c-0.11,-0.05 -0.22,-0.1 -0.32,-0.15c-0.13,-0.06 -0.27,-0.12 -0.4,-0.19c-0.06,-0.03 -0.13,-0.08 -0.19,-0.11c-0.17,-0.1 -0.33,-0.19 -0.49,-0.3c-0.01,-0.01 -0.03,-0.02 -0.04,-0.03C5.39,17.16 4,14.74 4,12c0,-1.85 0.64,-3.54 1.7,-4.89l9.73,9.73C15.16,17.33 15,17.9 15,18.5c0,1.93 1.57,3.5 3.5,3.5c0.6,0 1.17,-0.16 1.66,-0.43l1.13,1.13l1.41,-1.41L2.71,1.29z"/>
|
||||
</vector>
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.android.systemui.bubbles;
|
||||
|
||||
import static android.app.Notification.FLAG_BUBBLE;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
|
||||
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
|
||||
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
|
||||
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
|
||||
@@ -30,7 +32,6 @@ import static android.view.View.VISIBLE;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
|
||||
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS;
|
||||
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
|
||||
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.systemui.statusbar.StatusBarState.SHADE;
|
||||
@@ -43,6 +44,9 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
@@ -83,6 +87,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
import com.android.systemui.statusbar.FeatureFlags;
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
||||
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
|
||||
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifCollection;
|
||||
@@ -169,6 +174,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
private final ZenModeController mZenModeController;
|
||||
private StatusBarStateListener mStatusBarStateListener;
|
||||
private INotificationManager mINotificationManager;
|
||||
|
||||
// Callback that updates BubbleOverflowActivity on data change.
|
||||
@Nullable private Runnable mOverflowCallback = null;
|
||||
@@ -293,11 +299,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
FeatureFlags featureFlags,
|
||||
DumpManager dumpManager,
|
||||
FloatingContentCoordinator floatingContentCoordinator,
|
||||
SysUiState sysUiState) {
|
||||
SysUiState sysUiState,
|
||||
INotificationManager notificationManager) {
|
||||
this(context, notificationShadeWindowController, statusBarStateController, shadeController,
|
||||
data, null /* synchronizer */, configurationController, interruptionStateProvider,
|
||||
zenModeController, notifUserManager, groupManager, entryManager,
|
||||
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
|
||||
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState,
|
||||
notificationManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +327,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
FeatureFlags featureFlags,
|
||||
DumpManager dumpManager,
|
||||
FloatingContentCoordinator floatingContentCoordinator,
|
||||
SysUiState sysUiState) {
|
||||
SysUiState sysUiState,
|
||||
INotificationManager notificationManager) {
|
||||
dumpManager.registerDumpable(TAG, this);
|
||||
mContext = context;
|
||||
mShadeController = shadeController;
|
||||
@@ -327,6 +336,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
mNotifUserManager = notifUserManager;
|
||||
mZenModeController = zenModeController;
|
||||
mFloatingContentCoordinator = floatingContentCoordinator;
|
||||
mINotificationManager = notificationManager;
|
||||
mZenModeController.addCallback(new ZenModeController.Callback() {
|
||||
@Override
|
||||
public void onZenChanged(int zen) {
|
||||
@@ -829,37 +839,43 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
* This method will collapse the shade, create the bubble without a flyout or dot, and suppress
|
||||
* the notification from appearing in the shade.
|
||||
*
|
||||
* @param entry the notification to show as a bubble.
|
||||
* @param entry the notification to change bubble state for.
|
||||
* @param shouldBubble whether the notification should show as a bubble or not.
|
||||
*/
|
||||
public void onUserCreatedBubbleFromNotification(NotificationEntry entry) {
|
||||
if (DEBUG_EXPERIMENTS || DEBUG_BUBBLE_CONTROLLER) {
|
||||
Log.d(TAG, "onUserCreatedBubble: " + entry.getKey());
|
||||
public void onUserChangedBubble(NotificationEntry entry, boolean shouldBubble) {
|
||||
NotificationChannel channel = entry.getChannel();
|
||||
final String appPkg = entry.getSbn().getPackageName();
|
||||
final int appUid = entry.getSbn().getUid();
|
||||
if (channel == null || appPkg == null) {
|
||||
return;
|
||||
}
|
||||
mShadeController.collapsePanel(true);
|
||||
entry.setFlagBubble(true);
|
||||
updateBubble(entry, true /* suppressFlyout */, false /* showInShade */);
|
||||
mUserCreatedBubbles.add(entry.getKey());
|
||||
mUserBlockedBubbles.remove(entry.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user has indicated that an active notification appearing as a bubble should
|
||||
* no longer be shown as a bubble.
|
||||
*
|
||||
* @param entry the notification to no longer show as a bubble.
|
||||
*/
|
||||
public void onUserDemotedBubbleFromNotification(NotificationEntry entry) {
|
||||
if (DEBUG_EXPERIMENTS || DEBUG_BUBBLE_CONTROLLER) {
|
||||
Log.d(TAG, "onUserDemotedBubble: " + entry.getKey());
|
||||
// Update the state in NotificationManagerService
|
||||
try {
|
||||
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
|
||||
mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
entry.setFlagBubble(false);
|
||||
removeBubble(entry, DISMISS_BLOCKED);
|
||||
mUserCreatedBubbles.remove(entry.getKey());
|
||||
if (BubbleExperimentConfig.isPackageWhitelistedToAutoBubble(
|
||||
mContext, entry.getSbn().getPackageName())) {
|
||||
// This package is whitelist but user demoted the bubble, let's save it so we don't
|
||||
// auto-bubble for the whitelist again.
|
||||
mUserBlockedBubbles.add(entry.getKey());
|
||||
|
||||
// Change the settings
|
||||
channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext,
|
||||
mINotificationManager, entry, channel);
|
||||
channel.setAllowBubbles(shouldBubble);
|
||||
try {
|
||||
int currentPref = mINotificationManager.getBubblePreferenceForPackage(appPkg, appUid);
|
||||
if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) {
|
||||
mINotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED);
|
||||
}
|
||||
mINotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
|
||||
if (shouldBubble) {
|
||||
mShadeController.collapsePanel(true);
|
||||
if (entry.getRow() != null) {
|
||||
entry.getRow().updateBubbleButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1007,14 +1023,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
} else {
|
||||
// Update the flag for SysUI
|
||||
bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
|
||||
if (bubble.getEntry().getRow() != null) {
|
||||
bubble.getEntry().getRow().updateBubbleButton();
|
||||
}
|
||||
|
||||
// Make sure NoMan knows it's not a bubble anymore so anyone querying it
|
||||
// will get right result back
|
||||
// Update the state in NotificationManagerService
|
||||
try {
|
||||
mBarService.onNotificationBubbleChanged(bubble.getKey(),
|
||||
false /* isBubble */);
|
||||
false /* isBubble */, 0 /* flags */);
|
||||
} catch (RemoteException e) {
|
||||
// Bad things have happened
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.bubbles.dagger;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.bubbles.BubbleController;
|
||||
@@ -64,14 +65,15 @@ public interface BubbleModule {
|
||||
FeatureFlags featureFlags,
|
||||
DumpManager dumpManager,
|
||||
FloatingContentCoordinator floatingContentCoordinator,
|
||||
SysUiState sysUiState) {
|
||||
SysUiState sysUiState,
|
||||
INotificationManager notifManager) {
|
||||
return new BubbleController(
|
||||
context,
|
||||
notificationShadeWindowController,
|
||||
statusBarStateController,
|
||||
shadeController,
|
||||
data,
|
||||
/* synchronizer */null,
|
||||
null /* synchronizer */,
|
||||
configurationController,
|
||||
interruptionStateProvider,
|
||||
zenModeController,
|
||||
@@ -82,6 +84,7 @@ public interface BubbleModule {
|
||||
featureFlags,
|
||||
dumpManager,
|
||||
floatingContentCoordinator,
|
||||
sysUiState);
|
||||
sysUiState,
|
||||
notifManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.notification;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
|
||||
/**
|
||||
* Helps SystemUI create notification channels.
|
||||
*/
|
||||
public class NotificationChannelHelper {
|
||||
private static final String TAG = "NotificationChannelHelper";
|
||||
|
||||
/** Creates a conversation channel based on the shortcut info or notification title. */
|
||||
public static NotificationChannel createConversationChannelIfNeeded(
|
||||
Context context,
|
||||
INotificationManager notificationManager,
|
||||
NotificationEntry entry,
|
||||
NotificationChannel channel) {
|
||||
if (!TextUtils.isEmpty(channel.getConversationId())) {
|
||||
return channel;
|
||||
}
|
||||
final String conversationId = entry.getSbn().getShortcutId(context);
|
||||
final String pkg = entry.getSbn().getPackageName();
|
||||
final int appUid = entry.getSbn().getUid();
|
||||
if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg)) {
|
||||
return channel;
|
||||
}
|
||||
|
||||
String name;
|
||||
if (entry.getRanking().getShortcutInfo() != null) {
|
||||
name = entry.getRanking().getShortcutInfo().getShortLabel().toString();
|
||||
} else {
|
||||
Bundle extras = entry.getSbn().getNotification().extras;
|
||||
String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
|
||||
if (TextUtils.isEmpty(nameString)) {
|
||||
nameString = extras.getString(Notification.EXTRA_TITLE);
|
||||
}
|
||||
name = nameString;
|
||||
}
|
||||
|
||||
// If this channel is not already a customized conversation channel, create
|
||||
// a custom channel
|
||||
try {
|
||||
// TODO: When shortcuts are enforced remove this and use the shortcut label for naming
|
||||
channel.setName(context.getString(
|
||||
R.string.notification_summary_message_format,
|
||||
name, channel.getName()));
|
||||
notificationManager.createConversationNotificationChannelForPackage(
|
||||
pkg, appUid, entry.getSbn().getKey(), channel,
|
||||
conversationId);
|
||||
channel = notificationManager.getConversationNotificationChannel(
|
||||
context.getOpPackageName(), UserHandle.getUserId(appUid), pkg,
|
||||
channel.getId(), false, conversationId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Could not create conversation channel", e);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,7 @@ import com.android.internal.widget.CachingIconView;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.bubbles.BubbleController;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||
@@ -579,6 +580,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
}
|
||||
}
|
||||
|
||||
/** Call when bubble state has changed and the button on the notification should be updated. */
|
||||
public void updateBubbleButton() {
|
||||
for (NotificationContentView l : mLayouts) {
|
||||
l.updateBubbleButton(mEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateShelfIconColor() {
|
||||
StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
|
||||
@@ -1086,6 +1094,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
updateClickAndFocus();
|
||||
}
|
||||
|
||||
/** The click listener for the bubble button. */
|
||||
public View.OnClickListener getBubbleClickListener() {
|
||||
return new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Dependency.get(BubbleController.class)
|
||||
.onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
|
||||
mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void updateClickAndFocus() {
|
||||
boolean normalChild = !isChildInGroup() || isGroupExpanded();
|
||||
boolean clickable = mOnClickListener != null && normalChild;
|
||||
@@ -1267,7 +1287,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
return mNotificationColor;
|
||||
}
|
||||
|
||||
private void updateNotificationColor() {
|
||||
public void updateNotificationColor() {
|
||||
Configuration currentConfig = getResources().getConfiguration();
|
||||
boolean nightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
|
||||
== Configuration.UI_MODE_NIGHT_YES;
|
||||
@@ -1613,6 +1633,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
mFalsingManager = falsingManager;
|
||||
mStatusbarStateController = statusBarStateController;
|
||||
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
||||
for (NotificationContentView l : mLayouts) {
|
||||
l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
private void initDimens() {
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -47,6 +52,7 @@ import com.android.systemui.statusbar.SmartReplyController;
|
||||
import com.android.systemui.statusbar.TransformableView;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
||||
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
|
||||
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||
@@ -118,6 +124,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
private NotificationGroupManager mGroupManager;
|
||||
private RemoteInputController mRemoteInputController;
|
||||
private Runnable mExpandedVisibleListener;
|
||||
private PeopleNotificationIdentifier mPeopleIdentifier;
|
||||
/**
|
||||
* List of listeners for when content views become inactive (i.e. not the showing view).
|
||||
*/
|
||||
@@ -454,6 +461,9 @@ public class NotificationContentView extends FrameLayout {
|
||||
mExpandedChild = child;
|
||||
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
|
||||
mContainingNotification);
|
||||
if (mContainingNotification != null) {
|
||||
applyBubbleAction(mExpandedChild, mContainingNotification.getEntry());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,6 +503,9 @@ public class NotificationContentView extends FrameLayout {
|
||||
mHeadsUpChild = child;
|
||||
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
|
||||
mContainingNotification);
|
||||
if (mContainingNotification != null) {
|
||||
applyBubbleAction(mHeadsUpChild, mContainingNotification.getEntry());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1138,6 +1151,8 @@ public class NotificationContentView extends FrameLayout {
|
||||
mForceSelectNextLayout = true;
|
||||
mPreviousExpandedRemoteInputIntent = null;
|
||||
mPreviousHeadsUpRemoteInputIntent = null;
|
||||
applyBubbleAction(mExpandedChild, entry);
|
||||
applyBubbleAction(mHeadsUpChild, entry);
|
||||
}
|
||||
|
||||
private void updateAllSingleLineViews() {
|
||||
@@ -1308,6 +1323,58 @@ public class NotificationContentView extends FrameLayout {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to update state of the bubble button (i.e. does it show bubble or unbubble or no
|
||||
* icon at all).
|
||||
*
|
||||
* @param entry the new entry to use.
|
||||
*/
|
||||
public void updateBubbleButton(NotificationEntry entry) {
|
||||
applyBubbleAction(mExpandedChild, entry);
|
||||
}
|
||||
|
||||
private boolean isBubblesEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, 0) == 1;
|
||||
}
|
||||
|
||||
private void applyBubbleAction(View layout, NotificationEntry entry) {
|
||||
if (layout == null || mContainingNotification == null || mPeopleIdentifier == null) {
|
||||
return;
|
||||
}
|
||||
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
|
||||
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
|
||||
if (bubbleButton == null || actionContainer == null) {
|
||||
return;
|
||||
}
|
||||
boolean isPerson =
|
||||
mPeopleIdentifier.getPeopleNotificationType(entry.getSbn(), entry.getRanking())
|
||||
!= PeopleNotificationIdentifier.TYPE_NON_PERSON;
|
||||
boolean showButton = isBubblesEnabled()
|
||||
&& isPerson
|
||||
&& entry.getBubbleMetadata() != null;
|
||||
if (showButton) {
|
||||
Drawable d = mContext.getResources().getDrawable(entry.isBubble()
|
||||
? R.drawable.ic_stop_bubble
|
||||
: R.drawable.ic_create_bubble);
|
||||
mContainingNotification.updateNotificationColor();
|
||||
final int tint = mContainingNotification.getNotificationColor();
|
||||
d.setTint(tint);
|
||||
|
||||
String contentDescription = mContext.getResources().getString(entry.isBubble()
|
||||
? R.string.notification_conversation_unbubble
|
||||
: R.string.notification_conversation_bubble);
|
||||
|
||||
bubbleButton.setContentDescription(contentDescription);
|
||||
bubbleButton.setImageDrawable(d);
|
||||
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
|
||||
bubbleButton.setVisibility(VISIBLE);
|
||||
actionContainer.setVisibility(VISIBLE);
|
||||
} else {
|
||||
bubbleButton.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void applySmartReplyView(
|
||||
SmartRepliesAndActions smartRepliesAndActions,
|
||||
NotificationEntry entry) {
|
||||
@@ -1512,6 +1579,10 @@ public class NotificationContentView extends FrameLayout {
|
||||
mContainingNotification = containingNotification;
|
||||
}
|
||||
|
||||
public void setPeopleNotificationIdentifier(PeopleNotificationIdentifier peopleIdentifier) {
|
||||
mPeopleIdentifier = peopleIdentifier;
|
||||
}
|
||||
|
||||
public void requestSelectLayout(boolean needsAnimation) {
|
||||
selectLayout(needsAnimation, false);
|
||||
}
|
||||
|
||||
@@ -40,11 +40,9 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.text.TextUtils;
|
||||
import android.transition.ChangeBounds;
|
||||
@@ -53,7 +51,6 @@ import android.transition.TransitionManager;
|
||||
import android.transition.TransitionSet;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.ImageView;
|
||||
@@ -64,6 +61,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settingslib.notification.ConversationIconFactory;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
|
||||
@@ -205,7 +203,8 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
}
|
||||
mShortcutInfo = entry.getRanking().getShortcutInfo();
|
||||
|
||||
createConversationChannelIfNeeded();
|
||||
mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
|
||||
getContext(), mINotificationManager, entry, mNotificationChannel);
|
||||
|
||||
bindHeader();
|
||||
bindActions();
|
||||
@@ -214,27 +213,6 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
done.setOnClickListener(mOnDone);
|
||||
}
|
||||
|
||||
void createConversationChannelIfNeeded() {
|
||||
// If this channel is not already a customized conversation channel, create
|
||||
// a custom channel
|
||||
if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
|
||||
try {
|
||||
// TODO: remove
|
||||
mNotificationChannel.setName(mContext.getString(
|
||||
R.string.notification_summary_message_format,
|
||||
getName(), mNotificationChannel.getName()));
|
||||
mINotificationManager.createConversationNotificationChannelForPackage(
|
||||
mPackageName, mAppUid, mSbn.getKey(), mNotificationChannel,
|
||||
mConversationId);
|
||||
mNotificationChannel = mINotificationManager.getConversationNotificationChannel(
|
||||
mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName,
|
||||
mNotificationChannel.getId(), false, mConversationId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Could not create conversation channel", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindActions() {
|
||||
|
||||
// TODO: b/152050825
|
||||
@@ -318,24 +296,6 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private void bindName() {
|
||||
TextView name = findViewById(R.id.name);
|
||||
name.setText(getName());
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
if (mShortcutInfo != null) {
|
||||
return mShortcutInfo.getShortLabel().toString();
|
||||
} else {
|
||||
Bundle extras = mSbn.getNotification().extras;
|
||||
String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
|
||||
if (TextUtils.isEmpty(nameString)) {
|
||||
nameString = extras.getString(Notification.EXTRA_TITLE);
|
||||
}
|
||||
return nameString;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindPackage() {
|
||||
ApplicationInfo info;
|
||||
try {
|
||||
|
||||
@@ -42,6 +42,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.res.Resources;
|
||||
@@ -273,7 +274,8 @@ public class BubbleControllerTest extends SysuiTestCase {
|
||||
mFeatureFlagsOldPipeline,
|
||||
mDumpManager,
|
||||
mFloatingContentCoordinator,
|
||||
mSysUiState);
|
||||
mSysUiState,
|
||||
mock(INotificationManager.class));
|
||||
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
|
||||
mBubbleController.setExpandListener(mBubbleExpandListener);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.res.Resources;
|
||||
@@ -250,7 +251,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
|
||||
mFeatureFlagsNewPipeline,
|
||||
mDumpManager,
|
||||
mFloatingContentCoordinator,
|
||||
mSysUiState);
|
||||
mSysUiState,
|
||||
mock(INotificationManager.class));
|
||||
mBubbleController.addNotifCallback(mNotifCallback);
|
||||
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
|
||||
mBubbleController.setExpandListener(mBubbleExpandListener);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.bubbles;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
@@ -54,12 +55,14 @@ public class TestableBubbleController extends BubbleController {
|
||||
FeatureFlags featureFlags,
|
||||
DumpManager dumpManager,
|
||||
FloatingContentCoordinator floatingContentCoordinator,
|
||||
SysUiState sysUiState) {
|
||||
SysUiState sysUiState,
|
||||
INotificationManager notificationManager) {
|
||||
super(context,
|
||||
notificationShadeWindowController, statusBarStateController, shadeController,
|
||||
data, Runnable::run, configurationController, interruptionStateProvider,
|
||||
zenModeController, lockscreenUserManager, groupManager, entryManager,
|
||||
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
|
||||
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState,
|
||||
notificationManager);
|
||||
setInflateSynchronously(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public interface NotificationDelegate {
|
||||
/**
|
||||
* Called when the state of {@link Notification#FLAG_BUBBLE} is changed.
|
||||
*/
|
||||
void onNotificationBubbleChanged(String key, boolean isBubble);
|
||||
void onNotificationBubbleChanged(String key, boolean isBubble, int flags);
|
||||
/**
|
||||
* Called when the state of {@link Notification.BubbleMetadata#FLAG_SUPPRESS_NOTIFICATION}
|
||||
* changes.
|
||||
|
||||
@@ -1196,14 +1196,7 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationBubbleChanged(String key, boolean isBubble) {
|
||||
String pkg;
|
||||
synchronized (mNotificationLock) {
|
||||
NotificationRecord r = mNotificationsByKey.get(key);
|
||||
pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
|
||||
}
|
||||
boolean isAppForeground = pkg != null
|
||||
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
|
||||
public void onNotificationBubbleChanged(String key, boolean isBubble, int flags) {
|
||||
synchronized (mNotificationLock) {
|
||||
NotificationRecord r = mNotificationsByKey.get(key);
|
||||
if (r != null) {
|
||||
@@ -1220,8 +1213,13 @@ public class NotificationManagerService extends SystemService {
|
||||
// be applied there.
|
||||
r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
|
||||
r.setFlagBubbleRemoved(false);
|
||||
if (r.getNotification().getBubbleMetadata() != null) {
|
||||
r.getNotification().getBubbleMetadata().setFlags(flags);
|
||||
}
|
||||
// Force isAppForeground true here, because for sysui's purposes we
|
||||
// want to adjust the flag behaviour.
|
||||
mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
|
||||
r, isAppForeground));
|
||||
r, true /* isAppForeground*/));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1380,11 +1380,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationBubbleChanged(String key, boolean isBubble) {
|
||||
public void onNotificationBubbleChanged(String key, boolean isBubble, int flags) {
|
||||
enforceStatusBarService();
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mNotificationDelegate.onNotificationBubbleChanged(key, isBubble);
|
||||
mNotificationDelegate.onNotificationBubbleChanged(key, isBubble, flags);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
|
||||
@@ -5548,7 +5548,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
assertTrue((notifsBefore[0].getNotification().flags & FLAG_BUBBLE) != 0);
|
||||
|
||||
// Notify we're not a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false, 0);
|
||||
waitForIdle();
|
||||
|
||||
// Make sure we are not a bubble
|
||||
@@ -5587,7 +5587,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
reset(mListeners);
|
||||
|
||||
// Notify we are now a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true, 0);
|
||||
waitForIdle();
|
||||
|
||||
// Make sure we are a bubble
|
||||
@@ -5618,7 +5618,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
|
||||
|
||||
// Notify we are now a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true, 0);
|
||||
waitForIdle();
|
||||
|
||||
// We still wouldn't be a bubble because the notification didn't meet requirements
|
||||
@@ -5646,7 +5646,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
assertFalse(recordToCheck.isFlagBubbleRemoved());
|
||||
|
||||
// Notify we're not a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false, 0);
|
||||
waitForIdle();
|
||||
// Flag should be modified
|
||||
recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
|
||||
@@ -5681,14 +5681,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
assertFalse(recordToCheck.isFlagBubbleRemoved());
|
||||
|
||||
// Notify we're not a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false, 0);
|
||||
waitForIdle();
|
||||
// Flag should be modified
|
||||
recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
|
||||
assertTrue(recordToCheck.isFlagBubbleRemoved());
|
||||
|
||||
// Notify we are a bubble
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
|
||||
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true, 0);
|
||||
waitForIdle();
|
||||
// And the flag is reset
|
||||
assertFalse(recordToCheck.isFlagBubbleRemoved());
|
||||
|
||||
Reference in New Issue
Block a user