Fix a bug showing the background through a punch hole when switching bubbles.
Bug: 126856255
Test: Manual. Recorded screen and verified.
Change-Id: I2d29fc30b192c0d28c7d7e28b45ec7e01385a912
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index eb85589..e0ed111 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -118,6 +118,7 @@
private final BubbleTaskStackListener mTaskStackListener;
private BubbleStateChangeListener mStateChangeListener;
private BubbleExpandListener mExpandListener;
+ @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
private BubbleData mBubbleData;
private BubbleStackView mStackView;
@@ -178,7 +179,12 @@
@Inject
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
- BubbleData data) {
+ BubbleData data) {
+ this(context, statusBarWindowController, data, null /* synchronizer */);
+ }
+
+ public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
+ BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer) {
mContext = context;
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
@@ -206,6 +212,7 @@
}
mBubbleData = data;
+ mSurfaceSynchronizer = synchronizer;
}
/**
@@ -297,7 +304,7 @@
mStackView.updateBubble(notif, updatePosition);
} else {
if (mStackView == null) {
- mStackView = new BubbleStackView(mContext, mBubbleData);
+ mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
// XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
// between bubble and the shade
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 888e3fe..e150ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,6 +19,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -32,6 +33,7 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.StatsLog;
+import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -67,6 +69,43 @@
private static final String TAG = "BubbleStackView";
private static final boolean DEBUG = false;
+ /**
+ * Interface to synchronize {@link View} state and the screen.
+ *
+ * {@hide}
+ */
+ interface SurfaceSynchronizer {
+ /**
+ * Wait until requested change on a {@link View} is reflected on the screen.
+ *
+ * @param callback callback to run after the change is reflected on the screen.
+ */
+ void syncSurfaceAndRun(Runnable callback);
+ }
+
+ private static final SurfaceSynchronizer DEFAULT_SURFACE_SYNCHRONIZER =
+ new SurfaceSynchronizer() {
+ @Override
+ public void syncSurfaceAndRun(Runnable callback) {
+ Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
+ // Just wait 2 frames. There is no guarantee, but this is usually enough time that
+ // the requested change is reflected on the screen.
+ // TODO: Once SurfaceFlinger provide APIs to sync the state of {@code View} and
+ // surfaces, rewrite this logic with them.
+ private int mFrameWait = 2;
+
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ if (--mFrameWait > 0) {
+ Choreographer.getInstance().postFrameCallback(this);
+ } else {
+ callback.run();
+ }
+ }
+ });
+ }
+ };
+
private Point mDisplaySize;
private final SpringAnimation mExpandedViewXAnim;
@@ -79,7 +118,6 @@
private FrameLayout mExpandedViewContainer;
-
private int mBubbleSize;
private int mBubblePadding;
private int mExpandedAnimateXDistance;
@@ -129,7 +167,11 @@
}
};
- public BubbleStackView(Context context, BubbleData data) {
+ @NonNull private final SurfaceSynchronizer mSurfaceSynchronizer;
+
+
+ public BubbleStackView(Context context, BubbleData data,
+ @Nullable SurfaceSynchronizer synchronizer) {
super(context);
mBubbleData = data;
@@ -160,6 +202,7 @@
mStackAnimationController = new StackAnimationController();
mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize);
+ mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
mBubbleContainer = new PhysicsAnimationLayout(context);
mBubbleContainer.setMaxRenderedChildren(
@@ -314,17 +357,28 @@
// If we weren't previously expanded we should animate open.
animateExpansion(true /* expand */);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ mExpandedBubble.entry.setShowInShadeWhenBubble(false);
+ notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
} else {
- // Otherwise just update the views
- // TODO: probably animate / page to expanded one
- updateExpandedBubble();
- updatePointerPosition();
- requestUpdate();
- logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
- logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ // Make the container of the expanded view transparent before removing the expanded view
+ // from it. Otherwise a punch hole created by {@link android.view.SurfaceView} in the
+ // expanded view becomes visible on the screen. See b/126856255
+ mExpandedViewContainer.setAlpha(0.0f);
+
+ mSurfaceSynchronizer.syncSurfaceAndRun(new Runnable() {
+ @Override
+ public void run() {
+ updateExpandedBubble();
+ updatePointerPosition();
+ requestUpdate();
+ logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ logBubbleEvent(mExpandedBubble,
+ StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ mExpandedBubble.entry.setShowInShadeWhenBubble(false);
+ notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
+ }
+ });
}
- mExpandedBubble.entry.setShowInShadeWhenBubble(false);
- notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
}
/**
@@ -767,6 +821,7 @@
mExpandedViewContainer.addView(mExpandedBubble.expandedView);
mExpandedBubble.expandedView.populateExpandedView();
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ mExpandedViewContainer.setAlpha(1.0f);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 42c221a..4e9d892 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -329,10 +329,10 @@
}
static class TestableBubbleController extends BubbleController {
-
+ // Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
StatusBarWindowController statusBarWindowController, BubbleData data) {
- super(context, statusBarWindowController, data);
+ super(context, statusBarWindowController, data, Runnable::run);
}
@Override