diff options
| author | 2021-01-31 03:17:26 +0000 | |
|---|---|---|
| committer | 2021-01-31 03:17:26 +0000 | |
| commit | 9330a9aced9d3de18fada72a498c5602db73789c (patch) | |
| tree | 1c9e0fa97e75e0d498ca6e8f1db20d30cbebf248 | |
| parent | 5fb31dd7568413bb8c1786c37153e964e840e188 (diff) | |
| parent | 26c4ade4d515842299a69d336ce3b950c899c80f (diff) | |
Merge "8/ Updating bubbles to run on shell thread" into sc-dev
23 files changed, 691 insertions, 259 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 122f91720bbd..ffeabd876b81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -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 { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 9419b9c6d344..7538c8b7ffad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -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); - } - - @Override - public void removeSuppressedSummary(String groupKey) { - mBubbleData.removeSuppressedSummary(groupKey); - } - - @Override - public String getSummaryKey(String groupKey) { - return mBubbleData.getSummaryKey(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 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"); + } + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index e24ff063b661..9d196ba32f8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt index fc565f17546d..3108b02cc010 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt @@ -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) } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index fac368677d12..af421facd72a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 0e7e92d9554a..c5a712e271e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -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); + } + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index fa5ac449cd54..6102147e4b53 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt index a1b0dbe0a6dd..cf0cefec401a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt @@ -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() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index b736fb0b9895..6abc8f6dda89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -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; @@ -68,6 +69,26 @@ public interface ShellExecutor extends Executor { } /** + * 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)}. */ void executeDelayed(Runnable runnable, long delayMillis); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 7c9b9c37c2c4..d3a736e9153e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -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); - - 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); + mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null, + mMainExecutor); + + 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))); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java index bde04b604c79..fc828b30279f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java @@ -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 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java index 0d4d1269f767..35656bde7169 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 4d697005be7b..b0067cd15c1b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -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)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index 7f30009cda6f..6023b7f6f4f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -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); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 19c0b6dc193a..e39065b6cc04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index b20c45780183..e08224c84813 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index bf823b4b22b8..6e7aed064159 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -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. diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 103e6bb08ed4..c2e4e149f8e0 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 3b2e0550b9e7..21368d6d5309 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -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); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index ccc2eb328a04..76269dda8245 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -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()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 00f4e3a3f144..5340ff7e967c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java new file mode 100644 index 000000000000..d40eecffb29e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java @@ -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; + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index 3f918e8b8633..cdf47b82561e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -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); } } |