Migrate Bubbles to wm-shell (2/n)

Remove sysui dependency and dagger in Bubble and BubbleData class.

Bug: 161980186
Test: atest SystemUITests
Change-Id: Ic66787e194414e5a4b8c98226598bbf274a285a9
This commit is contained in:
Tony Huang
2020-09-24 11:59:40 +08:00
parent 4a681f2c42
commit 99162817d5
8 changed files with 330 additions and 208 deletions

View File

@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -35,16 +36,18 @@ import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
/**
@@ -169,11 +172,10 @@ class Bubble implements BubbleViewProvider {
}
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final NotificationEntry e,
Bubble(@NonNull final BubbleEntry entry,
@Nullable final BubbleController.NotificationSuppressionChangedListener listener,
final BubbleController.PendingIntentCanceledListener intentCancelListener) {
Objects.requireNonNull(e);
mKey = e.getKey();
mKey = entry.getKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
if (mIntent != null) {
@@ -181,7 +183,7 @@ class Bubble implements BubbleViewProvider {
}
intentCancelListener.onPendingIntentCanceled(this);
};
setEntry(e);
setEntry(entry);
}
@Override
@@ -294,8 +296,8 @@ class Bubble implements BubbleViewProvider {
}
/**
* Sets whether this bubble is considered visually interruptive. Normally pulled from the
* {@link NotificationEntry}, this method is purely for testing.
* Sets whether this bubble is considered visually interruptive. This method is purely for
* testing.
*/
@VisibleForTesting
void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) {
@@ -388,30 +390,28 @@ class Bubble implements BubbleViewProvider {
/**
* Sets the entry associated with this bubble.
*/
void setEntry(@NonNull final NotificationEntry entry) {
void setEntry(@NonNull final BubbleEntry entry) {
Objects.requireNonNull(entry);
Objects.requireNonNull(entry.getSbn());
mLastUpdated = entry.getSbn().getPostTime();
mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
mPackageName = entry.getSbn().getPackageName();
mUser = entry.getSbn().getUser();
mLastUpdated = entry.getStatusBarNotification().getPostTime();
mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
mPackageName = entry.getStatusBarNotification().getPackageName();
mUser = entry.getStatusBarNotification().getUser();
mTitle = getTitle(entry);
mIsClearable = entry.isClearable();
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
mChannelId = entry.getSbn().getNotification().getChannelId();
mNotificationId = entry.getSbn().getId();
mAppUid = entry.getSbn().getUid();
mInstanceId = entry.getSbn().getInstanceId();
mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null);
mMetadataShortcutId = (entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getShortcutId() : null);
mChannelId = entry.getStatusBarNotification().getNotification().getChannelId();
mNotificationId = entry.getStatusBarNotification().getId();
mAppUid = entry.getStatusBarNotification().getUid();
mInstanceId = entry.getStatusBarNotification().getInstanceId();
mFlyoutMessage = extractFlyoutMessage(entry);
if (entry.getRanking() != null) {
mShortcutInfo = entry.getRanking().getShortcutInfo();
mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
if (entry.getRanking().getChannel() != null) {
mIsImportantConversation =
entry.getRanking().getChannel().isImportantConversation();
}
}
if (entry.getBubbleMetadata() != null) {
mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId();
mFlags = entry.getBubbleMetadata().getFlags();
mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
@@ -433,8 +433,11 @@ class Bubble implements BubbleViewProvider {
}
mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
}
mIsImportantConversation =
entry.getChannel() != null && entry.getChannel().isImportantConversation();
mIsClearable = entry.isClearable();
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
}
@Nullable
@@ -735,9 +738,75 @@ class Bubble implements BubbleViewProvider {
}
@Nullable
private static String getTitle(@NonNull final NotificationEntry e) {
final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
Notification.EXTRA_TITLE);
private static String getTitle(@NonNull final BubbleEntry e) {
final CharSequence titleCharSeq = e.getStatusBarNotification()
.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE);
return titleCharSeq == null ? null : titleCharSeq.toString();
}
/**
* Returns our best guess for the most relevant text summary of the latest update to this
* notification, based on its type. Returns null if there should not be an update message.
*/
@NonNull
static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) {
Objects.requireNonNull(entry);
final Notification underlyingNotif = entry.getStatusBarNotification().getNotification();
final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
Notification.EXTRA_IS_GROUP_CONVERSATION);
try {
if (Notification.BigTextStyle.class.equals(style)) {
// Return the big text, it is big so probably important. If it's not there use the
// normal text.
CharSequence bigText =
underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
bubbleMessage.message = !TextUtils.isEmpty(bigText)
? bigText
: underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
return bubbleMessage;
} else if (Notification.MessagingStyle.class.equals(style)) {
final List<Notification.MessagingStyle.Message> messages =
Notification.MessagingStyle.Message.getMessagesFromBundleArray(
(Parcelable[]) underlyingNotif.extras.get(
Notification.EXTRA_MESSAGES));
final Notification.MessagingStyle.Message latestMessage =
Notification.MessagingStyle.findLatestIncomingMessage(messages);
if (latestMessage != null) {
bubbleMessage.message = latestMessage.getText();
Person sender = latestMessage.getSenderPerson();
bubbleMessage.senderName = sender != null ? sender.getName() : null;
bubbleMessage.senderAvatar = null;
bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
return bubbleMessage;
}
} else if (Notification.InboxStyle.class.equals(style)) {
CharSequence[] lines =
underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
// Return the last line since it should be the most recent.
if (lines != null && lines.length > 0) {
bubbleMessage.message = lines[lines.length - 1];
return bubbleMessage;
}
} else if (Notification.MediaStyle.class.equals(style)) {
// Return nothing, media updates aren't typically useful as a text update.
return bubbleMessage;
} else {
// Default to text extra.
bubbleMessage.message =
underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
return bubbleMessage;
}
} catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
// No use crashing, we'll just return null and the caller will assume there's no update
// message.
e.printStackTrace();
}
return bubbleMessage;
}
}

View File

@@ -345,7 +345,42 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
/**
* Injected constructor. See {@link BubbleModule}.
*/
public BubbleController(Context context,
public static BubbleController create(Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
ConfigurationController configurationController,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
BubbleDataRepository dataRepository,
SysUiState sysUiState,
INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
return new BubbleController(context, notificationShadeWindowController,
statusBarStateController, shadeController, new BubbleData(context), synchronizer,
configurationController, interruptionStateProvider, zenModeController,
notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
dumpManager, floatingContentCoordinator, dataRepository, sysUiState,
notificationManager, statusBarService, windowManager, windowManagerShellWrapper,
launcherApps);
}
/**
* Testing constructor.
*/
@VisibleForTesting
BubbleController(Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
@@ -1120,9 +1155,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
b.setEntry(notif);
b.setEntry(notifToBubbleEntry(notif));
} else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
Bubble bubble = mBubbleData.getOrCreateBubble(
notifToBubbleEntry(notif), null /* persistedBubble */);
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
@@ -1208,8 +1244,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
mBubbleData.removeSuppressedSummary(groupKey);
// Remove any associated bubble children with the summary
final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
groupKey, mNotificationEntryManager);
final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
for (int i = 0; i < bubbleChildren.size(); i++) {
removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
}
@@ -1255,6 +1290,25 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
/**
* Retrieves any bubbles that are part of the notification group represented by the provided
* group key.
*/
private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) {
ArrayList<Bubble> bubbleChildren = new ArrayList<>();
if (groupKey == null) {
return bubbleChildren;
}
for (Bubble bubble : mBubbleData.getActiveBubbles()) {
final NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey());
if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
bubbleChildren.add(bubble);
}
}
return bubbleChildren;
}
private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble,
final boolean autoExpand) {
Objects.requireNonNull(entry);
@@ -1365,8 +1419,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
if (entry != null) {
final String groupKey = entry.getSbn().getGroupKey();
if (mBubbleData.getBubblesInGroup(
groupKey, mNotificationEntryManager).isEmpty()) {
if (getBubblesInGroup(groupKey).isEmpty()) {
// Time to potentially remove the summary
for (NotifCallback cb : mCallbacks) {
cb.maybeCancelSummary(entry);
@@ -1449,8 +1502,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
String groupKey = entry.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
groupKey, mNotificationEntryManager);
ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1694,4 +1746,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
}
static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
e.shouldSuppressPeek());
}
}

View File

@@ -36,8 +36,6 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -52,8 +50,6 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.inject.Inject;
/**
* Keeps track of active bubbles.
*/
@@ -154,12 +150,11 @@ public class BubbleData {
* associated with it). This list is used to check if the summary should be hidden from the
* shade.
*
* Key: group key of the NotificationEntry
* Value: key of the NotificationEntry
* Key: group key of the notification
* Value: key of the notification
*/
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
@Inject
public BubbleData(Context context) {
mContext = context;
mBubbles = new ArrayList<>();
@@ -205,6 +200,11 @@ public class BubbleData {
return mSelectedBubble;
}
/** Return a read-only current active bubble lists. */
public List<Bubble> getActiveBubbles() {
return Collections.unmodifiableList(mBubbles);
}
public void setExpanded(boolean expanded) {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "setExpanded: " + expanded);
@@ -235,8 +235,8 @@ public class BubbleData {
* @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from
* the overflow that was persisted over reboot.
*/
Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) {
String key = entry != null ? entry.getKey() : persistedBubble.getKey();
public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) {
String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey();
Bubble bubbleToReturn = getBubbleInStackWithKey(key);
if (bubbleToReturn == null) {
@@ -266,7 +266,7 @@ public class BubbleData {
/**
* When this method is called it is expected that all info in the bubble has completed loading.
* @see Bubble#inflate(BubbleViewInfoTask.Callback, Context,
* BubbleStackView, BubbleIconFactory).
* BubbleStackView, BubbleIconFactory, boolean).
*/
void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
if (DEBUG_BUBBLE_DATA) {
@@ -329,7 +329,7 @@ public class BubbleData {
* Retrieves the notif entry key of the summary associated with the provided group key.
*
* @param groupKey the group to look up
* @return the key for the {@link NotificationEntry} that is the summary of this group.
* @return the key for the notification that is the summary of this group.
*/
String getSummaryKey(String groupKey) {
return mSuppressedGroupKeys.get(groupKey);
@@ -349,25 +349,6 @@ public class BubbleData {
return mSuppressedGroupKeys.containsKey(groupKey);
}
/**
* Retrieves any bubbles that are part of the notification group represented by the provided
* group key.
*/
ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
NotificationEntryManager nem) {
ArrayList<Bubble> bubbleChildren = new ArrayList<>();
if (groupKey == null) {
return bubbleChildren;
}
for (Bubble b : mBubbles) {
final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
bubbleChildren.add(b);
}
}
return bubbleChildren;
}
/**
* Removes bubbles from the given package whose shortcut are not in the provided list of valid
* shortcuts.

View File

@@ -0,0 +1,98 @@
/*
* 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.bubbles;
import android.app.Notification.BubbleMetadata;
import android.app.NotificationManager.Policy;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Represents a notification with needed data and flag for bubbles.
*
* @see Bubble
*/
public class BubbleEntry {
private StatusBarNotification mSbn;
private Ranking mRanking;
private boolean mIsClearable;
private boolean mShouldSuppressNotificationDot;
private boolean mShouldSuppressNotificationList;
private boolean mShouldSuppressPeek;
public BubbleEntry(@NonNull StatusBarNotification sbn,
Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot,
boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) {
mSbn = sbn;
mRanking = ranking;
mIsClearable = isClearable;
mShouldSuppressNotificationDot = shouldSuppressNotificationDot;
mShouldSuppressNotificationList = shouldSuppressNotificationList;
mShouldSuppressPeek = shouldSuppressPeek;
}
/** @return the {@link StatusBarNotification} for this entry. */
@NonNull
public StatusBarNotification getStatusBarNotification() {
return mSbn;
}
/** @return the {@link Ranking} for this entry. */
public Ranking getRanking() {
return mRanking;
}
/** @return the key in the {@link StatusBarNotification}. */
public String getKey() {
return mSbn.getKey();
}
/** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
@Nullable
public BubbleMetadata getBubbleMetadata() {
return getStatusBarNotification().getNotification().getBubbleMetadata();
}
/** @return true if this notification is clearable. */
public boolean isClearable() {
return mIsClearable;
}
/** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */
public boolean shouldSuppressNotificationDot() {
return mShouldSuppressNotificationDot;
}
/**
* @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
* set for this notification.
*/
public boolean shouldSuppressNotificationList() {
return mShouldSuppressNotificationList;
}
/** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */
public boolean shouldSuppressPeek() {
return mShouldSuppressPeek;
}
}

View File

@@ -22,8 +22,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.app.Notification;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -36,8 +34,6 @@ import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
@@ -47,10 +43,8 @@ import androidx.annotation.Nullable;
import com.android.internal.graphics.ColorUtils;
import com.android.launcher3.icons.BitmapInfo;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
/**
@@ -208,73 +202,6 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
}
}
/**
* Returns our best guess for the most relevant text summary of the latest update to this
* notification, based on its type. Returns null if there should not be an update message.
*/
@NonNull
static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
Objects.requireNonNull(entry);
final Notification underlyingNotif = entry.getSbn().getNotification();
final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
Notification.EXTRA_IS_GROUP_CONVERSATION);
try {
if (Notification.BigTextStyle.class.equals(style)) {
// Return the big text, it is big so probably important. If it's not there use the
// normal text.
CharSequence bigText =
underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
bubbleMessage.message = !TextUtils.isEmpty(bigText)
? bigText
: underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
return bubbleMessage;
} else if (Notification.MessagingStyle.class.equals(style)) {
final List<Notification.MessagingStyle.Message> messages =
Notification.MessagingStyle.Message.getMessagesFromBundleArray(
(Parcelable[]) underlyingNotif.extras.get(
Notification.EXTRA_MESSAGES));
final Notification.MessagingStyle.Message latestMessage =
Notification.MessagingStyle.findLatestIncomingMessage(messages);
if (latestMessage != null) {
bubbleMessage.message = latestMessage.getText();
Person sender = latestMessage.getSenderPerson();
bubbleMessage.senderName = sender != null ? sender.getName() : null;
bubbleMessage.senderAvatar = null;
bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
return bubbleMessage;
}
} else if (Notification.InboxStyle.class.equals(style)) {
CharSequence[] lines =
underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
// Return the last line since it should be the most recent.
if (lines != null && lines.length > 0) {
bubbleMessage.message = lines[lines.length - 1];
return bubbleMessage;
}
} else if (Notification.MediaStyle.class.equals(style)) {
// Return nothing, media updates aren't typically useful as a text update.
return bubbleMessage;
} else {
// Default to text extra.
bubbleMessage.message =
underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
return bubbleMessage;
}
} catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
// No use crashing, we'll just return null and the caller will assume there's no update
// message.
e.printStackTrace();
}
return bubbleMessage;
}
@Nullable
static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
Objects.requireNonNull(context);

View File

@@ -23,7 +23,6 @@ import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.bubbles.BubbleDataRepository;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
@@ -59,7 +58,6 @@ public interface BubbleModule {
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
@@ -77,12 +75,11 @@ public interface BubbleModule {
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
return new BubbleController(
return BubbleController.create(
context,
notificationShadeWindowController,
statusBarStateController,
shadeController,
data,
null /* synchronizer */,
configurationController,
interruptionStateProvider,

View File

@@ -16,8 +16,6 @@
package com.android.systemui.bubbles;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -29,7 +27,9 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -39,10 +39,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleData.TimeSource;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.google.common.collect.ImmutableList;
@@ -68,15 +64,15 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubbleDataTest extends SysuiTestCase {
private NotificationEntry mEntryA1;
private NotificationEntry mEntryA2;
private NotificationEntry mEntryA3;
private NotificationEntry mEntryB1;
private NotificationEntry mEntryB2;
private NotificationEntry mEntryB3;
private NotificationEntry mEntryC1;
private NotificationEntry mEntryInterruptive;
private NotificationEntry mEntryDismissed;
private BubbleEntry mEntryA1;
private BubbleEntry mEntryA2;
private BubbleEntry mEntryA3;
private BubbleEntry mEntryB1;
private BubbleEntry mEntryB2;
private BubbleEntry mEntryB3;
private BubbleEntry mEntryC1;
private BubbleEntry mEntryInterruptive;
private BubbleEntry mEntryDismissed;
private Bubble mBubbleA1;
private Bubble mBubbleA2;
@@ -99,8 +95,6 @@ public class BubbleDataTest extends SysuiTestCase {
@Mock
private PendingIntent mDeleteIntent;
private NotificationTestHelper mNotificationTestHelper;
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -112,29 +106,23 @@ public class BubbleDataTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
TestableLooper.get(this));
MockitoAnnotations.initMocks(this);
mEntryA1 = createBubbleEntry(1, "a1", "package.a");
mEntryA2 = createBubbleEntry(1, "a2", "package.a");
mEntryA3 = createBubbleEntry(1, "a3", "package.a");
mEntryB1 = createBubbleEntry(1, "b1", "package.b");
mEntryB2 = createBubbleEntry(1, "b2", "package.b");
mEntryB3 = createBubbleEntry(1, "b3", "package.b");
mEntryC1 = createBubbleEntry(1, "c1", "package.c");
mEntryA1 = createBubbleEntry(1, "a1", "package.a", null);
mEntryA2 = createBubbleEntry(1, "a2", "package.a", null);
mEntryA3 = createBubbleEntry(1, "a3", "package.a", null);
mEntryB1 = createBubbleEntry(1, "b1", "package.b", null);
mEntryB2 = createBubbleEntry(1, "b2", "package.b", null);
mEntryB3 = createBubbleEntry(1, "b3", "package.b", null);
mEntryC1 = createBubbleEntry(1, "c1", "package.c", null);
mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d");
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
NotificationListenerService.Ranking ranking =
mock(NotificationListenerService.Ranking.class);
when(ranking.visuallyInterruptive()).thenReturn(true);
mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
@@ -862,12 +850,13 @@ public class BubbleDataTest extends SysuiTestCase {
}
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
return createBubbleEntry(userId, notifKey, packageName, 1000);
private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
NotificationListenerService.Ranking ranking) {
return createBubbleEntry(userId, notifKey, packageName, ranking, 1000);
}
private void setPostTime(NotificationEntry entry, long postTime) {
when(entry.getSbn().getPostTime()).thenReturn(postTime);
private void setPostTime(BubbleEntry entry, long postTime) {
when(entry.getStatusBarNotification().getPostTime()).thenReturn(postTime);
}
/**
@@ -875,16 +864,19 @@ public class BubbleDataTest extends SysuiTestCase {
* required for BubbleData functionality and verification. NotificationTestHelper is used only
* as a convenience to create a Notification w/BubbleMetadata.
*/
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
long postTime) {
private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
NotificationListenerService.Ranking ranking, long postTime) {
// BubbleMetadata
Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder(
mExpandIntent, Icon.createWithResource("", 0))
.setDeleteIntent(mDeleteIntent)
.build();
// Notification -> BubbleMetadata
Notification notification = mNotificationTestHelper.createNotification(false,
null /* groupKey */, bubbleMetadata);
Notification notification = mock(Notification.class);
notification.setBubbleMetadata(bubbleMetadata);
// Notification -> extras
notification.extras = new Bundle();
// StatusBarNotification
StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -895,18 +887,18 @@ public class BubbleDataTest extends SysuiTestCase {
when(sbn.getNotification()).thenReturn(notification);
// NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
return new NotificationEntryBuilder().setSbn(sbn).build();
return new BubbleEntry(sbn, ranking, true, false, false, false);
}
private void setCurrentTime(long time) {
when(mTimeSource.currentTimeMillis()).thenReturn(time);
}
private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */);
}
private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime,
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime,
boolean visuallyInterruptive) {
setPostTime(entry, postTime);
// BubbleController calls this:

View File

@@ -24,12 +24,14 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -37,8 +39,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -52,8 +52,10 @@ import org.mockito.MockitoAnnotations;
public class BubbleTest extends SysuiTestCase {
@Mock
private Notification mNotif;
@Mock
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private BubbleEntry mBubbleEntry;
private Bundle mExtras;
private Bubble mBubble;
@@ -67,18 +69,16 @@ public class BubbleTest extends SysuiTestCase {
mExtras = new Bundle();
mNotif.extras = mExtras;
mEntry = new NotificationEntryBuilder()
.setNotification(mNotif)
.build();
mBubble = new Bubble(mEntry, mSuppressionListener, null);
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, target, 0),
Icon.createWithResource(mContext, R.drawable.android))
.build();
mEntry.setBubbleMetadata(metadata);
when(mSbn.getNotification()).thenReturn(mNotif);
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
when(mSbn.getKey()).thenReturn("mock");
mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null);
}
@Test
@@ -86,7 +86,7 @@ public class BubbleTest extends SysuiTestCase {
final String msg = "Hello there!";
doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
}
@Test
@@ -97,7 +97,7 @@ public class BubbleTest extends SysuiTestCase {
mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
// Should be big text, not the small text.
assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
}
@Test
@@ -105,7 +105,7 @@ public class BubbleTest extends SysuiTestCase {
doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
// Media notifs don't get update messages.
assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
assertNull(Bubble.extractFlyoutMessage(mBubbleEntry).message);
}
@Test
@@ -121,7 +121,7 @@ public class BubbleTest extends SysuiTestCase {
// Should be the last one only.
assertEquals("Really? I prefer them that way.",
BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
Bubble.extractFlyoutMessage(mBubbleEntry).message);
}
@Test
@@ -136,8 +136,8 @@ public class BubbleTest extends SysuiTestCase {
"Oh, hello!", 0, "Mady").toBundle()});
// Should be the last one only.
assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
assertEquals("Oh, hello!", Bubble.extractFlyoutMessage(mBubbleEntry).message);
assertEquals("Mady", Bubble.extractFlyoutMessage(mBubbleEntry).senderName);
}
@Test