From 0d96baec13bc46dcf23ae1076a82934b395f7ae7 Mon Sep 17 00:00:00 2001 From: Cyrus Boadway Date: Wed, 30 Jun 2021 10:36:51 +0000 Subject: [PATCH] Defer onColorsChanged updates during widget-to-home animation Color change updates recreate the view, which can interfere with the widget-to-home return animation, specifically the GhostView's use of the AppWidgetHostView's render node. Deferring the application of color changes until the end of the animation allows the widget to settle and remove the GhostView before a color change might recreate the widget's view. Bug: 190818220 Test: manual Change-Id: I6552e583ebb0e4810077d4e70fe9ecb07fd5d01a --- .../quickstep/views/FloatingWidgetView.java | 2 + .../widget/LauncherAppWidgetHostView.java | 45 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java index 22ce9421f7..88b11a0884 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java @@ -152,6 +152,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius, boolean appTargetIsTranslucent, int fallbackBackgroundColor) { mAppWidgetView = originalView; + // Deferrals must begin before GhostView is created. See b/190818220 mAppWidgetView.beginDeferringUpdates(); mBackgroundPosition = widgetBackgroundPosition; mAppTargetIsTranslucent = appTargetIsTranslucent; @@ -240,6 +241,7 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, ((ViewGroup) dragLayer.getParent()).removeView(this); dragLayer.removeView(mListenerView); mBackgroundView.finish(); + // Removing GhostView must occur before ending deferrals. See b/190818220 mAppWidgetView.endDeferringUpdates(); recycle(); mLauncher.getViewCache().recycleView(R.layout.floating_widget_view, this); diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index 70ed02f006..ea08a252e6 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -57,6 +57,7 @@ import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener; import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; import java.util.List; +import java.util.Optional; /** * {@inheritDoc} @@ -116,7 +117,8 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView private final Object mUpdateLock = new Object(); private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper; private long mDeferUpdatesUntilMillis = 0; - private RemoteViews mMostRecentRemoteViews; + private RemoteViews mDeferredRemoteViews; + private Optional mDeferredColorChange = Optional.empty(); public LauncherAppWidgetHostView(Context context) { super(context); @@ -173,8 +175,11 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView @Override public void updateAppWidget(RemoteViews remoteViews) { synchronized (mUpdateLock) { - mMostRecentRemoteViews = remoteViews; - if (SystemClock.uptimeMillis() < mDeferUpdatesUntilMillis) return; + if (isDeferringUpdates()) { + mDeferredRemoteViews = remoteViews; + return; + } + mDeferredRemoteViews = null; } super.updateAppWidget(remoteViews); @@ -210,11 +215,20 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView return false; } + /** + * Returns true if the application of {@link RemoteViews} through {@link #updateAppWidget} and + * colors through {@link #onColorsChanged} are currently being deferred. + * @see #beginDeferringUpdates() + */ + private boolean isDeferringUpdates() { + return SystemClock.uptimeMillis() < mDeferUpdatesUntilMillis; + } + /** * Begin deferring the application of any {@link RemoteViews} updates made through - * {@link #updateAppWidget(RemoteViews)} until {@link #endDeferringUpdates()} has been called or - * the next {@link #updateAppWidget(RemoteViews)} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} - * have elapsed. + * {@link #updateAppWidget} and color changes through {@link #onColorsChanged} until + * {@link #endDeferringUpdates()} has been called or the next {@link #updateAppWidget} or + * {@link #onColorsChanged} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} have elapsed. */ public void beginDeferringUpdates() { synchronized (mUpdateLock) { @@ -224,18 +238,23 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView /** * Stop deferring the application of {@link RemoteViews} updates made through - * {@link #updateAppWidget(RemoteViews)} and apply the most recently received update. + * {@link #updateAppWidget} and color changes made through {@link #onColorsChanged} and apply + * any deferred updates. */ public void endDeferringUpdates() { RemoteViews remoteViews; + Optional deferredColors; synchronized (mUpdateLock) { mDeferUpdatesUntilMillis = 0; - remoteViews = mMostRecentRemoteViews; - mMostRecentRemoteViews = null; + remoteViews = mDeferredRemoteViews; + mDeferredRemoteViews = null; + deferredColors = mDeferredColorChange; + mDeferredColorChange = Optional.empty(); } if (remoteViews != null) { updateAppWidget(remoteViews); } + deferredColors.ifPresent(colors -> onColorsChanged(null /* rectF */, colors)); } public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -388,6 +407,14 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView @Override public void onColorsChanged(RectF rectF, SparseIntArray colors) { + synchronized (mUpdateLock) { + if (isDeferringUpdates()) { + mDeferredColorChange = Optional.ofNullable(colors); + return; + } + mDeferredColorChange = Optional.empty(); + } + // setColorResources will reapply the view, which must happen in the UI thread. post(() -> setColorResources(colors)); }