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
This commit is contained in:
parent
67138875fa
commit
387279d938
|
@ -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) { }
|
||||
}
|
||||
|
|
|
@ -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,12 +197,16 @@ public abstract class RecentsUiFactory {
|
|||
return new RecentsViewStateController(launcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the swipe shared state for the current swipe gesture.
|
||||
*/
|
||||
public static void clearSwipeSharedState(boolean 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.<RecentsView>getOverviewPanel().switchToScreenshot(
|
||||
() -> TouchInteractionService.getSwipeSharedState().clearAllState(
|
||||
finishAnimation));
|
||||
} else {
|
||||
TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<T extends BaseDraggingActivity>
|
||||
extends BaseSwipeUpHandler<T, RecentsView>
|
||||
implements OnApplyWindowInsetsListener {
|
||||
extends BaseSwipeUpHandler<T, RecentsView> 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<T extends BaseDraggingActivity>
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return mCanceled;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void resumeLastTask() {
|
||||
mRecentsAnimationWrapper.finish(false /* toRecents */, null);
|
||||
|
@ -1099,14 +1100,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
}
|
||||
|
||||
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<T extends BaseDraggingActivity>
|
|||
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) {
|
||||
|
|
|
@ -78,8 +78,13 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
|
|||
|
||||
@Override
|
||||
public void startHome() {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */,
|
||||
() -> mActivity.getStateManager().goToState(NORMAL)));
|
||||
} else {
|
||||
mActivity.getStateManager().goToState(NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranslationY(float translationY) {
|
||||
|
|
|
@ -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<T extends BaseActivity> 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<T extends BaseActivity> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,16 +134,35 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates this thumbnail.
|
||||
* Updates the thumbnail.
|
||||
* @param refreshNow whether the {@code thumbnailData} will be used to redraw immediately.
|
||||
* In most cases, we use the {@link #setThumbnail(Task, ThumbnailData)}
|
||||
* version with {@code refreshNow} is true. The only exception is
|
||||
* in the live tile case that we grab a screenshot when user enters Overview
|
||||
* upon swipe up so that a usable screenshot is accessible immediately when
|
||||
* recents animation needs to be finished / cancelled.
|
||||
*/
|
||||
public void setThumbnail(Task task, ThumbnailData thumbnailData) {
|
||||
public void setThumbnail(Task task, ThumbnailData thumbnailData, boolean refreshNow) {
|
||||
mTask = task;
|
||||
if (thumbnailData != null && thumbnailData.thumbnail != null) {
|
||||
Bitmap bm = thumbnailData.thumbnail;
|
||||
mThumbnailData =
|
||||
(thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
|
||||
if (refreshNow) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/** See {@link #setThumbnail(Task, ThumbnailData, boolean)} */
|
||||
public void setThumbnail(Task task, ThumbnailData thumbnailData) {
|
||||
setThumbnail(task, thumbnailData, true /* refreshNow */);
|
||||
}
|
||||
|
||||
/** Updates the shader, paint, matrix to redraw. */
|
||||
public void refresh() {
|
||||
if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
|
||||
Bitmap bm = mThumbnailData.thumbnail;
|
||||
bm.prepareToDraw();
|
||||
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
mPaint.setShader(mBitmapShader);
|
||||
mThumbnailData = thumbnailData;
|
||||
updateThumbnailMatrix();
|
||||
} else {
|
||||
mBitmapShader = null;
|
||||
|
|
|
@ -53,7 +53,6 @@ import com.android.launcher3.Utilities;
|
|||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
|
|
|
@ -1893,7 +1893,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
|||
// recents animation into launcher. Defer launching the activity until Launcher is
|
||||
// next resumed.
|
||||
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
|
||||
UiFactory.clearSwipeSharedState(true /* finishAnimation */);
|
||||
UiFactory.clearSwipeSharedState(this, true /* finishAnimation */);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue