Merge "8/ Updating bubbles to run on shell thread" into sc-dev

This commit is contained in:
Winson Chung
2021-01-31 03:17:26 +00:00
committed by Android (Google) Code Review
23 changed files with 690 additions and 258 deletions

View File

@@ -49,6 +49,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Encapsulates the data and UI elements of a bubble.
@@ -58,6 +59,7 @@ public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
private final String mKey;
private final Executor mMainExecutor;
private long mLastUpdated;
private long mLastAccessed;
@@ -156,7 +158,8 @@ public class Bubble implements BubbleViewProvider {
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
Executor mainExecutor) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -170,20 +173,25 @@ public class Bubble implements BubbleViewProvider {
mDesiredHeightResId = desiredHeightResId;
mTitle = title;
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
}
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final BubbleEntry entry,
@Nullable final Bubbles.NotificationSuppressionChangedListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener) {
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
mKey = entry.getKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
if (mIntent != null) {
mIntent.unregisterCancelListener(mIntentCancelListener);
}
intentCancelListener.onPendingIntentCanceled(this);
mainExecutor.execute(() -> {
intentCancelListener.onPendingIntentCanceled(this);
});
};
mMainExecutor = mainExecutor;
setEntry(entry);
}
@@ -329,7 +337,8 @@ public class Bubble implements BubbleViewProvider {
stackView,
iconFactory,
skipInflation,
callback);
callback,
mMainExecutor);
if (mInflateSynchronously) {
mInflationTask.onPostExecute(mInflationTask.doInBackground());
} else {

View File

@@ -28,6 +28,16 @@ import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_BOT
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_LEFT;
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_NONE;
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_RIGHT;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_AGED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_BLOCKED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_GROUP_CANCELLED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_INVALID_INTENT;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_BUBBLE_UP;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -45,6 +55,8 @@ import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -53,6 +65,7 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseSetArray;
import android.view.View;
import android.view.ViewGroup;
@@ -75,6 +88,9 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -83,7 +99,7 @@ import java.util.function.IntConsumer;
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
public class BubbleController implements Bubbles {
public class BubbleController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -101,7 +117,8 @@ public class BubbleController implements Bubbles {
public static final String BOTTOM_POSITION = "Bottom";
private final Context mContext;
private BubbleExpandListener mExpandListener;
private final BubblesImpl mImpl = new BubblesImpl();
private Bubbles.BubbleExpandListener mExpandListener;
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
@@ -111,7 +128,7 @@ public class BubbleController implements Bubbles {
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
private BubblePositioner mBubblePositioner;
private SysuiProxy mSysuiProxy;
private Bubbles.SysuiProxy mSysuiProxy;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
@@ -177,7 +194,7 @@ public class BubbleController implements Bubbles {
/**
* Injected constructor.
*/
public static BubbleController create(Context context,
public static Bubbles create(Context context,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@Nullable IStatusBarService statusBarService,
@@ -186,14 +203,15 @@ public class BubbleController implements Bubbles {
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
ShellExecutor mainExecutor) {
ShellExecutor mainExecutor,
Handler mainHandler) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
BubbleData data = new BubbleData(context, logger, positioner);
BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps),
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, organizer, positioner, mainExecutor);
logger, organizer, positioner, mainExecutor, mainHandler).mImpl;
}
/**
@@ -212,7 +230,8 @@ public class BubbleController implements Bubbles {
BubbleLogger bubbleLogger,
ShellTaskOrganizer organizer,
BubblePositioner positioner,
ShellExecutor mainExecutor) {
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
@@ -299,7 +318,12 @@ public class BubbleController implements Bubbles {
mBubbleData.removeBubblesWithInvalidShortcuts(
packageName, validShortcuts, DISMISS_SHORTCUT_REMOVED);
}
});
}, mainHandler);
}
@VisibleForTesting
public Bubbles getImpl() {
return mImpl;
}
/**
@@ -313,8 +337,7 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void openBubbleOverflow() {
private void openBubbleOverflow() {
ensureStackViewCreated();
mBubbleData.setShowingOverflow(true);
mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
@@ -322,8 +345,7 @@ public class BubbleController implements Bubbles {
}
/** Called when any taskbar state changes (e.g. visibility, position, sizes). */
@Override
public void onTaskbarChanged(Bundle b) {
private void onTaskbarChanged(Bundle b) {
if (b == null) {
return;
}
@@ -371,8 +393,7 @@ public class BubbleController implements Bubbles {
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
@Override
public void onStatusBarVisibilityChanged(boolean visible) {
private void onStatusBarVisibilityChanged(boolean visible) {
if (mStackView != null) {
// Hide the stack temporarily if the status bar has been made invisible, and the stack
// is collapsed. An expanded stack should remain visible until collapsed.
@@ -380,15 +401,13 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void onZenStateChanged() {
private void onZenStateChanged() {
for (Bubble b : mBubbleData.getBubbles()) {
b.setShowDot(b.showInShade());
}
}
@Override
public void onStatusBarStateChanged(boolean isShade) {
private void onStatusBarStateChanged(boolean isShade) {
mIsStatusBarShade = isShade;
if (!mIsStatusBarShade) {
collapseStack();
@@ -402,8 +421,7 @@ public class BubbleController implements Bubbles {
updateStack();
}
@Override
public void onUserChanged(int newUserId) {
private void onUserChanged(int newUserId) {
saveBubbles(mCurrentUserId);
mBubbleData.dismissAll(DISMISS_USER_CHANGED);
restoreBubbles(newUserId);
@@ -442,7 +460,7 @@ public class BubbleController implements Bubbles {
return mBubblePositioner;
}
SysuiProxy getSysuiProxy() {
Bubbles.SysuiProxy getSysuiProxy() {
return mSysuiProxy;
}
@@ -453,7 +471,8 @@ public class BubbleController implements Bubbles {
private void ensureStackViewCreated() {
if (mStackView == null) {
mStackView = new BubbleStackView(
mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
mMainExecutor);
mStackView.onOrientationChanged();
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
@@ -576,8 +595,7 @@ public class BubbleController implements Bubbles {
mSavedBubbleKeysPerUser.remove(mCurrentUserId);
}
@Override
public void updateForThemeChanges() {
private void updateForThemeChanges() {
if (mStackView != null) {
mStackView.onThemeChanged();
}
@@ -593,8 +611,7 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void onConfigChanged(Configuration newConfig) {
private void onConfigChanged(Configuration newConfig) {
if (mBubblePositioner != null) {
// This doesn't trigger any changes, always update it
mBubblePositioner.update(newConfig.orientation);
@@ -620,18 +637,19 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void setBubbleScrim(View view) {
private void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
mBubbleScrim = view;
callback.accept(mMainExecutor, mMainExecutor.executeBlockingForResult(() -> {
return Looper.myLooper();
}, Looper.class));
}
@Override
public void setSysuiProxy(SysuiProxy proxy) {
private void setSysuiProxy(Bubbles.SysuiProxy proxy) {
mSysuiProxy = proxy;
}
@Override
public void setExpandListener(BubbleExpandListener listener) {
@VisibleForTesting
public void setExpandListener(Bubbles.BubbleExpandListener listener) {
mExpandListener = ((isExpanding, key) -> {
if (listener != null) {
listener.onBubbleExpandChanged(isExpanding, key);
@@ -654,17 +672,17 @@ public class BubbleController implements Bubbles {
return mBubbleData.hasBubbles() || mBubbleData.isShowingOverflow();
}
@Override
@VisibleForTesting
public boolean isStackExpanded() {
return mBubbleData.isExpanded();
}
@Override
@VisibleForTesting
public void collapseStack() {
mBubbleData.setExpanded(false /* expanded */);
}
@Override
@VisibleForTesting
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
&& !mBubbleData.getAnyBubbleWithkey(key).showInShade());
@@ -674,23 +692,19 @@ public class BubbleController implements Bubbles {
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
@Override
public boolean isSummarySuppressed(String groupKey) {
return mBubbleData.isSummarySuppressed(groupKey);
private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
Executor callbackExecutor) {
if (mBubbleData.isSummarySuppressed(groupKey)) {
mBubbleData.removeSuppressedSummary(groupKey);
if (callback != null) {
callbackExecutor.execute(() -> {
callback.accept(mBubbleData.getSummaryKey(groupKey));
});
}
}
}
@Override
public void removeSuppressedSummary(String groupKey) {
mBubbleData.removeSuppressedSummary(groupKey);
}
@Override
public String getSummaryKey(String groupKey) {
return mBubbleData.getSummaryKey(groupKey);
}
@Override
public boolean isBubbleExpanded(String key) {
private boolean isBubbleExpanded(String key) {
return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
&& mBubbleData.getSelectedBubble().getKey().equals(key);
}
@@ -704,7 +718,7 @@ public class BubbleController implements Bubbles {
setIsBubble(bubble, true /* isBubble */);
}
@Override
@VisibleForTesting
public void expandStackAndSelectBubble(BubbleEntry entry) {
if (mIsStatusBarShade) {
mNotifEntryToExpandOnShadeUnlock = null;
@@ -809,15 +823,13 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void onEntryAdded(BubbleEntry entry) {
private void onEntryAdded(BubbleEntry entry) {
if (canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
@Override
public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -828,8 +840,7 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void onEntryRemoved(BubbleEntry entry) {
private void onEntryRemoved(BubbleEntry entry) {
if (isSummaryOfBubbles(entry)) {
final String groupKey = entry.getStatusBarNotification().getGroupKey();
mBubbleData.removeSuppressedSummary(groupKey);
@@ -844,8 +855,7 @@ public class BubbleController implements Bubbles {
}
}
@Override
public void onRankingUpdated(RankingMap rankingMap) {
private void onRankingUpdated(RankingMap rankingMap) {
if (mTmpRanking == null) {
mTmpRanking = new NotificationListenerService.Ranking();
}
@@ -882,6 +892,8 @@ public class BubbleController implements Bubbles {
return bubbleChildren;
}
for (Bubble bubble : mBubbleData.getActiveBubbles()) {
// TODO(178620678): Prevent calling into SysUI since this can be a part of a blocking
// call from SysUI to Shell
final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) {
bubbleChildren.add(bubble);
@@ -951,7 +963,7 @@ public class BubbleController implements Bubbles {
ArrayList<Bubble> bubblesToBeRemovedFromRepository = new ArrayList<>();
for (Pair<Bubble, Integer> removed : removedBubbles) {
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
@Bubbles.DismissReason final int reason = removed.second;
if (mStackView != null) {
mStackView.removeBubble(bubble);
@@ -1029,8 +1041,7 @@ public class BubbleController implements Bubbles {
}
};
@Override
public boolean handleDismissalInterception(BubbleEntry entry,
private boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (isSummaryOfBubbles(entry)) {
handleSummaryDismissalInterception(entry, children, removeCallback);
@@ -1137,8 +1148,7 @@ public class BubbleController implements Bubbles {
/**
* Description of current bubble state.
*/
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
private void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BubbleController state:");
mBubbleData.dump(fd, pw, args);
pw.println();
@@ -1216,4 +1226,175 @@ public class BubbleController implements Bubbles {
}
}
}
private class BubblesImpl implements Bubbles {
@Override
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
return mMainExecutor.executeBlockingForResult(() -> {
return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey);
}, Boolean.class);
}
@Override
public boolean isBubbleExpanded(String key) {
return mMainExecutor.executeBlockingForResult(() -> {
return BubbleController.this.isBubbleExpanded(key);
}, Boolean.class);
}
@Override
public boolean isStackExpanded() {
return mMainExecutor.executeBlockingForResult(() -> {
return BubbleController.this.isStackExpanded();
}, Boolean.class);
}
@Override
public void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
Executor callbackExecutor) {
mMainExecutor.execute(() -> {
BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, callback,
callbackExecutor);
});
}
@Override
public void collapseStack() {
mMainExecutor.execute(() -> {
BubbleController.this.collapseStack();
});
}
@Override
public void updateForThemeChanges() {
mMainExecutor.execute(() -> {
BubbleController.this.updateForThemeChanges();
});
}
@Override
public void expandStackAndSelectBubble(BubbleEntry entry) {
mMainExecutor.execute(() -> {
BubbleController.this.expandStackAndSelectBubble(entry);
});
}
@Override
public void onTaskbarChanged(Bundle b) {
mMainExecutor.execute(() -> {
BubbleController.this.onTaskbarChanged(b);
});
}
@Override
public void openBubbleOverflow() {
mMainExecutor.execute(() -> {
BubbleController.this.openBubbleOverflow();
});
}
@Override
public boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
return mMainExecutor.executeBlockingForResult(() -> {
return BubbleController.this.handleDismissalInterception(entry, children,
removeCallback);
}, Boolean.class);
}
@Override
public void setSysuiProxy(SysuiProxy proxy) {
mMainExecutor.execute(() -> {
BubbleController.this.setSysuiProxy(proxy);
});
}
@Override
public void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
mMainExecutor.execute(() -> {
BubbleController.this.setBubbleScrim(view, callback);
});
}
@Override
public void setExpandListener(BubbleExpandListener listener) {
mMainExecutor.execute(() -> {
BubbleController.this.setExpandListener(listener);
});
}
@Override
public void onEntryAdded(BubbleEntry entry) {
mMainExecutor.execute(() -> {
BubbleController.this.onEntryAdded(entry);
});
}
@Override
public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
mMainExecutor.execute(() -> {
BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
});
}
@Override
public void onEntryRemoved(BubbleEntry entry) {
mMainExecutor.execute(() -> {
BubbleController.this.onEntryRemoved(entry);
});
}
@Override
public void onRankingUpdated(RankingMap rankingMap) {
mMainExecutor.execute(() -> {
BubbleController.this.onRankingUpdated(rankingMap);
});
}
@Override
public void onStatusBarVisibilityChanged(boolean visible) {
mMainExecutor.execute(() -> {
BubbleController.this.onStatusBarVisibilityChanged(visible);
});
}
@Override
public void onZenStateChanged() {
mMainExecutor.execute(() -> {
BubbleController.this.onZenStateChanged();
});
}
@Override
public void onStatusBarStateChanged(boolean isShade) {
mMainExecutor.execute(() -> {
BubbleController.this.onStatusBarStateChanged(isShade);
});
}
@Override
public void onUserChanged(int newUserId) {
mMainExecutor.execute(() -> {
BubbleController.this.onUserChanged(newUserId);
});
}
@Override
public void onConfigChanged(Configuration newConfig) {
mMainExecutor.execute(() -> {
BubbleController.this.onConfigChanged(newConfig);
});
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try {
mMainExecutor.executeBlocking(() -> {
BubbleController.this.dump(fd, pw, args);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to dump BubbleController in 2s");
}
}
}
}

View File

@@ -46,6 +46,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -117,6 +118,7 @@ public class BubbleData {
private final Context mContext;
private final BubblePositioner mPositioner;
private final Executor mMainExecutor;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
/** Bubbles that aged out to overflow. */
@@ -155,10 +157,12 @@ public class BubbleData {
*/
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner) {
public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner,
Executor mainExecutor) {
mContext = context;
mLogger = bubbleLogger;
mPositioner = positioner;
mMainExecutor = mainExecutor;
mOverflow = new BubbleOverflow(context, positioner);
mBubbles = new ArrayList<>();
mOverflowBubbles = new ArrayList<>();
@@ -264,7 +268,8 @@ public class BubbleData {
bubbleToReturn = mPendingBubbles.get(key);
} else if (entry != null) {
// New bubble
bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener);
bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener,
mMainExecutor);
} else {
// Persisted bubble being promoted
bubbleToReturn = persistedBubble;

View File

@@ -27,6 +27,8 @@ import android.util.Log
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.annotations.ExternalThread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -34,12 +36,12 @@ import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps) {
internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps,
private val mainExecutor : ShellExecutor) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
private val persistentRepository = BubblePersistentRepository(context)
private val ioScope = CoroutineScope(Dispatchers.IO)
private val uiScope = CoroutineScope(Dispatchers.Main)
private var job: Job? = null
/**
@@ -109,6 +111,8 @@ internal class BubbleDataRepository(context: Context, private val launcherApps:
/**
* Load bubbles from disk.
* @param cb The callback to be run after the bubbles are loaded. This callback is always made
* on the main thread of the hosting process.
*/
@SuppressLint("WrongConstant")
fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
@@ -163,10 +167,11 @@ internal class BubbleDataRepository(context: Context, private val launcherApps:
shortcutInfo,
entity.desiredHeight,
entity.desiredHeightResId,
entity.title
entity.title,
mainExecutor
) }
}
uiScope.launch { cb(bubbles) }
mainExecutor.execute { cb(bubbles) }
}
}

View File

@@ -79,6 +79,7 @@ import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import java.io.FileDescriptor;
@@ -147,7 +148,7 @@ public class BubbleStackView extends FrameLayout
* Handler to use for all delayed animations - this way, we can easily cancel them before
* starting a new animation.
*/
private final Handler mDelayedAnimationHandler = new Handler();
private final ShellExecutor mDelayedAnimationExecutor;
/**
* Interface to synchronize {@link View} state and the screen.
@@ -311,7 +312,7 @@ public class BubbleStackView extends FrameLayout
}
}
private BubbleController.BubbleExpandListener mExpandListener;
private Bubbles.BubbleExpandListener mExpandListener;
/** Callback to run when we want to unbubble the given notification's conversation. */
private Consumer<String> mUnbubbleConversationCallback;
@@ -734,9 +735,11 @@ public class BubbleStackView extends FrameLayout
@SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleController bubbleController,
BubbleData data, @Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator) {
FloatingContentCoordinator floatingContentCoordinator,
ShellExecutor mainExecutor) {
super(context);
mDelayedAnimationExecutor = mainExecutor;
mBubbleController = bubbleController;
mBubbleData = data;
@@ -1366,7 +1369,7 @@ public class BubbleStackView extends FrameLayout
/**
* Sets the listener to notify when the bubble stack is expanded.
*/
public void setExpandListener(BubbleController.BubbleExpandListener listener) {
public void setExpandListener(Bubbles.BubbleExpandListener listener) {
mExpandListener = listener;
}
@@ -1734,7 +1737,7 @@ public class BubbleStackView extends FrameLayout
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
}
mDelayedAnimationHandler.postDelayed(() -> {
mDelayedAnimationExecutor.executeDelayed(() -> {
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -1791,10 +1794,12 @@ public class BubbleStackView extends FrameLayout
final long startDelay =
(long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
mDelayedAnimationHandler.postDelayed(() -> mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
/* collapseTo */,
() -> mBubbleContainer.setActiveController(mStackAnimationController)), startDelay);
mDelayedAnimationExecutor.executeDelayed(() -> {
mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
/* collapseTo */,
() -> mBubbleContainer.setActiveController(mStackAnimationController));
}, startDelay);
if (mTaskbarScrim.getVisibility() == VISIBLE) {
mTaskbarScrim.animate().alpha(0f).start();
@@ -1945,7 +1950,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
mDelayedAnimationHandler.postDelayed(() -> {
mDelayedAnimationExecutor.executeDelayed(() -> {
if (!mIsExpanded) {
mIsBubbleSwitchAnimating = false;
return;
@@ -1978,7 +1983,7 @@ public class BubbleStackView extends FrameLayout
* animating flags for those animations.
*/
private void cancelDelayedExpandCollapseSwitchAnimations() {
mDelayedAnimationHandler.removeCallbacksAndMessages(null);
mDelayedAnimationExecutor.removeAllCallbacks();
mIsExpansionAnimating = false;
mIsBubbleSwitchAnimating = false;

View File

@@ -46,6 +46,7 @@ import com.android.wm.shell.R;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Simple task to inflate views & load necessary info to display a bubble.
@@ -71,6 +72,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
private BubbleIconFactory mIconFactory;
private boolean mSkipInflation;
private Callback mCallback;
private Executor mMainExecutor;
/**
* Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -82,7 +84,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
BubbleStackView stackView,
BubbleIconFactory factory,
boolean skipInflation,
Callback c) {
Callback c,
Executor mainExecutor) {
mBubble = b;
mContext = new WeakReference<>(context);
mController = new WeakReference<>(controller);
@@ -90,6 +93,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
mIconFactory = factory;
mSkipInflation = skipInflation;
mCallback = c;
mMainExecutor = mainExecutor;
}
@Override
@@ -103,10 +107,12 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (isCancelled() || viewInfo == null) {
return;
}
mBubble.setViewInfo(viewInfo);
if (mCallback != null) {
mCallback.onBubbleViewsReady(mBubble);
}
mMainExecutor.execute(() -> {
mBubble.setViewInfo(viewInfo);
if (mCallback != null) {
mCallback.onBubbleViewsReady(mBubble);
}
});
}
/**

View File

@@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Looper;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.view.View;
@@ -37,6 +38,9 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
@@ -86,13 +90,14 @@ public interface Bubbles {
/** @return {@code true} if stack of bubbles is expanded or not. */
boolean isStackExpanded();
/** @return {@code true} if the summary for the provided group key is suppressed. */
boolean isSummarySuppressed(String groupKey);
/**
* Removes a group key indicating that summary for this group should no longer be suppressed.
* Removes a group key indicating that the summary for this group should no longer be
* suppressed.
*
* @param callback If removed, this callback will be called with the summary key of the group
*/
void removeSuppressedSummary(String groupKey);
void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
Executor callbackExecutor);
/** Tell the stack of bubbles to collapse. */
void collapseStack();
@@ -134,19 +139,16 @@ public interface Bubbles {
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
IntConsumer removeCallback);
/**
* Retrieves the notif entry key of the summary associated with the provided group key.
*
* @param groupKey the group to look up
* @return the key for the notification that is the summary of this group.
*/
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 the scrim view for bubbles.
*
* @param callback The callback made with the executor and the executor's looper that the view
* will be running on.
**/
void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback);
/** Set a listener to be notified of bubble expand events. */
void setExpandListener(BubbleExpandListener listener);

View File

@@ -91,7 +91,6 @@ abstract class RelativeTouchListener : View.OnTouchListener {
private var touchSlop: Int = -1
private var movedEnough = false
private val handler = Handler(Looper.myLooper()!!)
private var performedLongClick = false
@Suppress("UNCHECKED_CAST")
@@ -115,7 +114,7 @@ abstract class RelativeTouchListener : View.OnTouchListener {
viewPositionOnTouchDown.set(v.translationX, v.translationY)
performedLongClick = false
handler.postDelayed({
v.handler.postDelayed({
if (v.isLongClickable) {
performedLongClick = v.performLongClick()
}
@@ -125,7 +124,7 @@ abstract class RelativeTouchListener : View.OnTouchListener {
MotionEvent.ACTION_MOVE -> {
if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
movedEnough = true
handler.removeCallbacksAndMessages(null)
v.handler.removeCallbacksAndMessages(null)
}
if (movedEnough) {
@@ -141,7 +140,7 @@ abstract class RelativeTouchListener : View.OnTouchListener {
} else if (!performedLongClick) {
v.performClick()
} else {
handler.removeCallbacksAndMessages(null)
v.handler.removeCallbacksAndMessages(null)
}
velocityTracker.clear()

View File

@@ -20,6 +20,7 @@ import android.os.Looper;
import android.os.SystemClock;
import android.os.Trace;
import java.lang.reflect.Array;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -67,6 +68,26 @@ public interface ShellExecutor extends Executor {
executeBlocking(runnable, 2, TimeUnit.SECONDS);
}
/**
* Convenience method to execute the blocking call with a default timeout and returns a value.
* Waits indefinitely for a typed result from a call.
*/
default <T> T executeBlockingForResult(Supplier<T> runnable, Class clazz) {
final T[] result = (T[]) Array.newInstance(clazz, 1);
final CountDownLatch latch = new CountDownLatch(1);
execute(() -> {
result[0] = runnable.get();
latch.countDown();
});
try {
latch.await();
return result[0];
} catch (InterruptedException e) {
return null;
}
}
/**
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
*/

View File

@@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
import com.google.common.collect.ImmutableList;
@@ -98,6 +99,8 @@ public class BubbleDataTest extends ShellTestCase {
private PendingIntent mDeleteIntent;
@Mock
private BubbleLogger mBubbleLogger;
@Mock
private ShellExecutor mMainExecutor;
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -124,21 +127,31 @@ public class BubbleDataTest extends ShellTestCase {
mock(NotificationListenerService.Ranking.class);
when(ranking.visuallyInterruptive()).thenReturn(true);
mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null,
mMainExecutor);
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null,
mMainExecutor);
mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
TestableBubblePositioner positioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner);
mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner,
mMainExecutor);
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -796,7 +809,7 @@ public class BubbleDataTest extends ShellTestCase {
assertWithMessage("addedBubble").that(update.addedBubble).isEqualTo(expected);
}
private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
private void assertBubbleRemoved(Bubble expected, @Bubbles.DismissReason int reason) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertWithMessage("removedBubbles").that(update.removedBubbles)
.isEqualTo(ImmutableList.of(Pair.create(expected, reason)));

View File

@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -54,6 +55,8 @@ public class BubbleTest extends ShellTestCase {
private Notification mNotif;
@Mock
private StatusBarNotification mSbn;
@Mock
private ShellExecutor mMainExecutor;
private BubbleEntry mBubbleEntry;
private Bundle mExtras;
@@ -78,7 +81,7 @@ public class BubbleTest extends ShellTestCase {
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
when(mSbn.getKey()).thenReturn("mock");
mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null);
mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null, mMainExecutor);
}
@Test

View File

@@ -47,17 +47,17 @@ public class PipTouchStateTest extends ShellTestCase {
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
private CountDownLatch mHoverExitCallbackTriggeredLatch;
private TestShellExecutor mShellMainExecutor;
private TestShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
mShellMainExecutor = new TestShellExecutor();
mMainExecutor = new TestShellExecutor();
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
mDoubleTapCallbackTriggeredLatch::countDown,
mHoverExitCallbackTriggeredLatch::countDown,
mShellMainExecutor);
mMainExecutor);
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
}
@@ -87,7 +87,7 @@ public class PipTouchStateTest extends ShellTestCase {
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10);
mTouchState.scheduleDoubleTapTimeoutCallback();
mShellMainExecutor.flushAll();
mMainExecutor.flushAll();
assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0);
}
@@ -122,7 +122,7 @@ public class PipTouchStateTest extends ShellTestCase {
@Test
public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
mTouchState.scheduleHoverExitTimeoutCallback();
mShellMainExecutor.flushAll();
mMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
}
@@ -137,7 +137,7 @@ public class PipTouchStateTest extends ShellTestCase {
mTouchState.scheduleHoverExitTimeoutCallback();
mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
0, 0));
mShellMainExecutor.flushAll();
mMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
}

View File

@@ -29,6 +29,7 @@ import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dump.DumpManager;
@@ -75,6 +76,7 @@ import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Binds;
import dagger.BindsOptionalOf;
@@ -153,6 +155,7 @@ public abstract class SystemUIModule {
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
// TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton
@Provides
@@ -166,11 +169,12 @@ public abstract class SystemUIModule {
ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
DumpManager dumpManager) {
DumpManager dumpManager, @Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
sysuiMainExecutor));
}
}

View File

@@ -26,25 +26,41 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
import java.util.concurrent.Executor;
/**
* A view which can draw a scrim
* A view which can draw a scrim. This view maybe be used in multiple windows running on different
* threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
* need to be careful to synchronize when necessary.
*/
public class ScrimView extends View {
private final Object mColorLock = new Object();
@GuardedBy("mColorLock")
private final ColorExtractor.GradientColors mColors;
// Used only for returning the colors
private final ColorExtractor.GradientColors mTmpColors = new ColorExtractor.GradientColors();
private float mViewAlpha = 1.0f;
private Drawable mDrawable;
private PorterDuffColorFilter mColorFilter;
private int mTintColor;
private Runnable mChangeRunnable;
private Executor mChangeRunnableExecutor;
private Executor mExecutor;
private Looper mExecutorLooper;
public ScrimView(Context context) {
this(context, null);
@@ -64,7 +80,16 @@ public class ScrimView extends View {
mDrawable = new ScrimDrawable();
mDrawable.setCallback(this);
mColors = new ColorExtractor.GradientColors();
updateColorWithTint(false);
mExecutorLooper = Looper.myLooper();
mExecutor = Runnable::run;
executeOnExecutor(() -> {
updateColorWithTint(false);
});
}
public void setExecutor(Executor executor, Looper looper) {
mExecutor = executor;
mExecutorLooper = looper;
}
@Override
@@ -75,11 +100,13 @@ public class ScrimView extends View {
}
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
mDrawable.setCallback(this);
mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
mDrawable.setAlpha((int) (255 * mViewAlpha));
invalidate();
executeOnExecutor(() -> {
mDrawable = drawable;
mDrawable.setCallback(this);
mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
mDrawable.setAlpha((int) (255 * mViewAlpha));
invalidate();
});
}
@Override
@@ -99,6 +126,13 @@ public class ScrimView extends View {
}
}
@Override
public void setClickable(boolean clickable) {
executeOnExecutor(() -> {
super.setClickable(clickable);
});
}
public void setColors(@NonNull ColorExtractor.GradientColors colors) {
setColors(colors, false);
}
@@ -107,11 +141,15 @@ public class ScrimView extends View {
if (colors == null) {
throw new IllegalArgumentException("Colors cannot be null");
}
if (mColors.equals(colors)) {
return;
}
mColors.set(colors);
updateColorWithTint(animated);
executeOnExecutor(() -> {
synchronized(mColorLock) {
if (mColors.equals(colors)) {
return;
}
mColors.set(colors);
}
updateColorWithTint(animated);
});
}
@VisibleForTesting
@@ -120,7 +158,10 @@ public class ScrimView extends View {
}
public ColorExtractor.GradientColors getColors() {
return mColors;
synchronized(mColorLock) {
mTmpColors.set(mColors);
}
return mTmpColors;
}
public void setTint(int color) {
@@ -128,11 +169,13 @@ public class ScrimView extends View {
}
public void setTint(int color, boolean animated) {
if (mTintColor == color) {
return;
}
mTintColor = color;
updateColorWithTint(animated);
executeOnExecutor(() -> {
if (mTintColor == color) {
return;
}
mTintColor = color;
updateColorWithTint(animated);
});
}
private void updateColorWithTint(boolean animated) {
@@ -160,7 +203,7 @@ public class ScrimView extends View {
}
if (mChangeRunnable != null) {
mChangeRunnable.run();
mChangeRunnableExecutor.execute(mChangeRunnable);
}
}
@@ -184,26 +227,37 @@ public class ScrimView extends View {
if (isNaN(alpha)) {
throw new IllegalArgumentException("alpha cannot be NaN: " + alpha);
}
if (alpha != mViewAlpha) {
mViewAlpha = alpha;
executeOnExecutor(() -> {
if (alpha != mViewAlpha) {
mViewAlpha = alpha;
mDrawable.setAlpha((int) (255 * alpha));
if (mChangeRunnable != null) {
mChangeRunnable.run();
mDrawable.setAlpha((int) (255 * alpha));
if (mChangeRunnable != null) {
mChangeRunnableExecutor.execute(mChangeRunnable);
}
}
}
});
}
public float getViewAlpha() {
return mViewAlpha;
}
public void setChangeRunnable(Runnable changeRunnable) {
public void setChangeRunnable(Runnable changeRunnable, Executor changeRunnableExecutor) {
mChangeRunnable = changeRunnable;
mChangeRunnableExecutor = changeRunnableExecutor;
}
@Override
protected boolean canReceivePointerEvents() {
return false;
}
private void executeOnExecutor(Runnable r) {
if (mExecutor == null || Looper.myLooper() == mExecutorLooper) {
r.run();
} else {
mExecutor.execute(r);
}
}
}

View File

@@ -48,6 +48,7 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.FeatureFlags;
@@ -65,6 +66,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -150,6 +152,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private final AlarmTimeout mTimeTicker;
private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
private final Handler mHandler;
private final Executor mMainExecutor;
private final BlurUtils mBlurUtils;
private GradientColors mColors;
@@ -214,8 +217,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
BlurUtils blurUtils, ConfigurationController configurationController,
FeatureFlags featureFlags) {
FeatureFlags featureFlags, @Main Executor mainExecutor) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA;
ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque()
@@ -228,6 +230,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
mMainExecutor = mainExecutor;
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", mHandler);
mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build();
@@ -273,7 +276,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
updateThemeColors();
if (mScrimBehindChangeRunnable != null) {
mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable);
mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
mScrimBehindChangeRunnable = null;
}
@@ -1022,7 +1025,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (mScrimBehind == null) {
mScrimBehindChangeRunnable = changeRunnable;
} else {
mScrimBehind.setChangeRunnable(changeRunnable);
mScrimBehind.setChangeRunnable(changeRunnable, mMainExecutor);
}
}

View File

@@ -841,8 +841,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mBubbleExpandListener =
(isExpanding, key) -> {
mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
updateScrimController();
mContext.getMainExecutor().execute(() -> {
mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
updateScrimController();
});
};
mActivityIntentHelper = new ActivityIntentHelper(mContext);

View File

@@ -38,6 +38,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -83,10 +84,14 @@ import com.android.wm.shell.bubbles.Bubbles;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
import java.util.function.Supplier;
/**
* The SysUi side bubbles manager which communicate with other SysUi components.
@@ -106,8 +111,9 @@ public class BubblesManager implements Dumpable {
private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final NotificationEntryManager mNotificationEntryManager;
private final NotifPipeline mNotifPipeline;
private final Executor mSysuiMainExecutor;
private final ScrimView mBubbleScrim;
private 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<>();
@@ -133,14 +139,15 @@ public class BubblesManager implements Dumpable {
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
DumpManager dumpManager) {
DumpManager dumpManager,
Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context, bubblesOptional.get(),
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags,
dumpManager);
dumpManager, sysuiMainExecutor);
} else {
return null;
}
@@ -163,7 +170,8 @@ public class BubblesManager implements Dumpable {
NotifPipeline notifPipeline,
SysUiState sysUiState,
FeatureFlags featureFlags,
DumpManager dumpManager) {
DumpManager dumpManager,
Executor sysuiMainExecutor) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -173,6 +181,7 @@ public class BubblesManager implements Dumpable {
mNotificationGroupManager = groupManager;
mNotificationEntryManager = entryManager;
mNotifPipeline = notifPipeline;
mSysuiMainExecutor = sysuiMainExecutor;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -181,7 +190,9 @@ public class BubblesManager implements Dumpable {
mBubbleScrim = new ScrimView(mContext);
mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mBubbles.setBubbleScrim(mBubbleScrim);
mBubbles.setBubbleScrim(mBubbleScrim, (executor, looper) -> {
mBubbleScrim.setExecutor(executor, looper);
});
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
setupNotifPipeline();
@@ -237,128 +248,177 @@ public class BubblesManager implements Dumpable {
});
mSysuiProxy = new Bubbles.SysuiProxy() {
private <T> T executeBlockingForResult(Supplier<T> runnable, Executor executor,
Class clazz) {
if (Looper.myLooper() == Looper.getMainLooper()) {
return runnable.get();
}
final T[] result = (T[]) Array.newInstance(clazz, 1);
final CountDownLatch latch = new CountDownLatch(1);
executor.execute(() -> {
result[0] = runnable.get();
latch.countDown();
});
try {
latch.await();
return result[0];
} catch (InterruptedException e) {
return null;
}
}
@Override
@Nullable
public BubbleEntry getPendingOrActiveEntry(String key) {
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
return entry == null ? null : notifToBubbleEntry(entry);
return executeBlockingForResult(() -> {
NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
return entry == null ? null : notifToBubbleEntry(entry);
}, sysuiMainExecutor, BubbleEntry.class);
}
@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 executeBlockingForResult(() -> {
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;
return result;
}, sysuiMainExecutor, List.class);
}
@Override
public boolean isNotificationShadeExpand() {
return mNotificationShadeWindowController.getPanelExpanded();
return executeBlockingForResult(() -> {
return mNotificationShadeWindowController.getPanelExpanded();
}, sysuiMainExecutor, Boolean.class);
}
@Override
public boolean shouldBubbleUp(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null) {
return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
}
return false;
return executeBlockingForResult(() -> {
final NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
if (entry != null) {
return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
}
return false;
}, sysuiMainExecutor, Boolean.class);
}
@Override
public void setNotificationInterruption(String key) {
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
key);
if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
entry.setInterruption();
}
sysuiMainExecutor.execute(() -> {
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);
sysuiMainExecutor.execute(() -> {
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);
sysuiMainExecutor.execute(() -> {
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);
}
sysuiMainExecutor.execute(() -> {
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);
sysuiMainExecutor.execute(() -> {
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);
}
sysuiMainExecutor.execute(() -> {
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();
}
sysuiMainExecutor.execute(() -> {
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);
}
sysuiMainExecutor.execute(() -> {
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());
sysuiMainExecutor.execute(() -> {
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 */);
}
sysuiMainExecutor.execute(() -> {
final NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
if (entry != null) {
onUserChangedBubble(entry, false /* shouldBubble */);
}
});
}
};
mBubbles.setSysuiProxy(mSysuiProxy);
@@ -424,9 +484,8 @@ public class BubblesManager implements Dumpable {
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null
&& mBubbles.isSummarySuppressed(groupKey)) {
mBubbles.removeSuppressedSummary(groupKey);
if (!suppressed && groupKey != null) {
mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null);
}
}
});
@@ -449,19 +508,16 @@ public class BubblesManager implements Dumpable {
// 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);
mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> {
final NotificationEntry summary =
mNotificationEntryManager.getActiveNotificationUnfiltered(
mBubbles.getSummaryKey(groupKey));
mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey);
if (summary != null) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
}, mSysuiMainExecutor);
// 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.

View File

@@ -331,6 +331,7 @@ public abstract class WMShellBaseModule {
@BindsOptionalOf
abstract AppPairs optionalAppPairs();
// Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
static Optional<Bubbles> provideBubbles(Context context,
@@ -341,11 +342,12 @@ public abstract class WMShellBaseModule {
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
@ShellMainThread ShellExecutor mainExecutor) {
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
mainExecutor));
mainExecutor, mainHandler));
}
// Needs the shell main handler for ContentObserver callbacks

View File

@@ -54,6 +54,8 @@ import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.ConfigurationController;
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.util.wakelock.DelayedWakeLock;
import com.android.systemui.utils.os.FakeHandler;
@@ -220,7 +222,8 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController = new ScrimController(mLightBarController,
mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags);
mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags,
new FakeExecutor(new FakeSystemClock()));
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
mScrimController.setAnimatorListener(mAnimatorListener);

View File

@@ -166,6 +166,7 @@ public class BubblesTest extends SysuiTestCase {
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
private BubblesManager mBubblesManager;
// TODO(178618782): Move tests on the controller directly to the shell
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener;
@@ -221,6 +222,9 @@ public class BubblesTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
// For the purposes of this test, just run everything synchronously
ShellExecutor syncExecutor = new SyncExecutor();
mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
@@ -257,8 +261,9 @@ public class BubblesTest extends SysuiTestCase {
mSysUiStateBubblesExpanded =
(sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
// TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
@@ -273,7 +278,7 @@ public class BubblesTest extends SysuiTestCase {
);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -286,12 +291,13 @@ public class BubblesTest extends SysuiTestCase {
mBubbleLogger,
mShellTaskOrganizer,
mPositioner,
mock(ShellExecutor.class));
syncExecutor,
mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
mBubbleController,
mBubbleController.getImpl(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
@@ -306,7 +312,8 @@ public class BubblesTest extends SysuiTestCase {
mNotifPipeline,
mSysUiState,
mFeatureFlagsOldPipeline,
mDumpManager);
mDumpManager,
syncExecutor);
// Get a reference to the BubbleController's entry listener
verify(mNotificationEntryManager, atLeastOnce())

View File

@@ -83,8 +83,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.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.BubbleData;
@@ -203,6 +201,9 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
// For the purposes of this test, just run everything synchronously
ShellExecutor syncExecutor = new SyncExecutor();
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
@@ -227,7 +228,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
@@ -241,7 +242,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mock(Handler.class)
);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -254,12 +255,13 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mBubbleLogger,
mShellTaskOrganizer,
mPositioner,
mock(ShellExecutor.class));
syncExecutor,
mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
mContext,
mBubbleController,
mBubbleController.getImpl(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
@@ -274,7 +276,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mNotifPipeline,
mSysUiState,
mFeatureFlagsNewPipeline,
mDumpManager);
mDumpManager,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
// Get a reference to the BubbleController's entry listener

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2021 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 com.android.wm.shell.common.ShellExecutor;
/**
* And executor that just executes everything synchronously. To be removed once we move the
* tests of shell behavior over to the shell.
*/
public class SyncExecutor implements ShellExecutor {
@Override
public void execute(Runnable runnable) {
runnable.run();
}
@Override
public void executeDelayed(Runnable runnable, long delayMillis) {
runnable.run();
}
@Override
public void removeAllCallbacks() {
}
@Override
public void removeCallbacks(Runnable runnable) {
}
@Override
public boolean hasCallback(Runnable runnable) {
return false;
}
}

View File

@@ -49,10 +49,11 @@ public class TestableBubbleController extends BubbleController {
BubbleLogger bubbleLogger,
ShellTaskOrganizer shellTaskOrganizer,
BubblePositioner positioner,
ShellExecutor shellMainExecutor) {
ShellExecutor shellMainExecutor,
Handler shellMainHandler) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, shellTaskOrganizer, positioner, shellMainExecutor);
bubbleLogger, shellTaskOrganizer, positioner, shellMainExecutor, shellMainHandler);
setInflateSynchronously(true);
}
}