From 387279d9385b520e20ba7c6e10efb6c64de130a0 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Wed, 14 Aug 2019 15:01:53 -0700 Subject: [PATCH] Live tile should switch to screenshot before finishing recents animation Fixes: 139439373 Test: Launch a new app from shortcuts in Overview mode. Make sure the live tile stays put (no hole). Same with touching empty space in Overview to go home. Change-Id: I6cacf2842e21f9856d0021cea9fddf4f870f09f0 --- .../uioverrides/RecentsUiFactory.java | 2 +- .../uioverrides/RecentsUiFactory.java | 15 ++-- .../src/com/android/quickstep/ViewUtils.java | 74 +++++++++++++++++++ .../WindowTransformSwipeHandler.java | 48 ++++-------- .../quickstep/views/LauncherRecentsView.java | 7 +- .../android/quickstep/views/RecentsView.java | 29 +++++++- .../quickstep/views/TaskThumbnailView.java | 29 ++++++-- .../com/android/quickstep/views/TaskView.java | 1 - src/com/android/launcher3/Launcher.java | 2 +- .../launcher3/uioverrides/UiFactory.java | 3 +- 10 files changed, 160 insertions(+), 50 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index ae8fd82241..f2aa842d12 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -89,5 +89,5 @@ public abstract class RecentsUiFactory { return RotationMode.NORMAL; } - public static void clearSwipeSharedState(boolean finishAnimation) {} + public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 46e883aca3..2f8af44663 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -18,6 +18,7 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import android.content.Context; @@ -196,11 +197,15 @@ public abstract class RecentsUiFactory { return new RecentsViewStateController(launcher); } - /** - * Clears the swipe shared state for the current swipe gesture. - */ - public static void clearSwipeSharedState(boolean finishAnimation) { - TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation); + /** Clears the swipe shared state for the current swipe gesture. */ + public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + launcher.getOverviewPanel().switchToScreenshot( + () -> TouchInteractionService.getSwipeSharedState().clearAllState( + finishAnimation)); + } else { + TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation); + } } /** diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java new file mode 100644 index 0000000000..cbb6ad4188 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 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.quickstep; + +import android.graphics.Canvas; +import android.view.View; + +import com.android.systemui.shared.system.WindowCallbacksCompat; + +import java.util.function.BooleanSupplier; + +/** + * Utility class for helpful methods related to {@link View} objects. + */ +public class ViewUtils { + + /** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */ + public static boolean postDraw(View view, Runnable onFinishRunnable) { + return postDraw(view, onFinishRunnable, () -> false); + } + + /** + * Inject some addition logic in order to make sure that the view is updated smoothly post + * draw, and allow addition task to be run after view update. + * + * @param onFinishRunnable runnable to be run right after the view finishes drawing. + */ + public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) { + // Defer finishing the animation until the next launcher frame with the + // new thumbnail + return new WindowCallbacksCompat(view) { + // The number of frames to defer until we actually finish the animation + private int mDeferFrameCount = 2; + + @Override + public void onPostDraw(Canvas canvas) { + // If we were cancelled after this was attached, do not update + // the state. + if (canceled.getAsBoolean()) { + detach(); + return; + } + + if (mDeferFrameCount > 0) { + mDeferFrameCount--; + // Workaround, detach and reattach to invalidate the root node for + // another draw + detach(); + attach(); + view.invalidate(); + return; + } + + if (onFinishRunnable != null) { + onFinishRunnable.run(); + } + detach(); + } + }.attach(); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index db8eb27533..c03457948c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -45,7 +45,6 @@ import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.Intent; -import android.graphics.Canvas; import android.graphics.PointF; import android.graphics.RectF; import android.os.Build; @@ -90,12 +89,10 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.WindowCallbacksCompat; @TargetApi(Build.VERSION_CODES.O) public class WindowTransformSwipeHandler - extends BaseSwipeUpHandler - implements OnApplyWindowInsetsListener { + extends BaseSwipeUpHandler implements OnApplyWindowInsetsListener { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null; @@ -1007,6 +1004,10 @@ public class WindowTransformSwipeHandler } } + public boolean isCanceled() { + return mCanceled; + } + @UiThread private void resumeLastTask() { mRecentsAnimationWrapper.finish(false /* toRecents */, null); @@ -1099,14 +1100,21 @@ public class WindowTransformSwipeHandler } private void switchToScreenshot() { + SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController(); if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + if (controller != null) { + // Update the screenshot of the task + if (mTaskSnapshot == null) { + mTaskSnapshot = controller.screenshotTask(mRunningTaskId); + } + mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot, false /* refreshNow */); + } setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); } else if (!mRecentsAnimationWrapper.hasTargets()) { // If there are no targets, then we don't need to capture anything setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); } else { boolean finishTransitionPosted = false; - SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController(); if (controller != null) { // Update the screenshot of the task if (mTaskSnapshot == null) { @@ -1123,34 +1131,8 @@ public class WindowTransformSwipeHandler if (taskView != null && !mCanceled) { // Defer finishing the animation until the next launcher frame with the // new thumbnail - finishTransitionPosted = new WindowCallbacksCompat(taskView) { - - // The number of frames to defer until we actually finish the animation - private int mDeferFrameCount = 2; - - @Override - public void onPostDraw(Canvas canvas) { - // If we were cancelled after this was attached, do not update - // the state. - if (mCanceled) { - detach(); - return; - } - - if (mDeferFrameCount > 0) { - mDeferFrameCount--; - // Workaround, detach and reattach to invalidate the root node for - // another draw - detach(); - attach(); - taskView.invalidate(); - return; - } - - setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); - detach(); - } - }.attach(); + finishTransitionPosted = ViewUtils.postDraw(taskView, + () -> setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), this::isCanceled); } } if (!finishTransitionPosted) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 0f9fc17aa6..4c308fb8c0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -78,7 +78,12 @@ public class LauncherRecentsView extends RecentsView implements StateL @Override public void startHome() { - mActivity.getStateManager().goToState(NORMAL); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */, + () -> mActivity.getStateManager().goToState(NORMAL))); + } else { + mActivity.getStateManager().goToState(NORMAL); + } } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 554f4372c3..29e93aa8d3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -107,6 +107,7 @@ import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; +import com.android.quickstep.ViewUtils; import com.android.quickstep.util.ClipAnimationHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -380,14 +381,23 @@ public abstract class RecentsView extends PagedView impl return null; } - public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { + /** + * Update the thumbnail of the task. + * @param refreshNow Refresh immediately if it's true. + */ + public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) { TaskView taskView = getTaskView(taskId); if (taskView != null) { - taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData); + taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow); } return taskView; } + /** See {@link #updateThumbnail(int, ThumbnailData, boolean)} */ + public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { + return updateThumbnail(taskId, thumbnailData, true /* refreshNow */); + } + @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); @@ -1819,4 +1829,19 @@ public abstract class RecentsView extends PagedView impl final WindowInsets insets = getRootWindowInsets(); return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight()); } + + /** If it's in the live tile mode, switch the running task into screenshot mode. */ + public void switchToScreenshot(Runnable onFinishRunnable) { + TaskView taskView = getRunningTaskView(); + if (taskView == null) { + if (onFinishRunnable != null) { + onFinishRunnable.run(); + } + return; + } + + taskView.setShowScreenshot(true); + taskView.getThumbnail().refresh(); + ViewUtils.postDraw(taskView, onFinishRunnable); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 5799c0164f..adeb97425e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -134,16 +134,35 @@ public class TaskThumbnailView extends View implements PluginListener startActivitySafely(v, intent, item, sourceContainer)); - UiFactory.clearSwipeSharedState(true /* finishAnimation */); + UiFactory.clearSwipeSharedState(this, true /* finishAnimation */); return true; } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 467ae02d5c..6d9ed88e08 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -96,7 +96,8 @@ public class UiFactory { public static void resetPendingActivityResults(Launcher launcher, int requestCode) { } - public static void clearSwipeSharedState(boolean finishAnimation) {} + /** No-op. */ + public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { } public static Person[] getPersons(ShortcutInfo si) { return Utilities.EMPTY_PERSON_ARRAY;