From a7843583be7603e1656a5d413a8b5d70468d952f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 22 Feb 2018 15:05:41 -0800 Subject: [PATCH] Fix flash when switching from app window to snapshot. - If there is any delay in launcher draw, then we will finish the transition and hide the app window before launcher has had a chance to draw the task view with the updated snapshot. Change-Id: I81c5b467f32520fd7474bde0e89819336292e84f --- .../com/android/quickstep/RecentsView.java | 5 +- .../WindowTransformSwipeHandler.java | 58 ++++++++++++++----- .../launcher3/util/ViewOnDrawExecutor.java | 27 +++++++-- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index c400ffd176..c8122d21ee 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -132,15 +132,16 @@ public class RecentsView extends PagedView implements Insettable { mScrollState.isRtl = mIsRtl; } - public void updateThumbnail(int taskId, ThumbnailData thumbnailData) { + public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { for (int i = mFirstTaskIndex; i < getChildCount(); i++) { final TaskView taskView = (TaskView) getChildAt(i); if (taskView.getTask().key.id == taskId) { taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData); taskView.setAlpha(1); - return; + return taskView; } } + return null; } private void setupLayoutTransition() { diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index a6192c2f50..012afb26cb 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -46,7 +46,9 @@ import android.support.annotation.UiThread; import android.support.annotation.WorkerThread; import android.util.Log; import android.view.View; +import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnDrawListener; +import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; @@ -67,6 +69,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TraceHelper; +import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.quickstep.TouchConsumer.InteractionType; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.utilities.RectFEvaluator; @@ -214,22 +217,24 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED, this::notifyGestureStarted); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED - | STATE_GESTURE_CANCELLED, this::resetStateForAnimationCancel); + | STATE_GESTURE_CANCELLED, + this::resetStateForAnimationCancel); - mStateCallback.addCallback(STATE_SCALED_CONTROLLER_APP | STATE_APP_CONTROLLER_RECEIVED, + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED + | STATE_SCALED_CONTROLLER_APP, this::resumeLastTask); - mStateCallback.addCallback(STATE_SCALED_CONTROLLER_RECENTS - | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_APP_CONTROLLER_RECEIVED, + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED + | STATE_ACTIVITY_MULTIPLIER_COMPLETE + | STATE_SCALED_CONTROLLER_RECENTS, this::switchToScreenshot); - - mStateCallback.addCallback(STATE_SCALED_CONTROLLER_RECENTS - | STATE_ACTIVITY_MULTIPLIER_COMPLETE, + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED + | STATE_ACTIVITY_MULTIPLIER_COMPLETE + | STATE_SCALED_CONTROLLER_RECENTS + | STATE_SWITCH_TO_SCREENSHOT_COMPLETE, this::setupLauncherUiAfterSwipeUpAnimation); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP, this::reset); - mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_RECENTS, - this::reset); mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, @@ -718,22 +723,47 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } private void switchToScreenshot() { + boolean finishTransitionPosted = false; + final Runnable finishTransitionRunnable = () -> { + synchronized (mRecentsAnimationWrapper) { + mRecentsAnimationWrapper.finish(true /* toHome */, + () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE)); + } + }; synchronized (mRecentsAnimationWrapper) { if (mRecentsAnimationWrapper.controller != null) { TransactionCompat transaction = new TransactionCompat(); for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) { if (app.mode == MODE_CLOSING) { // Update the screenshot of the task - final ThumbnailData thumbnail = + ThumbnailData thumbnail = mRecentsAnimationWrapper.controller.screenshotTask(app.taskId); - mRecentsView.updateThumbnail(app.taskId, thumbnail); + TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail); + if (taskView != null) { + // Defer finishing the animation until the next launcher frame with the + // new thumbnail + ViewOnDrawExecutor executor = new ViewOnDrawExecutor(mMainExecutor) { + @Override + public void onViewDetachedFromWindow(View v) { + if (!isCompleted()) { + runAllTasks(); + } + } + }; + executor.attachTo(mLauncher, taskView, + false /* waitForLoadAnimation */); + executor.execute(finishTransitionRunnable); + finishTransitionPosted = true; + } } } transaction.apply(); } } - mRecentsAnimationWrapper.finish(true /* toHome */, - () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE)); + if (!finishTransitionPosted) { + // If we haven't posted the transition end runnable, run it now + finishTransitionRunnable.run(); + } doLogGesture(true /* toLauncher */); } @@ -743,6 +773,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // Animate the first icon. mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */); + + reset(); } public void onQuickScrubEnd() { diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java index e5c1dd1b43..34bdcc9377 100644 --- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java +++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java @@ -49,9 +49,16 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, } public void attachTo(Launcher launcher) { + attachTo(launcher, launcher.getWorkspace(), true /* waitForLoadAnimation */); + } + + public void attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation) { mLauncher = launcher; - mAttachedView = launcher.getWorkspace(); + mAttachedView = attachedView; mAttachedView.addOnAttachStateChangeListener(this); + if (!waitForLoadAnimation) { + mLoadAnimationCompleted = true; + } attachObserver(); } @@ -74,7 +81,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, } @Override - public void onViewDetachedFromWindow(View v) { } + public void onViewDetachedFromWindow(View v) {} @Override public void onDraw() { @@ -101,10 +108,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called. if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) { mIsExecuting = true; - for (final Runnable r : mTasks) { - mExecutor.execute(r); - } - markCompleted(); + runAllTasks(); } } @@ -121,4 +125,15 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, } LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT); } + + protected boolean isCompleted() { + return mCompleted; + } + + protected void runAllTasks() { + for (final Runnable r : mTasks) { + mExecutor.execute(r); + } + markCompleted(); + } }