Migrate Bubbles to wm-shell (4/n)

Move BubbleController sysui dependent code to new class
BubblesManager and remove using of Dependency of Bubbles.

TODO: Tests still need to be remain on sysui, find suitable
      method to test both classes independently.

Bug: 161980186
Test: atest SystemUITests
Change-Id: I7be1dfb81f7ddfb5fc20b180fc5affb7a1d8e93c
This commit is contained in:
Tony Huang
2020-10-16 17:53:08 +08:00
parent 51f89ab4fa
commit e23aad5157
44 changed files with 1732 additions and 1438 deletions

View File

@@ -37,7 +37,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -308,7 +307,6 @@ public class Dependency {
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@Inject Lazy<Bubbles> mBubbles;
@Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@@ -506,7 +504,6 @@ public class Dependency {
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
mRemoteInputQuickSettingsDisabler::get);
mProviders.put(Bubbles.class, mBubbles::get);
mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);

View File

@@ -53,7 +53,8 @@ import java.util.Objects;
/**
* Encapsulates the data and UI elements of a bubble.
*/
class Bubble implements BubbleViewProvider {
@VisibleForTesting
public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
private final String mKey;
@@ -62,7 +63,7 @@ class Bubble implements BubbleViewProvider {
private long mLastAccessed;
@Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
/** Whether the bubble should show a dot for the notification indicating updated content. */
private boolean mShowBubbleUpdateDot = true;
@@ -173,8 +174,8 @@ class Bubble implements BubbleViewProvider {
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final BubbleEntry entry,
@Nullable final BubbleController.NotificationSuppressionChangedListener listener,
final BubbleController.PendingIntentCanceledListener intentCancelListener) {
@Nullable final Bubbles.NotificationSuppressionChangedListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener) {
mKey = entry.getKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
@@ -309,11 +310,13 @@ class Bubble implements BubbleViewProvider {
*
* @param callback the callback to notify one the bubble is ready to be displayed.
* @param context the context for the bubble.
* @param controller
* @param stackView the stackView the bubble is eventually added to.
* @param iconFactory the iconfactory use to create badged images for the bubble.
*/
void inflate(BubbleViewInfoTask.Callback callback,
Context context,
BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory iconFactory,
boolean skipInflation) {
@@ -322,6 +325,7 @@ class Bubble implements BubbleViewProvider {
}
mInflationTask = new BubbleViewInfoTask(this,
context,
controller,
stackView,
iconFactory,
skipInflation,
@@ -522,7 +526,8 @@ class Bubble implements BubbleViewProvider {
/**
* Sets whether this notification should be suppressed in the shade.
*/
void setSuppressNotification(boolean suppressNotification) {
@VisibleForTesting
public void setSuppressNotification(boolean suppressNotification) {
boolean prevShowInShade = showInShade();
if (suppressNotification) {
mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
@@ -559,7 +564,8 @@ class Bubble implements BubbleViewProvider {
/**
* Whether the flyout for the bubble should be shown.
*/
boolean showFlyout() {
@VisibleForTesting
public boolean showFlyout() {
return !mSuppressFlyout && !mShouldSuppressPeek
&& !shouldSuppressNotification()
&& !mShouldSuppressNotificationList;

View File

@@ -34,7 +34,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.bubbles.Bubbles.DismissReason;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -137,8 +137,8 @@ public class BubbleData {
private Listener mListener;
@Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
private BubbleController.PendingIntentCanceledListener mCancelledListener;
private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
private Bubbles.PendingIntentCanceledListener mCancelledListener;
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
@@ -165,12 +165,12 @@ public class BubbleData {
}
public void setSuppressionChangedListener(
BubbleController.NotificationSuppressionChangedListener listener) {
Bubbles.NotificationSuppressionChangedListener listener) {
mSuppressionListener = listener;
}
public void setPendingIntentCancelledListener(
BubbleController.PendingIntentCanceledListener listener) {
Bubbles.PendingIntentCanceledListener listener) {
mCancelledListener = listener;
}
@@ -344,7 +344,8 @@ public class BubbleData {
/**
* Whether the summary for the provided group key is suppressed.
*/
boolean isSummarySuppressed(String groupKey) {
@VisibleForTesting
public boolean isSummarySuppressed(String groupKey) {
return mSuppressedGroupKeys.containsKey(groupKey);
}
@@ -415,7 +416,7 @@ public class BubbleData {
// skip the selected bubble
.filter((b) -> !b.equals(mSelectedBubble))
.findFirst()
.ifPresent((b) -> doRemove(b.getKey(), BubbleController.DISMISS_AGED));
.ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED));
}
}
@@ -459,12 +460,12 @@ public class BubbleData {
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
if (hasOverflowBubbleWithKey(key)
&& (reason == BubbleController.DISMISS_NOTIF_CANCEL
|| reason == BubbleController.DISMISS_GROUP_CANCELLED
|| reason == BubbleController.DISMISS_NO_LONGER_BUBBLE
|| reason == BubbleController.DISMISS_BLOCKED
|| reason == BubbleController.DISMISS_SHORTCUT_REMOVED
|| reason == BubbleController.DISMISS_PACKAGE_REMOVED)) {
&& (reason == Bubbles.DISMISS_NOTIF_CANCEL
|| reason == Bubbles.DISMISS_GROUP_CANCELLED
|| reason == Bubbles.DISMISS_NO_LONGER_BUBBLE
|| reason == Bubbles.DISMISS_BLOCKED
|| reason == Bubbles.DISMISS_SHORTCUT_REMOVED
|| reason == Bubbles.DISMISS_PACKAGE_REMOVED)) {
Bubble b = getOverflowBubbleWithKey(key);
if (DEBUG_BUBBLE_DATA) {
@@ -512,8 +513,8 @@ public class BubbleData {
void overflowBubble(@DismissReason int reason, Bubble bubble) {
if (bubble.getPendingIntentCanceled()
|| !(reason == BubbleController.DISMISS_AGED
|| reason == BubbleController.DISMISS_USER_GESTURE)) {
|| !(reason == Bubbles.DISMISS_AGED
|| reason == Bubbles.DISMISS_USER_GESTURE)) {
return;
}
if (DEBUG_BUBBLE_DATA) {
@@ -529,7 +530,7 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "Overflow full. Remove: " + oldest);
}
mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED);
mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED);
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
mOverflowBubbles.remove(oldest);
mStateChange.removedOverflowBubble = oldest;
@@ -694,7 +695,7 @@ public class BubbleData {
}
private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) {
if (reason != BubbleController.DISMISS_USER_GESTURE) return;
if (reason != Bubbles.DISMISS_USER_GESTURE) return;
PendingIntent deleteIntent = bubble.getDeleteIntent();
if (deleteIntent == null) return;
try {
@@ -726,7 +727,7 @@ public class BubbleData {
* The set of bubbles in overflow.
*/
@VisibleForTesting(visibility = PRIVATE)
List<Bubble> getOverflowBubbles() {
public List<Bubble> getOverflowBubbles() {
return Collections.unmodifiableList(mOverflowBubbles);
}
@@ -742,7 +743,7 @@ public class BubbleData {
@VisibleForTesting(visibility = PRIVATE)
@Nullable
Bubble getBubbleInStackWithKey(String key) {
public Bubble getBubbleInStackWithKey(String key) {
for (int i = 0; i < mBubbles.size(); i++) {
Bubble bubble = mBubbles.get(i);
if (bubble.getKey().equals(key)) {
@@ -764,7 +765,7 @@ public class BubbleData {
}
@VisibleForTesting(visibility = PRIVATE)
Bubble getOverflowBubbleWithKey(String key) {
public Bubble getOverflowBubbleWithKey(String key) {
for (int i = 0; i < mOverflowBubbles.size(); i++) {
Bubble bubble = mOverflowBubbles.get(i);
if (bubble.getKey().equals(key)) {
@@ -788,7 +789,7 @@ public class BubbleData {
* This method should only be used in tests, not in production.
*/
@VisibleForTesting
void setMaxOverflowBubbles(int maxOverflowBubbles) {
public void setMaxOverflowBubbles(int maxOverflowBubbles) {
mMaxOverflowBubbles = maxOverflowBubbles;
}

View File

@@ -33,10 +33,10 @@ public class BubbleDebugConfig {
// to figure-out the origin of a log message while debugging the Bubbles a little painful. By
// setting this constant to true, log messages from the Bubbles package will be tagged with
// their class names instead fot the generic tag.
static final boolean TAG_WITH_CLASS_NAME = false;
public static final boolean TAG_WITH_CLASS_NAME = false;
// Default log tag for the Bubbles package.
static final String TAG_BUBBLES = "Bubbles";
public static final String TAG_BUBBLES = "Bubbles";
static final boolean DEBUG_BUBBLE_CONTROLLER = false;
static final boolean DEBUG_BUBBLE_DATA = false;

View File

@@ -16,6 +16,9 @@
package com.android.systemui.bubbles;
import static android.app.Notification.FLAG_BUBBLE;
import android.app.Notification;
import android.app.Notification.BubbleMetadata;
import android.app.NotificationManager.Policy;
import android.service.notification.NotificationListenerService.Ranking;
@@ -67,12 +70,45 @@ public class BubbleEntry {
return mSbn.getKey();
}
/** @return the group key in the {@link StatusBarNotification}. */
public String getGroupKey() {
return mSbn.getGroupKey();
}
/** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
@Nullable
public BubbleMetadata getBubbleMetadata() {
return getStatusBarNotification().getNotification().getBubbleMetadata();
}
/**
* Updates the {@link Notification#FLAG_BUBBLE} flag on this notification to indicate
* whether it is a bubble or not. If this entry is set to not bubble, or does not have
* the required info to bubble, the flag cannot be set to true.
*
* @param shouldBubble whether this notification should be flagged as a bubble.
* @return true if the value changed.
*/
public boolean setFlagBubble(boolean shouldBubble) {
boolean wasBubble = isBubble();
if (!shouldBubble) {
mSbn.getNotification().flags &= ~FLAG_BUBBLE;
} else if (getBubbleMetadata() != null && canBubble()) {
// wants to be bubble & can bubble, set flag
mSbn.getNotification().flags |= FLAG_BUBBLE;
}
return wasBubble != isBubble();
}
public boolean isBubble() {
return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0;
}
/** @see Ranking#canBubble() */
public boolean canBubble() {
return mRanking.canBubble();
}
/** @return true if this notification is clearable. */
public boolean isClearable() {
return mIsClearable;

View File

@@ -54,7 +54,6 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.AlphaOptimizedButton;
@@ -99,7 +98,7 @@ public class BubbleExpandedView extends LinearLayout {
// TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead
private boolean mIsOverflow;
private Bubbles mBubbles = Dependency.get(Bubbles.class);
private BubbleController mController;
private BubbleStackView mStackView;
private BubblePositioner mPositioner;
@@ -156,8 +155,7 @@ public class BubbleExpandedView extends LinearLayout {
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
mBubbles.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
}
});
mInitialized = true;
@@ -195,15 +193,15 @@ public class BubbleExpandedView extends LinearLayout {
}
if (mBubble != null) {
// Must post because this is called from a binder thread.
post(() -> mBubbles.removeBubble(mBubble.getKey(),
BubbleController.DISMISS_TASK_FINISHED));
post(() -> mController.removeBubble(
mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED));
}
}
@Override
public void onBackPressedOnTaskRoot(int taskId) {
if (mTaskId == taskId && mStackView.isExpanded()) {
mBubbles.collapseStack();
mController.collapseStack();
}
}
};
@@ -250,9 +248,6 @@ public class BubbleExpandedView extends LinearLayout {
R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
mPositioner = mBubbles.getPositioner();
mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer());
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
@@ -263,7 +258,6 @@ public class BubbleExpandedView extends LinearLayout {
}
});
mExpandedViewContainer.setClipToOutline(true);
mExpandedViewContainer.addView(mTaskView);
mExpandedViewContainer.setLayoutParams(
new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
addView(mExpandedViewContainer);
@@ -274,9 +268,7 @@ public class BubbleExpandedView extends LinearLayout {
// ==> expanded view
// ==> activity view
// ==> manage button
bringChildToFront(mTaskView);
bringChildToFront(mSettingsIcon);
mTaskView.setListener(mTaskViewListener);
applyThemeAttrs();
@@ -314,6 +306,20 @@ public class BubbleExpandedView extends LinearLayout {
super.onAttachedToWindow();
mTaskView.setExecutor(new HandlerExecutor(getHandler()));
}
/**
* Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
* to be called after view inflate.
*/
void initialize(BubbleController controller, BubbleStackView stackView) {
mController = controller;
mStackView = stackView;
mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
mTaskView.setListener(mTaskViewListener);
mPositioner = mController.getPositioner();
}
void updateDimensions() {
Resources res = getResources();
@@ -464,16 +470,12 @@ public class BubbleExpandedView extends LinearLayout {
return mTaskId;
}
void setStackView(BubbleStackView stackView) {
mStackView = stackView;
}
public void setOverflow(boolean overflow) {
mIsOverflow = overflow;
Intent target = new Intent(mContext, BubbleOverflowActivity.class);
Bundle extras = new Bundle();
extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles));
extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController));
target.putExtras(extras);
mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
target, PendingIntent.FLAG_UPDATE_CURRENT);

View File

@@ -91,14 +91,14 @@ public class BubbleLogger {
* @param b Bubble removed from overflow
* @param r Reason that bubble was removed
*/
public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
if (r == Bubbles.DISMISS_NOTIF_CANCEL) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
} else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
} else if (r == Bubbles.DISMISS_GROUP_CANCELLED) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
} else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
} else if (r == Bubbles.DISMISS_NO_LONGER_BUBBLE) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
} else if (r == BubbleController.DISMISS_BLOCKED) {
} else if (r == Bubbles.DISMISS_BLOCKED) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
}
}
@@ -107,10 +107,10 @@ public class BubbleLogger {
* @param b Bubble added to overflow
* @param r Reason that bubble was added to overflow
*/
public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
if (r == BubbleController.DISMISS_AGED) {
public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) {
if (r == Bubbles.DISMISS_AGED) {
log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
} else if (r == BubbleController.DISMISS_USER_GESTURE) {
} else if (r == Bubbles.DISMISS_USER_GESTURE) {
log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
}
}

View File

@@ -35,6 +35,7 @@ import com.android.systemui.R
class BubbleOverflow(
private val context: Context,
private val controller: BubbleController,
private val stack: BubbleStackView
) : BubbleViewProvider {
@@ -56,8 +57,8 @@ class BubbleOverflow(
init {
updateResources()
with(expandedView) {
initialize(controller, stack)
setOverflow(true)
setStackView(stack)
applyThemeAttrs()
}
with(overflowBtn) {

View File

@@ -63,7 +63,7 @@ public class BubbleOverflowActivity extends Activity {
private TextView mEmptyStateTitle;
private TextView mEmptyStateSubtitle;
private ImageView mEmptyStateImage;
private Bubbles mBubbles;
private BubbleController mController;
private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
private List<Bubble> mOverflowBubbles = new ArrayList<>();
@@ -111,7 +111,7 @@ public class BubbleOverflowActivity extends Activity {
if (intent != null && intent.getExtras() != null) {
IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
if (binder instanceof ObjectWrapper) {
mBubbles = ((ObjectWrapper<Bubbles>) binder).get();
mController = ((ObjectWrapper<BubbleController>) binder).get();
}
} else {
Log.w(TAG, "Bubble overflow activity created without bubble controller!");
@@ -139,15 +139,15 @@ public class BubbleOverflowActivity extends Activity {
final int viewHeight = recyclerViewHeight / rows;
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
mBubbles::promoteBubbleFromOverflow, viewWidth, viewHeight);
mController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
mOverflowBubbles.clear();
mOverflowBubbles.addAll(mBubbles.getOverflowBubbles());
mOverflowBubbles.addAll(mController.getOverflowBubbles());
mAdapter.notifyDataSetChanged();
updateEmptyStateVisibility();
mBubbles.setOverflowListener(mDataListener);
mController.setOverflowListener(mDataListener);
updateTheme();
}
@@ -217,7 +217,7 @@ public class BubbleOverflowActivity extends Activity {
if (DEBUG_OVERFLOW) {
Log.d(TAG, BubbleDebugConfig.formatBubblesString(
mBubbles.getOverflowBubbles(), null));
mController.getOverflowBubbles(), null));
}
}
};

View File

@@ -155,7 +155,7 @@ public class BubbleStackView extends FrameLayout
*
* {@hide}
*/
interface SurfaceSynchronizer {
public interface SurfaceSynchronizer {
/**
* Wait until requested change on a {@link View} is reflected on the screen.
*
@@ -186,7 +186,7 @@ public class BubbleStackView extends FrameLayout
});
}
};
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
private final ValueAnimator mDesaturateAndDarkenAnimator;
@@ -383,22 +383,6 @@ public class BubbleStackView extends FrameLayout
@NonNull
private final SurfaceSynchronizer mSurfaceSynchronizer;
/**
* Callback to run when the IME visibility changes - BubbleController uses this to update the
* Bubbles window focusability flags with the WindowManager.
*/
public final Consumer<Boolean> mOnImeVisibilityChanged;
/**
* Callback to run when the bubble expand status changes.
*/
private final Consumer<Boolean> mOnBubbleExpandChanged;
/**
* Callback to run to ask BubbleController to hide the current IME.
*/
private final Runnable mHideCurrentInputMethodCallback;
/**
* The currently magnetized object, which is being dragged and will be attracted to the magnetic
* dismiss target.
@@ -733,16 +717,12 @@ public class BubbleStackView extends FrameLayout
private BubblePositioner mPositioner;
@SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
Runnable allBubblesAnimatedOutAction,
Consumer<Boolean> onImeVisibilityChanged,
Runnable hideCurrentInputMethodCallback,
Consumer<Boolean> onBubbleExpandChanged,
BubblePositioner positioner) {
public BubbleStackView(Context context, BubbleController bubbleController,
BubbleData data, @Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator) {
super(context);
mBubbleController = bubbleController;
mBubbleData = data;
Resources res = getResources();
@@ -755,7 +735,7 @@ public class BubbleStackView extends FrameLayout
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
mPositioner = positioner;
mPositioner = mBubbleController.getPositioner();
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -767,7 +747,7 @@ public class BubbleStackView extends FrameLayout
final Runnable onBubbleAnimatedOut = () -> {
if (getBubbleCount() == 0) {
allBubblesAnimatedOutAction.run();
mBubbleController.onAllBubblesAnimatedOut();
}
};
@@ -839,7 +819,7 @@ public class BubbleStackView extends FrameLayout
setFocusable(true);
mBubbleContainer.bringToFront();
mBubbleOverflow = new BubbleOverflow(getContext(), this);
mBubbleOverflow = new BubbleOverflow(getContext(), bubbleController, this);
mBubbleContainer.addView(mBubbleOverflow.getIconView(),
mBubbleContainer.getChildCount() /* index */,
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -850,12 +830,9 @@ public class BubbleStackView extends FrameLayout
showManageMenu(false);
});
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
mOnBubbleExpandChanged = onBubbleExpandChanged;
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
mBubbleController.onImeVisibilityChanged(
insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
if (!mIsExpanded || mIsExpansionAnimating) {
return view.onApplyWindowInsets(insets);
}
@@ -1299,7 +1276,7 @@ public class BubbleStackView extends FrameLayout
// R constants are not final so we cannot use switch-case here.
if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
mBubbleData.dismissAll(Bubbles.DISMISS_ACCESSIBILITY_ACTION);
announceForAccessibility(
getResources().getString(R.string.accessibility_bubble_dismissed));
return true;
@@ -1402,8 +1379,9 @@ public class BubbleStackView extends FrameLayout
/**
* The {@link Bubble} that is expanded, null if one does not exist.
*/
@VisibleForTesting
@Nullable
BubbleViewProvider getExpandedBubble() {
public BubbleViewProvider getExpandedBubble() {
return mExpandedBubble;
}
@@ -1617,7 +1595,7 @@ public class BubbleStackView extends FrameLayout
hideCurrentInputMethod();
mOnBubbleExpandChanged.accept(shouldExpand);
mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand);
if (mIsExpanded) {
animateCollapse();
@@ -1637,7 +1615,7 @@ public class BubbleStackView extends FrameLayout
* not.
*/
void hideCurrentInputMethod() {
mHideCurrentInputMethodCallback.run();
mBubbleController.hideCurrentInputMethod();
}
private void beforeExpandedViewAnimation() {
@@ -2135,14 +2113,13 @@ public class BubbleStackView extends FrameLayout
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView));
} else {
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
}
}
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mBubbleData.dismissBubbleWithKey(
bubble.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE);
}
}

View File

@@ -66,6 +66,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
private Bubble mBubble;
private WeakReference<Context> mContext;
private WeakReference<BubbleController> mController;
private WeakReference<BubbleStackView> mStackView;
private BubbleIconFactory mIconFactory;
private boolean mSkipInflation;
@@ -77,12 +78,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
*/
BubbleViewInfoTask(Bubble b,
Context context,
BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory factory,
boolean skipInflation,
Callback c) {
mBubble = b;
mContext = new WeakReference<>(context);
mController = new WeakReference<>(controller);
mStackView = new WeakReference<>(stackView);
mIconFactory = factory;
mSkipInflation = skipInflation;
@@ -91,8 +94,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble,
mSkipInflation);
return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
mIconFactory, mBubble, mSkipInflation);
}
@Override
@@ -121,8 +124,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Bubble.FlyoutMessage flyoutMessage;
@Nullable
static BubbleViewInfo populate(Context c, BubbleStackView stackView,
BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
static BubbleViewInfo populate(Context c, BubbleController controller,
BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
boolean skipInflation) {
BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble
@@ -133,7 +137,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
info.expandedView.setStackView(stackView);
info.expandedView.initialize(controller, stackView);
}
if (b.getShortcutInfo() != null) {

View File

@@ -26,7 +26,7 @@ import androidx.annotation.Nullable;
/**
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
*/
interface BubbleViewProvider {
public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
void setContentVisibility(boolean visible);

View File

@@ -16,62 +16,93 @@
package com.android.systemui.bubbles;
import android.annotation.NonNull;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.MainThread;
import android.content.res.Configuration;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.view.View;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.wm.shell.ShellTaskOrganizer;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import java.util.function.IntConsumer;
/**
* Interface to engage bubbles feature.
*/
public interface Bubbles {
@Retention(SOURCE)
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
DISMISS_NO_BUBBLE_UP})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
int DISMISS_USER_GESTURE = 1;
int DISMISS_AGED = 2;
int DISMISS_TASK_FINISHED = 3;
int DISMISS_BLOCKED = 4;
int DISMISS_NOTIF_CANCEL = 5;
int DISMISS_ACCESSIBILITY_ACTION = 6;
int DISMISS_NO_LONGER_BUBBLE = 7;
int DISMISS_USER_CHANGED = 8;
int DISMISS_GROUP_CANCELLED = 9;
int DISMISS_INVALID_INTENT = 10;
int DISMISS_OVERFLOW_MAX_REACHED = 11;
int DISMISS_SHORTCUT_REMOVED = 12;
int DISMISS_PACKAGE_REMOVED = 13;
int DISMISS_NO_BUBBLE_UP = 14;
/**
* @return {@code true} if there is a bubble associated with the provided key and if its
* notification is hidden from the shade or there is a group summary associated with the
* provided key that is hidden from the shade because it has been dismissed but still has child
* bubbles active.
*/
boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry);
boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey);
/**
* @return {@code true} if the current notification entry same as selected bubble
* notification entry and the stack is currently expanded.
*/
boolean isBubbleExpanded(NotificationEntry entry);
boolean isBubbleExpanded(String key);
/** @return {@code true} if stack of bubbles is expanded or not. */
boolean isStackExpanded();
/**
* @return the {@link ScrimView} drawn behind the bubble stack. This is managed by
* {@link ScrimController} since we want the scrim's appearance and behavior to be identical to
* that of the notification shade scrim.
*/
ScrimView getScrimForBubble();
/** @return {@code true} if the summary for the provided group key is suppressed. */
boolean isSummarySuppressed(String groupKey);
/** @return Bubbles for updating overflow. */
List<Bubble> getOverflowBubbles();
/**
* Removes a group key indicating that summary for this group should no longer be suppressed.
*/
void removeSuppressedSummary(String groupKey);
/** Tell the stack of bubbles to collapse. */
void collapseStack();
/** Tell the controller need update its UI to fit theme. */
void updateForThemeChanges();
/**
* Request the stack expand if needed, then select the specified Bubble as current.
* If no bubble exists for this entry, one is created.
*
* @param entry the notification for the bubble to be selected
*/
void expandStackAndSelectBubble(NotificationEntry entry);
/** Promote the provided bubbles when overflow view. */
void promoteBubbleFromOverflow(Bubble bubble);
void expandStackAndSelectBubble(BubbleEntry entry);
/**
* We intercept notification entries (including group summaries) dismissed by the user when
@@ -81,26 +112,65 @@ public interface Bubbles {
* {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
* {@link BubbleData#addSummaryToSuppress}.
*
* @param entry the notification of the BubbleEntry should be removed.
* @param children the list of child notification of the BubbleEntry from 1st param entry,
* this will be null if entry does have no children.
* @param removeCallback the remove callback for SystemUI side to remove notification, the int
* number should be list position of children list and use -1 for
* removing the parent notification.
*
* @return true if we want to intercept the dismissal of the entry, else false.
*/
boolean handleDismissalInterception(NotificationEntry entry);
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
IntConsumer removeCallback);
/**
* Removes the bubble with the given key.
* <p>
* Must be called from the main thread.
*/
@MainThread
void removeBubble(String key, int reason);
/**
* When a notification is marked Priority, expand the stack if needed,
* then (maybe create and) select the given bubble.
* Retrieves the notif entry key of the summary associated with the provided group key.
*
* @param entry the notification for the bubble to show
* @param groupKey the group to look up
* @return the key for the notification that is the summary of this group.
*/
void onUserChangedImportance(NotificationEntry entry);
String getSummaryKey(String groupKey);
/** Set the proxy to commnuicate with SysUi side components. */
void setSysuiProxy(SysuiProxy proxy);
/** Set the scrim view for bubbles. */
void setBubbleScrim(View view);
/** Set a listener to be notified of bubble expand events. */
void setExpandListener(BubbleExpandListener listener);
/**
* Called when new notification entry added.
*
* @param entry the {@link BubbleEntry} by the notification.
*/
void onEntryAdded(BubbleEntry entry);
/**
* Called when new notification entry updated.
*
* @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
*/
void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
/**
* Called when new notification entry removed.
*
* @param entry the {@link BubbleEntry} by the notification.
*/
void onEntryRemoved(BubbleEntry entry);
/**
* Called when NotificationListener has received adjusted notification rank and reapplied
* filtering and sorting. This is used to dismiss or create bubbles based on changes in
* permissions on the notification channel or the global setting.
*
* @param rankingMap the updated ranking map from NotificationListenerService
*/
void onRankingUpdated(RankingMap rankingMap);
/**
* Called when the status bar has become visible or invisible (either permanently or
@@ -108,30 +178,85 @@ public interface Bubbles {
*/
void onStatusBarVisibilityChanged(boolean visible);
/** Called when system zen mode state changed. */
void onZenStateChanged();
/**
* Called when a user has indicated that an active notification should be shown as a bubble.
* <p>
* This method will collapse the shade, create the bubble without a flyout or dot, and suppress
* the notification from appearing in the shade.
* Called when statusBar state changed.
*
* @param entry the notification to change bubble state for.
* @param shouldBubble whether the notification should show as a bubble or not.
* @param isShade {@code true} is state is SHADE.
*/
void onUserChangedBubble(@NonNull NotificationEntry entry, boolean shouldBubble);
void onStatusBarStateChanged(boolean isShade);
/**
* Called when the current user changed.
*
* @param newUserId the new user's id.
*/
void onUserChanged(int newUserId);
/** See {@link BubbleController.NotifCallback}. */
void addNotifCallback(BubbleController.NotifCallback callback);
/**
* Called when config changed.
*
* @param newConfig the new config.
*/
void onConfigChanged(Configuration newConfig);
/** Set a listener to be notified of bubble expand events. */
void setExpandListener(BubbleController.BubbleExpandListener listener);
/** Description of current bubble state. */
void dump(FileDescriptor fd, PrintWriter pw, String[] args);
/** Set a listener to be notified of when overflow view update. */
void setOverflowListener(BubbleData.Listener listener);
/** Listener to find out about stack expansion / collapse events. */
interface BubbleExpandListener {
/**
* Called when the expansion state of the bubble stack changes.
*
* @param isExpanding whether it's expanding or collapsing
* @param key the notification key associated with bubble being expanded
*/
void onBubbleExpandChanged(boolean isExpanding, String key);
}
/** The task listener for events in bubble tasks. **/
ShellTaskOrganizer getTaskOrganizer();
/** Listener to be notified when a bubbles' notification suppression state changes.*/
interface NotificationSuppressionChangedListener {
/** Called when the notification suppression state of a bubble changes. */
void onBubbleNotificationSuppressionChange(Bubble bubble);
}
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner();
/** Listener to be notified when a pending intent has been canceled for a bubble. */
interface PendingIntentCanceledListener {
/** Called when the pending intent for a bubble has been canceled. */
void onPendingIntentCanceled(Bubble bubble);
}
/** Callback to tell SysUi components execute some methods. */
interface SysuiProxy {
@Nullable
BubbleEntry getPendingOrActiveEntry(String key);
List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys);
boolean isNotificationShadeExpand();
boolean shouldBubbleUp(String key);
void setNotificationInterruption(String key);
void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag);
void notifyRemoveNotification(String key, int reason);
void notifyInvalidateNotifications(String reason);
void notifyMaybeCancelSummary(String key);
void removeNotificationEntry(String key);
void updateNotificationBubbleButton(String key);
void updateNotificationSuppression(String key);
void onStackExpandChanged(boolean shouldExpand);
void onUnbubbleConversation(String key);
}
}

View File

@@ -22,6 +22,8 @@ import android.content.pm.LauncherApps;
import android.os.Handler;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
@@ -41,10 +43,13 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
import java.util.Optional;
import dagger.Module;
import dagger.Provides;
@@ -56,23 +61,8 @@ public interface BubbleModule {
*/
@SysUISingleton
@Provides
static Bubbles newBubbleController(
Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
ConfigurationController configurationController,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
static Bubbles newBubbleController(Context context,
FloatingContentCoordinator floatingContentCoordinator,
SysUiState sysUiState,
INotificationManager notifManager,
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -80,30 +70,29 @@ public interface BubbleModule {
UiEventLogger uiEventLogger,
@Main Handler mainHandler,
ShellTaskOrganizer organizer) {
return BubbleController.create(
context,
notificationShadeWindowController,
statusBarStateController,
shadeController,
null /* synchronizer */,
configurationController,
interruptionStateProvider,
zenModeController,
notifUserManager,
groupManager,
entryManager,
notifPipeline,
featureFlags,
dumpManager,
floatingContentCoordinator,
sysUiState,
notifManager,
statusBarService,
windowManager,
windowManagerShellWrapper,
launcherApps,
uiEventLogger,
mainHandler,
organizer);
return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
uiEventLogger, mainHandler, organizer);
}
/** Provides Optional of BubbleManager */
@SysUISingleton
@Provides
static Optional<BubblesManager> provideBubblesManager(Context context,
Optional<Bubbles> bubblesOptional,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController, ShadeController shadeController,
ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService, INotificationManager notificationManager,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
DumpManager dumpManager) {
return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
}
}

View File

@@ -159,7 +159,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent()
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(ent);
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
ent.getKey(), ent.getSbn().getGroupKey());
if (ent.isRowDismissed() || ent.isRowRemoved()
|| isBubbleNotificationSuppressedFromShade
|| mFgsSectionController.hasEntry(ent)) {

View File

@@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.wmshell.BubblesManager;
import java.util.HashSet;
import java.util.Optional;
@@ -56,6 +57,7 @@ import javax.inject.Inject;
public class BubbleCoordinator implements Coordinator {
private static final String TAG = "BubbleCoordinator";
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final NotifCollection mNotifCollection;
private final Set<String> mInterceptedDismissalEntries = new HashSet<>();
@@ -64,8 +66,10 @@ public class BubbleCoordinator implements Coordinator {
@Inject
public BubbleCoordinator(
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
NotifCollection notifCollection) {
mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mNotifCollection = notifCollection;
}
@@ -75,8 +79,8 @@ public class BubbleCoordinator implements Coordinator {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
mNotifPipeline.addFinalizeFilter(mNotifFilter);
if (mBubblesOptional.isPresent()) {
mBubblesOptional.get().addNotifCallback(mNotifCallback);
if (mBubblesManagerOptional.isPresent()) {
mBubblesManagerOptional.get().addNotifCallback(mNotifCallback);
}
}
@@ -85,7 +89,8 @@ public class BubbleCoordinator implements Coordinator {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
return mBubblesOptional.isPresent()
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(entry);
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
entry.getKey(), entry.getSbn().getGroupKey());
}
};
@@ -102,9 +107,8 @@ public class BubbleCoordinator implements Coordinator {
@Override
public boolean shouldInterceptDismissal(NotificationEntry entry) {
// for experimental bubbles
if (mBubblesOptional.isPresent()
&& mBubblesOptional.get().handleDismissalInterception(entry)) {
if (mBubblesManagerOptional.isPresent()
&& mBubblesManagerOptional.get().handleDismissalInterception(entry)) {
mInterceptedDismissalEntries.add(entry.getKey());
return true;
} else {
@@ -119,8 +123,7 @@ public class BubbleCoordinator implements Coordinator {
}
};
private final BubbleController.NotifCallback mNotifCallback =
new BubbleController.NotifCallback() {
private final BubblesManager.NotifCallback mNotifCallback = new BubblesManager.NotifCallback() {
@Override
public void removeNotification(
NotificationEntry entry,

View File

@@ -64,7 +64,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Lazy<Bubbles>> mBubblesOptional;
private final Optional<Bubbles> mBubblesOptional;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -74,7 +74,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
public NotificationGroupManagerLegacy(
StatusBarStateController statusBarStateController,
Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier,
Optional<Lazy<Bubbles>> bubblesOptional) {
Optional<Bubbles> bubblesOptional) {
statusBarStateController.addCallback(this);
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesOptional = bubblesOptional;
@@ -242,8 +242,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
if (mBubblesOptional.isPresent() && !mBubblesOptional.get().get()
.isBubbleNotificationSuppressedFromShade(entry)) {
if (mBubblesOptional.isPresent() && !mBubblesOptional.get()
.isBubbleNotificationSuppressedFromShade(
entry.getKey(), entry.getSbn().getGroupKey())) {
childCount++;
} else {
hasBubbles = true;

View File

@@ -26,7 +26,6 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -73,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogC
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -133,7 +133,7 @@ public interface NotificationsModule {
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
Optional<Bubbles> bubblesOptional,
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
return new NotificationGutsManager(
@@ -150,7 +150,7 @@ public interface NotificationsModule {
contextTracker,
builderProvider,
assistantFeedbackController,
bubblesOptional,
bubblesManagerOptional,
uiEventLogger,
onUserInteractionCallback);
}

View File

@@ -72,7 +72,6 @@ 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.Bubbles;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -102,12 +101,14 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -147,6 +148,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private LayoutListener mLayoutListener;
private RowContentBindStage mRowContentBindStage;
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private Optional<BubblesManager> mBubblesManagerOptional;
private int mIconTransformContentShift;
private int mMaxHeadsUpHeightBeforeN;
private int mMaxHeadsUpHeightBeforeP;
@@ -1078,13 +1080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/** The click listener for the bubble button. */
public View.OnClickListener getBubbleClickListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Dependency.get(Bubbles.class)
.onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
return v -> {
if (mBubblesManagerOptional.isPresent()) {
mBubblesManagerOptional.get()
.onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
}
mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
};
}
@@ -1553,7 +1554,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
OnUserInteractionCallback onUserInteractionCallback) {
OnUserInteractionCallback onUserInteractionCallback,
Optional<BubblesManager> bubblesManagerOptional) {
mEntry = entry;
mAppName = appName;
if (mMenuRow == null) {
@@ -1581,6 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
}
mOnUserInteractionCallback = onUserInteractionCallback;
mBubblesManagerOptional = bubblesManagerOptional;
cacheIsSystemNotification();
}

View File

@@ -43,8 +43,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.wmshell.BubblesManager;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
@@ -79,6 +81,7 @@ public class ExpandableNotificationRowController implements NodeController {
private final FalsingManager mFalsingManager;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private final Optional<BubblesManager> mBubblesManagerOptional;
@Inject
public ExpandableNotificationRowController(
@@ -102,7 +105,8 @@ public class ExpandableNotificationRowController implements NodeController {
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
PeopleNotificationIdentifier peopleNotificationIdentifier) {
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional) {
mView = view;
mListContainer = listContainer;
mActivatableNotificationViewController = activatableNotificationViewController;
@@ -125,6 +129,7 @@ public class ExpandableNotificationRowController implements NodeController {
mAllowLongPress = allowLongPress;
mFalsingManager = falsingManager;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
}
/**
@@ -148,8 +153,8 @@ public class ExpandableNotificationRowController implements NodeController {
mFalsingManager,
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback
mOnUserInteractionCallback,
mBubblesManagerOptional
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {

View File

@@ -67,12 +67,12 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.wmshell.BubblesManager;
import java.lang.annotation.Retention;
import java.util.Optional;
@@ -94,7 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements
private OnUserInteractionCallback mOnUserInteractionCallback;
private Handler mMainHandler;
private Handler mBgHandler;
private Optional<Bubbles> mBubblesOptional;
private Optional<BubblesManager> mBubblesManagerOptional;
private String mPackageName;
private String mAppName;
private int mAppUid;
@@ -223,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements
@Main Handler mainHandler,
@Background Handler bgHandler,
OnConversationSettingsClickListener onConversationSettingsClickListener,
Optional<Bubbles> bubblesOptional) {
Optional<BubblesManager> bubblesManagerOptional) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -242,7 +242,7 @@ public class NotificationConversationInfo extends LinearLayout implements
mIconFactory = conversationIconFactory;
mUserContext = userContext;
mBubbleMetadata = bubbleMetadata;
mBubblesOptional = bubblesOptional;
mBubblesManagerOptional = bubblesManagerOptional;
mBuilderProvider = builderProvider;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
@@ -641,9 +641,9 @@ public class NotificationConversationInfo extends LinearLayout implements
mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
BUBBLE_PREFERENCE_SELECTED);
}
if (mBubblesOptional.isPresent()) {
if (mBubblesManagerOptional.isPresent()) {
post(() -> {
mBubblesOptional.get().onUserChangedImportance(mEntry);
mBubblesManagerOptional.get().onUserChangedImportance(mEntry);
});
}
}

View File

@@ -47,7 +47,6 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -67,6 +66,7 @@ import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSav
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -117,7 +117,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final Lazy<StatusBar> mStatusBarLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
private final Optional<Bubbles> mBubblesOptional;
private final Optional<BubblesManager> mBubblesManagerOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
private final LauncherApps mLauncherApps;
@@ -142,7 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
Optional<Bubbles> bubblesOptional,
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
mContext = context;
@@ -158,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mBuilderProvider = builderProvider;
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
mBubblesOptional = bubblesOptional;
mBubblesManagerOptional = bubblesManagerOptional;
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
}
@@ -491,7 +491,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mMainHandler,
mBgHandler,
onConversationSettingsListener,
mBubblesOptional);
mBubblesManagerOptional);
}
/**

View File

@@ -306,7 +306,8 @@ public class NotificationIconAreaController implements
|| !entry.isPulseSuppressed())) {
return false;
}
if (mBubblesOptional.isPresent() && mBubblesOptional.get().isBubbleExpanded(entry)) {
if (mBubblesOptional.isPresent()
&& mBubblesOptional.get().isBubbleExpanded(entry.getKey())) {
return false;
}
return true;

View File

@@ -144,7 +144,6 @@ import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
@@ -229,6 +228,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
@@ -648,8 +648,9 @@ public class StatusBar extends SystemUI implements DemoMode,
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final BubbleController.BubbleExpandListener mBubbleExpandListener;
private final Bubbles.BubbleExpandListener mBubbleExpandListener;
private ActivityIntentHelper mActivityIntentHelper;
private NotificationStackScrollLayoutController mStackScrollerController;
@@ -697,6 +698,7 @@ public class StatusBar extends SystemUI implements DemoMode,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -776,6 +778,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mVibratorHelper = vibratorHelper;
mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
@@ -1141,8 +1144,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
ScrimView scrimForBubble = mBubblesOptional.isPresent()
? mBubblesOptional.get().getScrimForBubble() : null;
ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
? mBubblesManagerOptional.get().getScrimForBubble() : null;
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);

View File

@@ -48,7 +48,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -76,6 +75,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -104,7 +104,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
private final Optional<Bubbles> mBubblesOptional;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
Optional<Bubbles> bubblesOptional,
Optional<BubblesManager> bubblesManagerOptional,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -176,7 +176,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
mBubblesOptional = bubblesOptional;
mBubblesManagerOptional = bubblesManagerOptional;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -387,14 +387,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
if (!mBubblesOptional.isPresent()) {
if (!mBubblesManagerOptional.isPresent()) {
return;
}
if (Looper.getMainLooper().isCurrentThread()) {
mBubblesOptional.get().expandStackAndSelectBubble(entry);
mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
} else {
mMainThreadHandler.post(() -> mBubblesOptional.get().expandStackAndSelectBubble(entry));
mMainThreadHandler.post(
() -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
}
}
@@ -609,7 +610,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
private final Optional<Bubbles> mBubblesOptional;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -646,7 +647,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
Optional<Bubbles> bubblesOptional,
Optional<BubblesManager> bubblesManager,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -676,7 +677,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
mBubblesOptional = bubblesOptional;
mBubblesManagerOptional = bubblesManager;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -732,7 +733,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager,
mKeyguardManager,
mDreamManager,
mBubblesOptional,
mBubblesManagerOptional,
mAssistManagerLazy,
mRemoteInputManager,
mGroupMembershipManager,

View File

@@ -97,6 +97,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -155,6 +156,7 @@ public interface StatusBarPhoneModule {
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -233,6 +235,7 @@ public interface StatusBarPhoneModule {
wakefulnessLifecycle,
statusBarStateController,
vibratorHelper,
bubblesManagerOptional,
bubblesOptional,
visualStabilityManager,
deviceProvisionedController,

View File

@@ -0,0 +1,725 @@
/*
* 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.wmshell;
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;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
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;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.bubbles.BubbleEntry;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.ScrimView;
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;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.IntConsumer;
/**
* The SysUi side bubbles manager which communicate with other SysUi components.
*/
@SysUISingleton
public class BubblesManager implements Dumpable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesManager" : TAG_BUBBLES;
private final Context mContext;
private final Bubbles mBubbles;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final ShadeController mShadeController;
private final IStatusBarService mBarService;
private final INotificationManager mNotificationManager;
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final NotificationEntryManager mNotificationEntryManager;
private final NotifPipeline mNotifPipeline;
private final ScrimView mBubbleScrim;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
/**
* Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
* which means bubbles feature not support.
*/
@Nullable
public static BubblesManager create(Context context,
Optional<Bubbles> bubblesOptional,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
DumpManager dumpManager) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context, bubblesOptional.get(),
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags,
dumpManager);
} else {
return null;
}
}
@VisibleForTesting
BubblesManager(Context context,
Bubbles bubbles,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
DumpManager dumpManager) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
mShadeController = shadeController;
mNotificationManager = notificationManager;
mNotificationInterruptStateProvider = interruptionStateProvider;
mNotificationGroupManager = groupManager;
mNotificationEntryManager = entryManager;
mNotifPipeline = notifPipeline;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE))
: statusBarService;
mBubbleScrim = new ScrimView(mContext);
mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mBubbles.setBubbleScrim(mBubbleScrim);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
setupNotifPipeline();
} else {
setupNEM();
}
dumpManager.registerDumpable(TAG, this);
statusBarStateController.addCallback(new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
boolean isShade = newState == SHADE;
bubbles.onStatusBarStateChanged(isShade);
}
});
configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
mBubbles.onConfigChanged(newConfig);
}
@Override
public void onUiModeChanged() {
mBubbles.updateForThemeChanges();
}
@Override
public void onThemeChanged() {
mBubbles.updateForThemeChanges();
}
});
zenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
mBubbles.onZenStateChanged();
}
@Override
public void onConfigChanged(ZenModeConfig config) {
mBubbles.onZenStateChanged();
}
});
notifUserManager.addUserChangedListener(
new NotificationLockscreenUserManager.UserChangedListener() {
@Override
public void onUserChanged(int userId) {
mBubbles.onUserChanged(userId);
}
});
mSysuiProxy = new Bubbles.SysuiProxy() {
@Override
@Nullable
public BubbleEntry getPendingOrActiveEntry(String key) {
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
return entry == null ? null : notifToBubbleEntry(entry);
}
@Override
public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) {
List<BubbleEntry> result = new ArrayList<>();
List<NotificationEntry> activeEntries =
mNotificationEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < activeEntries.size(); i++) {
NotificationEntry entry = activeEntries.get(i);
if (savedBubbleKeys.contains(entry.getKey())
&& mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& entry.isBubble()) {
result.add(notifToBubbleEntry(entry));
}
}
return result;
}
@Override
public boolean isNotificationShadeExpand() {
return mNotificationShadeWindowController.getPanelExpanded();
}
@Override
public boolean shouldBubbleUp(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
}
return false;
}
@Override
public void setNotificationInterruption(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
entry.setInterruption();
}
}
@Override
public void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag) {
mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag);
}
@Override
public void notifyRemoveNotification(String key, int reason) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
for (NotifCallback cb : mCallbacks) {
cb.removeNotification(entry, getDismissedByUserStats(entry, true), reason);
}
}
}
@Override
public void notifyInvalidateNotifications(String reason) {
for (NotifCallback cb : mCallbacks) {
cb.invalidateNotifications(reason);
}
}
@Override
public void notifyMaybeCancelSummary(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
for (NotifCallback cb : mCallbacks) {
cb.maybeCancelSummary(entry);
}
}
}
@Override
public void removeNotificationEntry(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
mNotificationGroupManager.onEntryRemoved(entry);
}
}
@Override
public void updateNotificationBubbleButton(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null && entry.getRow() != null) {
entry.getRow().updateBubbleButton();
}
}
@Override
public void updateNotificationSuppression(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
mNotificationGroupManager.updateSuppression(entry);
}
}
@Override
public void onStackExpandChanged(boolean shouldExpand) {
sysUiState
.setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
.commitUpdate(mContext.getDisplayId());
}
@Override
public void onUnbubbleConversation(String key) {
final NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
if (entry != null) {
onUserChangedBubble(entry, false /* shouldBubble */);
}
}
};
mBubbles.setSysuiProxy(mSysuiProxy);
}
private void setupNEM() {
mNotificationEntryManager.addNotificationEntryListener(
new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
BubblesManager.this.onEntryAdded(entry);
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
BubblesManager.this.onEntryUpdated(entry);
}
@Override
public void onEntryRemoved(NotificationEntry entry,
@Nullable NotificationVisibility visibility,
boolean removedByUser, int reason) {
BubblesManager.this.onEntryRemoved(entry);
}
@Override
public void onNotificationRankingUpdated(RankingMap rankingMap) {
BubblesManager.this.onRankingUpdate(rankingMap);
}
});
// The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
mNotificationEntryManager.addNotificationRemoveInterceptor(
(key, entry, dismissReason) -> {
final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
final boolean isUserDismiss = dismissReason == REASON_CANCEL
|| dismissReason == REASON_CLICK;
final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
|| dismissReason == REASON_APP_CANCEL_ALL;
final boolean isSummaryCancel =
dismissReason == REASON_GROUP_SUMMARY_CANCELED;
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
boolean userRemovedNotif =
(entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDismiss || isSummaryCancel;
if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
return false;
});
mNotificationGroupManager.registerGroupChangeListener(
new NotificationGroupManagerLegacy.OnGroupChangeListener() {
@Override
public void onGroupSuppressionChanged(
NotificationGroupManagerLegacy.NotificationGroup group,
boolean suppressed) {
// More notifications could be added causing summary to no longer
// be suppressed -- in this case need to remove the key.
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null
&& mBubbles.isSummarySuppressed(groupKey)) {
mBubbles.removeSuppressedSummary(groupKey);
}
}
});
addNotifCallback(new NotifCallback() {
@Override
public void removeNotification(NotificationEntry entry,
DismissedByUserStats dismissedByUserStats, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
dismissedByUserStats, reason);
}
@Override
public void invalidateNotifications(String reason) {
mNotificationEntryManager.updateNotifications(reason);
}
@Override
public void maybeCancelSummary(NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroupKey();
if (mBubbles.isSummarySuppressed(groupKey)) {
mBubbles.removeSuppressedSummary(groupKey);
final NotificationEntry summary =
mNotificationEntryManager.getActiveNotificationUnfiltered(
mBubbles.getSummaryKey(groupKey));
if (summary != null) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
// Check if we still need to remove the summary from NoManGroup because the summary
// may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
// For example:
// 1. Bubbled notifications (group) is posted to shade and are visible bubbles
// 2. User expands bubbles so now their respective notifications in the shade are
// hidden, including the group summary
// 3. User removes all bubbles
// 4. We expect all the removed bubbles AND the summary (note: the summary was
// never added to the suppressedSummary list in BubbleData, so we add this check)
NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
if (summary != null) {
ArrayList<NotificationEntry> summaryChildren =
mNotificationGroupManager.getLogicalChildren(summary.getSbn());
boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
if (!isSummaryThisNotif && (summaryChildren == null
|| summaryChildren.isEmpty())) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
}
});
}
private void setupNotifPipeline() {
mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
public void onEntryAdded(NotificationEntry entry) {
BubblesManager.this.onEntryAdded(entry);
}
@Override
public void onEntryUpdated(NotificationEntry entry) {
BubblesManager.this.onEntryUpdated(entry);
}
@Override
public void onEntryRemoved(NotificationEntry entry,
@NotifCollection.CancellationReason int reason) {
BubblesManager.this.onEntryRemoved(entry);
}
@Override
public void onRankingUpdate(RankingMap rankingMap) {
BubblesManager.this.onRankingUpdate(rankingMap);
}
});
}
void onEntryAdded(NotificationEntry entry) {
if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& entry.isBubble()) {
mBubbles.onEntryAdded(notifToBubbleEntry(entry));
}
}
void onEntryUpdated(NotificationEntry entry) {
mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
mNotificationInterruptStateProvider.shouldBubbleUp(entry));
}
void onEntryRemoved(NotificationEntry entry) {
mBubbles.onEntryRemoved(notifToBubbleEntry(entry));
}
void onRankingUpdate(RankingMap rankingMap) {
mBubbles.onRankingUpdated(rankingMap);
}
/**
* Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
* Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
* Instead, this is taken care of by {@link BubbleCoordinator}.
*/
private DismissedByUserStats getDismissedByUserStats(
NotificationEntry entry,
boolean isVisible) {
return new DismissedByUserStats(
DISMISSAL_BUBBLE,
DISMISS_SENTIMENT_NEUTRAL,
NotificationVisibility.obtain(
entry.getKey(),
entry.getRanking().getRank(),
mNotificationEntryManager.getActiveNotificationsCount(),
isVisible,
NotificationLogger.getNotificationLocation(entry)));
}
/**
* Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController}
* since we want the scrim's appearance and behavior to be identical to that of the notification
* shade scrim.
*/
public ScrimView getScrimForBubble() {
return mBubbleScrim;
}
/**
* We intercept notification entries (including group summaries) dismissed by the user when
* there is an active bubble associated with it. We do this so that developers can still
* cancel it (and hence the bubbles associated with it).
*
* @return true if we want to intercept the dismissal of the entry, else false.
* @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer)
*/
public boolean handleDismissalInterception(NotificationEntry entry) {
if (entry == null) {
return false;
}
List<NotificationEntry> children = entry.getAttachedNotifChildren();
List<BubbleEntry> bubbleChildren = null;
if (children != null) {
bubbleChildren = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
bubbleChildren.add(notifToBubbleEntry(children.get(i)));
}
}
return mBubbles.handleDismissalInterception(notifToBubbleEntry(entry), bubbleChildren,
// TODO : b/171847985 should re-work on notification side to make this more clear.
(int i) -> {
if (i >= 0) {
for (NotifCallback cb : mCallbacks) {
cb.removeNotification(children.get(i),
getDismissedByUserStats(children.get(i), true),
REASON_GROUP_SUMMARY_CANCELED);
}
} else {
mNotificationGroupManager.onEntryRemoved(entry);
}
});
}
/**
* Request the stack expand if needed, then select the specified Bubble as current.
* If no bubble exists for this entry, one is created.
*
* @param entry the notification for the bubble to be selected
*/
public void expandStackAndSelectBubble(NotificationEntry entry) {
mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry));
}
/** See {@link NotifCallback}. */
public void addNotifCallback(NotifCallback callback) {
mCallbacks.add(callback);
}
/**
* When a notification is marked Priority, expand the stack if needed,
* then (maybe create and) select the given bubble.
*
* @param entry the notification for the bubble to show
*/
public void onUserChangedImportance(NotificationEntry entry) {
try {
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
mShadeController.collapsePanel(true);
if (entry.getRow() != null) {
entry.getRow().updateBubbleButton();
}
}
/**
* Called when a user has indicated that an active notification should be shown as a bubble.
* <p>
* 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 change bubble state for.
* @param shouldBubble whether the notification should show as a bubble or not.
*/
public void onUserChangedBubble(@NonNull final 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;
}
// Update the state in NotificationManagerService
try {
int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags);
} catch (RemoteException e) {
}
// Change the settings
channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext,
mNotificationManager, entry, channel);
channel.setAllowBubbles(shouldBubble);
try {
int currentPref = mNotificationManager.getBubblePreferenceForPackage(appPkg, appUid);
if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) {
mNotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED);
}
mNotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
if (shouldBubble) {
mShadeController.collapsePanel(true);
if (entry.getRow() != null) {
entry.getRow().updateBubbleButton();
}
}
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
mBubbles.dump(fd, pw, args);
}
static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
e.shouldSuppressPeek());
}
/**
* Callback for when the BubbleController wants to interact with the notification pipeline to:
* - Remove a previously bubbled notification
* - Update the notification shade since bubbled notification should/shouldn't be showing
*/
public interface NotifCallback {
/**
* Called when a bubbled notification that was hidden from the shade is now being removed
* This can happen when an app cancels a bubbled notification or when the user dismisses a
* bubble.
*/
void removeNotification(@NonNull NotificationEntry entry,
@NonNull DismissedByUserStats stats, int reason);
/**
* Called when a bubbled notification has changed whether it should be
* filtered from the shade.
*/
void invalidateNotifications(@NonNull String reason);
/**
* Called on a bubbled entry that has been removed when there are no longer
* bubbled entries in its group.
*
* Checks whether its group has any other (non-bubbled) children. If it doesn't,
* removes all remnants of the group's summary from the notification pipeline.
* TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
*/
void maybeCancelSummary(@NonNull NotificationEntry entry);
}
}

View File

@@ -102,10 +102,10 @@ public class BubbleDataTest extends SysuiTestCase {
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@Mock
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
@Mock
private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener;
private Bubbles.PendingIntentCanceledListener mPendingIntentCanceledListener;
@Before
public void setUp() throws Exception {
@@ -171,12 +171,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Verify
verifyUpdateReceived();
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE);
assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE);
}
@Test
@@ -269,7 +268,7 @@ public class BubbleDataTest extends SysuiTestCase {
sendUpdatedEntryAtTime(mEntryC1, 6000);
verifyUpdateReceived();
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_AGED);
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1, null /* persistedBubble */);
@@ -277,7 +276,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/,
true /* showInShade */);
verifyUpdateReceived();
assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED);
assertBubbleRemoved(mBubbleA2, Bubbles.DISMISS_AGED);
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -294,14 +293,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
mBubbleData.setMaxOverflowBubbles(1);
mBubbleData.dismissBubbleWithKey(
mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
// Overflow max of 1 is reached; A1 is oldest, so it gets removed
mBubbleData.dismissBubbleWithKey(
mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -322,14 +319,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(),
BubbleController.DISMISS_NOTIF_CANCEL);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
// Test
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(),
BubbleController.DISMISS_GROUP_CANCELLED);
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_GROUP_CANCELLED);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of());
}
@@ -409,8 +404,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
// TODO: this should fail if things work as I expect them to?
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
@@ -430,8 +424,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderNotChanged();
}
@@ -450,8 +443,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
}
@@ -545,8 +537,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Verify the selection was cleared.
verifyUpdateReceived();
@@ -646,8 +637,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryB2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleA1);
}
@@ -671,13 +661,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
mBubbleData.dismissBubbleWithKey(
mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryB1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
}
@@ -791,8 +779,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
mBubbleData.dismissBubbleWithKey(
mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertExpandedChangedTo(false);
}

View File

@@ -60,7 +60,7 @@ public class BubbleTest extends SysuiTestCase {
private Bubble mBubble;
@Mock
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
@Before
public void setUp() {

View File

@@ -1,85 +0,0 @@
/*
* 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.INotificationManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
*/
public class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController,
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
BubbleDataRepository dataRepository,
SysUiState sysUiState,
INotificationManager notificationManager,
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
Handler mainHandler,
ShellTaskOrganizer shellTaskOrganizer,
BubblePositioner positioner) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
dataRepository, sysUiState, notificationManager, statusBarService,
windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
mainHandler, shellTaskOrganizer, positioner);
setInflateSynchronously(true);
}
}

View File

@@ -116,7 +116,7 @@ public class NotificationFilterTest extends SysuiTestCase {
new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
Optional.of(() -> mock(Bubbles.class))));
Optional.of(mock(Bubbles.class))));
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);

View File

@@ -76,12 +76,12 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Rule;
@@ -137,7 +137,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
private Bubbles mBubbles;
private BubblesManager mBubblesManager;
@Mock
private LauncherApps mLauncherApps;
@Mock
@@ -255,7 +255,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
}
@@ -279,7 +279,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -330,7 +330,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -355,7 +355,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
assertEquals(GONE, textView.getVisibility());
@@ -379,7 +379,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -414,7 +414,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -442,7 +442,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -468,7 +468,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -495,7 +495,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
false,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -520,7 +520,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
}
@@ -548,7 +548,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -579,7 +579,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -609,7 +609,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -653,7 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mTestableLooper.processAllMessages();
@@ -696,7 +696,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -740,7 +740,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -777,7 +777,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -813,7 +813,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -851,7 +851,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -887,7 +887,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -923,7 +923,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -958,7 +958,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
View silence = mNotificationInfo.findViewById(R.id.silence);
silence.performClick();
@@ -992,7 +992,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), any(), eq(CONVERSATION_ID));
@@ -1017,7 +1017,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), any(), eq(CONVERSATION_ID));
@@ -1052,7 +1052,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
@@ -1092,7 +1092,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
mTestHandler, null, Optional.of(mBubbles));
mTestHandler, null, Optional.of(mBubblesManager));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);

View File

@@ -84,6 +84,7 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplies;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.After;
import org.junit.Before;
@@ -96,6 +97,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
/**
@@ -243,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
true,
null,
mFalsingManager,
mPeopleNotificationIdentifier
mPeopleNotificationIdentifier,
Optional.of(mock(BubblesManager.class))
));
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))

View File

@@ -67,7 +67,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -81,6 +80,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Ignore;
@@ -131,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock private UserContextProvider mContextTracker;
@Mock private Bubbles mBubbles;
@Mock private BubblesManager mBubblesManager;
@Mock(answer = Answers.RETURNS_SELF)
private PriorityOnboardingDialogController.Builder mBuilder;
private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder;
@@ -156,7 +156,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
() -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
mINotificationManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mProvider,
mAssistantFeedbackController, Optional.of(mBubbles),
mAssistantFeedbackController, Optional.of(mBubblesManager),
new UiEventLoggerFake(), mOnUserInteractionCallback);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mCheckSaveListener, mOnSettingsClickListener);

View File

@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.InflatedSmartReplies;
import com.android.systemui.wmshell.BubblesManager;
import org.mockito.ArgumentCaptor;
@@ -121,7 +122,7 @@ public class NotificationTestHelper {
mGroupMembershipManager = new NotificationGroupManagerLegacy(
mStatusBarStateController,
() -> mock(PeopleNotificationIdentifier.class),
Optional.of(() -> mock(Bubbles.class)));
Optional.of((mock(Bubbles.class))));
mGroupExpansionManager = mGroupMembershipManager;
mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class),
@@ -430,7 +431,8 @@ public class NotificationTestHelper {
mock(FalsingManager.class),
mStatusBarStateController,
mPeopleNotificationIdentifier,
mock(OnUserInteractionCallback.class));
mock(OnUserInteractionCallback.class),
Optional.of(mock(BubblesManager.class)));
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);

View File

@@ -92,7 +92,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
Optional.of(() -> mock(Bubbles.class)));
Optional.of(mock(Bubbles.class)));
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);

View File

@@ -70,7 +70,7 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
Optional.of(() -> mock(Bubbles.class)));
Optional.of(mock(Bubbles.class)));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}

View File

@@ -53,7 +53,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -77,6 +76,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Test;
@@ -116,7 +116,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
private Bubbles mBubbles;
private BubblesManager mBubblesManager;
@Mock
private ShadeControllerImpl mShadeController;
@Mock
@@ -193,7 +193,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
mock(IDreamManager.class),
Optional.of(mBubbles),
Optional.of(mBubblesManager),
() -> mAssistManager,
mRemoteInputManager,
mock(NotificationGroupManagerLegacy.class),
@@ -280,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
verify(mBubbles).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
// This is called regardless, and simply short circuits when there is nothing to do.
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -312,7 +312,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -342,7 +342,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mBubblesManager).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mShadeController, atLeastOnce()).collapsePanel();

View File

@@ -145,6 +145,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -223,6 +224,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
@Mock private VibratorHelper mVibratorHelper;
@Mock private BubblesManager mBubblesManager;
@Mock private Bubbles mBubbles;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@@ -377,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase {
wakefulnessLifecycle,
mStatusBarStateController,
mVibratorHelper,
Optional.of(mBubblesManager),
Optional.of(mBubbles),
mVisualStabilityManager,
mDeviceProvisionedController,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.systemui.bubbles;
package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
@@ -64,6 +64,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.Bubble;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.bubbles.BubbleDataRepository;
import com.android.systemui.bubbles.BubbleEntry;
import com.android.systemui.bubbles.BubbleLogger;
import com.android.systemui.bubbles.BubblePositioner;
import com.android.systemui.bubbles.BubbleStackView;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -112,12 +120,12 @@ import java.util.List;
/**
* Tests the NotificationEntryManager setup with BubbleController.
* The {@link NotifPipeline} setup with BubbleController is tested in
* {@link NewNotifPipelineBubbleControllerTest}.
* {@link NewNotifPipelineBubblesTest}.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubbleControllerTest extends SysuiTestCase {
public class BubblesTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
@@ -157,6 +165,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener;
@@ -167,9 +176,12 @@ public class BubbleControllerTest extends SysuiTestCase {
private ExpandableNotificationRow mRow2;
private ExpandableNotificationRow mRow3;
private ExpandableNotificationRow mNonBubbleNotifRow;
private BubbleEntry mBubbleEntry;
private BubbleEntry mBubbleEntry2;
private BubbleEntry mBubbleEntry3;
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
@Mock
@@ -226,6 +238,9 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry());
mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry());
mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3.getEntry());
// Return non-null notification data from the NEM
when(mNotificationEntryManager
@@ -258,26 +273,13 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(HeadsUpManager.class),
mock(Handler.class)
);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(
mContext,
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
mBubbleData,
mConfigurationController,
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
mNotificationGroupManager,
mNotificationEntryManager,
mNotifPipeline,
mFeatureFlagsOldPipeline,
mDumpManager,
mFloatingContentCoordinator,
mDataRepository,
mSysUiState,
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
@@ -288,6 +290,25 @@ public class BubbleControllerTest extends SysuiTestCase {
mPositioner);
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
mBubbleController,
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
mConfigurationController,
mStatusBarService,
mock(INotificationManager.class),
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
mNotificationGroupManager,
mNotificationEntryManager,
mNotifPipeline,
mSysUiState,
mFeatureFlagsOldPipeline,
mDumpManager);
// Get a reference to the BubbleController's entry listener
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture());
@@ -300,7 +321,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testAddBubble() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
@@ -309,20 +330,20 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testHasBubbles() {
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
}
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mBubbleEntry);
assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotificationEntryManager).updateNotifications(any());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -331,14 +352,14 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testPromoteBubble_autoExpand() throws Exception {
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry2);
mBubbleController.updateBubble(mBubbleEntry);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
@@ -361,18 +382,18 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testCancelOverflowBubble() {
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
mBubbleController.updateBubble(mBubbleEntry2);
mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
false, /* showInShade */ true);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -381,11 +402,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testUserChange_doesNotRemoveNotif() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_CHANGED);
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
@@ -395,15 +416,15 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
verify(mNotificationEntryManager, times(1)).updateNotifications(any());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry2);
verify(mNotificationEntryManager, times(2)).updateNotifications(any());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
@@ -417,12 +438,12 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
@@ -434,7 +455,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Collapse
mBubbleController.collapseStack();
@@ -450,15 +471,15 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -472,7 +493,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
@@ -481,7 +502,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -501,12 +522,12 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -520,7 +541,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -529,12 +550,12 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -548,7 +569,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -558,7 +579,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Nothing should have changed
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -568,8 +589,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -584,13 +605,13 @@ public class BubbleControllerTest extends SysuiTestCase {
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Dismiss currently expanded
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE);
Bubbles.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
@@ -602,7 +623,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE);
Bubbles.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
@@ -619,7 +640,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
@@ -636,7 +657,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
@@ -653,11 +674,11 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -667,22 +688,22 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testSuppressNotif_onUpdateNotif() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -695,13 +716,13 @@ public class BubbleControllerTest extends SysuiTestCase {
final String key = mRow.getEntry().getKey();
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Simulate notification cancellation.
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(mRow.getEntry());
mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
assertTrue(mSysUiStateBubblesExpanded);
}
@@ -710,7 +731,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -726,31 +747,31 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@Test
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
@Test
public void testRemoveBubble_noLongerBubbleAfterUpdate()
throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -766,7 +787,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_succeeds_appCancel() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -780,7 +801,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_entryListenerRemove() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -792,11 +813,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_clearAllIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL);
@@ -805,17 +826,17 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeBubble_userDismissNotifIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
@@ -824,21 +845,21 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeNotif_inOverflow_intercepted() {
// Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -852,14 +873,14 @@ public class BubbleControllerTest extends SysuiTestCase {
public void removeNotif_notInOverflow_notIntercepted() {
// Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
mRow.getEntry().getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -872,11 +893,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
mBubbleController.updateBubble(
mRow.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
@@ -887,12 +908,12 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleData.setMaxOverflowBubbles(1);
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertEquals(mBubbleData.getBubbles().size(), 2);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
mBubbleController.removeBubble(
mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow2.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), eq(REASON_CANCEL));
@@ -902,22 +923,22 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
BubbleController.NotificationSuppressionChangedListener listener =
mock(BubbleController.NotificationSuppressionChangedListener.class);
Bubbles.NotificationSuppressionChangedListener listener =
mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -926,21 +947,21 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
BubbleController.NotificationSuppressionChangedListener listener =
mock(BubbleController.NotificationSuppressionChangedListener.class);
Bubbles.NotificationSuppressionChangedListener listener =
mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -959,11 +980,11 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry()));
groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -979,7 +1000,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
@@ -1002,7 +1023,7 @@ public class BubbleControllerTest extends SysuiTestCase {
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
@@ -1017,7 +1038,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// THEN the bubble child is suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry()));
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is removed from GroupManager
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
@@ -1031,18 +1053,18 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
// Setup
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
assertTrue(mBubbleController.hasBubbles());
// Overflow it
mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
Bubbles.DISMISS_USER_GESTURE);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue();
// Test
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.systemui.bubbles;
package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
@@ -46,7 +46,6 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
import android.os.Handler;
import android.os.PowerManager;
import android.service.dreams.IDreamManager;
@@ -60,8 +59,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.bubbles.BubbleDataRepository;
import com.android.systemui.bubbles.BubbleEntry;
import com.android.systemui.bubbles.BubbleLogger;
import com.android.systemui.bubbles.BubblePositioner;
import com.android.systemui.bubbles.BubbleStackView;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -69,9 +74,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
@@ -81,10 +84,8 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -92,7 +93,6 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -111,18 +111,18 @@ import java.util.List;
/**
* Tests the NotifPipeline setup with BubbleController.
* The NotificationEntryManager setup with BubbleController is tested in
* {@link BubbleControllerTest}.
* {@link BubblesTest}.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public class NewNotifPipelineBubblesTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
private NotificationGroupManagerLegacy mNotificationGroupManager;
@Mock
private BubbleController.NotifCallback mNotifCallback;
private BubblesManager.NotifCallback mNotifCallback;
@Mock
private WindowManager mWindowManager;
@Mock
@@ -136,8 +136,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Mock
private ZenModeConfig mZenModeConfig;
@Mock
private FaceManager mFaceManager;
@Mock
private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@@ -156,6 +154,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotifCollectionListener mEntryListener;
@@ -163,8 +162,10 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
private ExpandableNotificationRow mRow2;
private ExpandableNotificationRow mNonBubbleNotifRow;
private BubbleEntry mBubbleEntry;
private BubbleEntry mBubbleEntry2;
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
@Mock
@@ -174,16 +175,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Mock
private ShadeController mShadeController;
@Mock
private NotificationShelfComponent mNotificationShelfComponent;
@Mock
private NotifPipeline mNotifPipeline;
@Mock
private FeatureFlags mFeatureFlagsNewPipeline;
@Mock
private DumpManager mDumpManager;
@Mock
private LockscreenLockIconController mLockIconController;
@Mock
private IStatusBarService mStatusBarService;
@Mock
private LauncherApps mLauncherApps;
@@ -197,7 +194,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private BubbleData mBubbleData;
private TestableLooper mTestableLooper;
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@Before
public void setUp() throws Exception {
@@ -205,26 +201,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
new InjectionInflationController(SystemUIFactory.getInstance().getSysUIComponent()
.createViewInstanceCreatorFactory()),
new NotificationShelfComponent.Builder() {
@Override
public NotificationShelfComponent.Builder notificationShelf(
NotificationShelf view) {
return this;
}
@Override
public NotificationShelfComponent build() {
return mNotificationShelfComponent;
}
},
mLockIconController);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -240,6 +218,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry());
mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry());
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -265,23 +245,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mBubbleController = new TestableBubbleController(
mContext,
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
mBubbleData,
mConfigurationController,
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
mNotificationGroupManager,
mNotificationEntryManager,
mNotifPipeline,
mFeatureFlagsNewPipeline,
mDumpManager,
mFloatingContentCoordinator,
mDataRepository,
mSysUiState,
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
@@ -290,9 +256,28 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mock(Handler.class),
mock(ShellTaskOrganizer.class),
mPositioner);
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
mBubbleController,
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
mConfigurationController,
mStatusBarService,
mock(INotificationManager.class),
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
mNotificationGroupManager,
mNotificationEntryManager,
mNotifPipeline,
mSysUiState,
mFeatureFlagsNewPipeline,
mDumpManager);
mBubblesManager.addNotifCallback(mNotifCallback);
// Get a reference to the BubbleController's entry listener
verify(mNotifPipeline, atLeastOnce())
.addCollectionListener(mNotifListenerCaptor.capture());
@@ -301,26 +286,26 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testAddBubble() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
}
@Test
public void testHasBubbles() {
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
}
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
}
@@ -328,17 +313,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_withDismissedNotif_inOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// We don't remove the notification since the bubble is still in overflow.
@@ -349,19 +335,20 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_withDismissedNotif_notInOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// Since the notif is dismissed and not in overflow, once the bubble is removed,
@@ -373,15 +360,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry2);
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
@@ -393,12 +380,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
@@ -407,7 +394,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Collapse
mBubbleController.collapseStack();
@@ -421,15 +409,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mEntryListener.onEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -440,15 +428,17 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -467,11 +457,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -483,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -492,12 +483,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -509,7 +500,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -519,7 +510,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Nothing should have changed
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -529,8 +520,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mEntryListener.onEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -543,13 +534,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Dismiss currently expanded
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE);
Bubbles.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
@@ -561,7 +552,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE);
Bubbles.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
@@ -576,7 +567,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
@@ -591,7 +582,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
@@ -606,11 +597,11 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the suppress notif bubble
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -618,22 +609,22 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testSuppressNotif_onUpdateNotif() {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -643,7 +634,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onEntryAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -659,31 +650,31 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@Test
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
@Test
public void testRemoveBubble_noLongerBubbleAfterUpdate()
throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -699,7 +690,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_entryListenerRemove() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -711,36 +702,36 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_intercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeBubble_dismissIntoOverflow_intercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Intercept dismissal since bubble is going into overflow
assertTrue(intercepted);
@@ -749,19 +740,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_notIntercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Not a bubble anymore so we don't intercept dismissal.
assertFalse(intercepted);
@@ -769,21 +759,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
BubbleController.NotificationSuppressionChangedListener listener =
mock(BubbleController.NotificationSuppressionChangedListener.class);
Bubbles.NotificationSuppressionChangedListener listener =
mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleController.handleDismissalInterception(mRow.getEntry());
mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -792,21 +782,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
BubbleController.NotificationSuppressionChangedListener listener =
mock(BubbleController.NotificationSuppressionChangedListener.class);
Bubbles.NotificationSuppressionChangedListener listener =
mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -825,11 +815,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry()));
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -845,7 +836,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0);
@@ -868,7 +859,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
@@ -882,11 +873,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// THEN the bubble child still exists as a bubble and is suppressed from the shade
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry()));
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is also suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupSummary.getEntry()));
groupSummary.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey()));
}
/**

View File

@@ -0,0 +1,57 @@
/*
* 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.wmshell;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
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.BubbleLogger;
import com.android.systemui.bubbles.BubblePositioner;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
*/
public class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
BubbleData data,
FloatingContentCoordinator floatingContentCoordinator,
BubbleDataRepository dataRepository,
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
Handler mainHandler,
ShellTaskOrganizer shellTaskOrganizer,
BubblePositioner positioner) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, mainHandler, shellTaskOrganizer, positioner);
setInflateSynchronously(true);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.systemui.bubbles;
package com.android.systemui.wmshell;
import android.content.ContentResolver;
import android.hardware.display.AmbientDisplayConfiguration;