diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 1dbd69c67831c..24fe0638b0910 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -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);
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 425801991927e..ec54091e5a20b 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -20,16 +20,34 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_action_list_margin_top"
android:layout_gravity="bottom">
-
-
-
+
+
+
+
+
+
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ef1e8b74b05fb..69862b2e2820e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3495,6 +3495,7 @@
+
diff --git a/packages/SystemUI/res/drawable/ic_create_bubble.xml b/packages/SystemUI/res/drawable/ic_create_bubble.xml
index 1947f58f8f5e6..d58e9a347a2ff 100644
--- a/packages/SystemUI/res/drawable/ic_create_bubble.xml
+++ b/packages/SystemUI/res/drawable/ic_create_bubble.xml
@@ -14,16 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
-
-
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_stop_bubble.xml b/packages/SystemUI/res/drawable/ic_stop_bubble.xml
new file mode 100644
index 0000000000000..11bc741a4f178
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_stop_bubble.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9d885fd3c207e..878a88452f438 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -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
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index e84e932c9e61d..72d646e0554d5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -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);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
new file mode 100644
index 0000000000000..ff945d15a4ed6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5c578dfc57441..ba060636500a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -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() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 3c3f1b21fb3c0..bd1745eaa0284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -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);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 290784ddbffec..a27199370b163 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -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 {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 037f04ec1d7cd..95f0bd52f4ee7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -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);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 545de210d5b50..e8721a89c3b4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -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);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index f4861028e81ae..7815ae78823ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -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);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index b8140be2e2663..1051423ea17fb 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -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.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2a81e37b8fa59..c11a80ddf352f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -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*/));
}
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d7a0c9871b483..289bf66e1add1 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -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);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e6e84eeab4ed2..3cd0e92964ec1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -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());