From 60876ac0d77cbf3abda9eb609f6cd49ea3081cfa Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 20 Feb 2018 14:21:20 -0800 Subject: [PATCH] Handling recents animation cancelation When the animation is cancelled, if remove the pending handler (incase it has not executed yet) and revert back to the old state if it has executed. Change-Id: Iddc305aaaeae78c06cbf6c1c271254409cc1487d --- .../quickstep/OtherActivityTouchConsumer.java | 11 +- .../com/android/quickstep/RecentsView.java | 31 ++++- .../src/com/android/quickstep/TaskView.java | 27 +---- .../WindowTransformSwipeHandler.java | 110 +++++++++++------- src/com/android/launcher3/Launcher.java | 17 +++ .../states/InternalStateHandler.java | 80 ++++++++----- 6 files changed, 170 insertions(+), 106 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 19e1cca5e9..604b60b90d 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; + import static com.android.quickstep.RemoteRunnable.executeSafely; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; @@ -211,7 +212,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC notifyGestureStarted(); } - } else { + } else if (mInteractionHandler != null) { // Move mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); } @@ -322,7 +323,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC switchToMainChoreographer(); } }); - handler.initWhenReady(mMainThreadExecutor); + handler.initWhenReady(); Runnable startActivity = () -> ActivityManagerWrapper.getInstance() .startRecentsActivity(mHomeIntent, @@ -338,7 +339,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) { if (mInteractionHandler == handler) { - handler.setRecentsAnimation(controller, apps, homeContentInsets, + handler.onRecentsAnimationStart(controller, apps, homeContentInsets, minimizedHomeBounds); } else { controller.finish(false /* toHome */); @@ -357,7 +358,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC public void onAnimationCanceled() { if (mInteractionHandler == handler) { - handler.setRecentsAnimation(null, null, null, null); + handler.onRecentsAnimationCanceled(); } } }, null, null); @@ -381,7 +382,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC * the animation can still be running. */ private void finishTouchTracking() { - if (mTouchThresholdCrossed) { + if (mTouchThresholdCrossed && mInteractionHandler != null) { mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.get(this).getScaledMaximumFlingVelocity()); diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index cb510c8f9f..5d01009b42 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -16,6 +16,10 @@ package com.android.quickstep; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.quickstep.TaskView.CURVE_FACTOR; +import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR; + import android.animation.LayoutTransition; import android.content.Context; import android.graphics.Bitmap; @@ -54,10 +58,6 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.util.ArrayList; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.quickstep.TaskView.CURVE_FACTOR; -import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR; - /** * A list of recent tasks. */ @@ -108,6 +108,8 @@ public class RecentsView extends PagedView implements Insettable { private Matrix mFadeMatrix; private boolean mScrimOnLeft; + private boolean mFirstTaskIconScaledDown = false; + public RecentsView(Context context) { this(context, null); } @@ -298,6 +300,7 @@ public class RecentsView extends PagedView implements Insettable { taskView.setAlpha(1f); loader.loadTaskData(task); } + applyIconScale(false /* animate */); if (oldChildCount != getChildCount()) { mQuickScrubController.snapToPageForCurrentQuickScrubSection(); @@ -486,6 +489,26 @@ public class RecentsView extends PagedView implements Insettable { return mQuickScrubController; } + public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) { + if (mFirstTaskIconScaledDown == isScaledDown) { + return; + } + mFirstTaskIconScaledDown = isScaledDown; + applyIconScale(animate); + } + + private void applyIconScale(boolean animate) { + float scale = mFirstTaskIconScaledDown ? 0 : 1; + TaskView firstTask = (TaskView) getChildAt(mFirstTaskIndex); + if (firstTask != null) { + if (animate) { + firstTask.animateIconToScale(scale); + } else { + firstTask.setIconScale(scale); + } + } + } + @Override public void draw(Canvas canvas) { if (mScrim == null) { diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java index 8865a42781..f21742aa59 100644 --- a/quickstep/src/com/android/quickstep/TaskView.java +++ b/quickstep/src/com/android/quickstep/TaskView.java @@ -19,7 +19,6 @@ package com.android.quickstep; import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK; import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.app.ActivityOptions; import android.content.Context; @@ -28,7 +27,6 @@ import android.graphics.Outline; import android.graphics.Rect; import android.os.Handler; import android.util.AttributeSet; -import android.util.Property; import android.view.View; import android.view.ViewOutlineProvider; import android.widget.FrameLayout; @@ -68,23 +66,9 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback private static final long SCALE_ICON_DURATION = 120; - private static final Property SCALE_ICON_PROPERTY = - new Property(Float.TYPE, "scale_icon") { - @Override - public Float get(TaskView taskView) { - return taskView.mIconScale; - } - - @Override - public void set(TaskView taskView, Float iconScale) { - taskView.setIconScale(iconScale); - } - }; - private Task mTask; private TaskThumbnailView mSnapshotView; private ImageView mIconView; - private float mIconScale = 1f; public TaskView(Context context) { this(context, null); @@ -185,16 +169,13 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback } public void animateIconToScale(float scale) { - ObjectAnimator.ofFloat(this, SCALE_ICON_PROPERTY, scale) - .setDuration(SCALE_ICON_DURATION).start(); + mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start(); } protected void setIconScale(float iconScale) { - mIconScale = iconScale; - if (mIconView != null) { - mIconView.setScaleX(mIconScale); - mIconView.setScaleY(mIconScale); - } + mIconView.animate().cancel(); + mIconView.setScaleX(iconScale); + mIconView.setScaleY(iconScale); } @Override diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index ddddbf67e0..d8b87096d8 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -77,32 +77,36 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // Launcher UI related states private static final int STATE_LAUNCHER_PRESENT = 1 << 0; - private static final int STATE_LAUNCHER_DRAWN = 1 << 1; - private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 2; + private static final int STATE_LAUNCHER_STARTED = 1 << 1; + private static final int STATE_LAUNCHER_DRAWN = 1 << 2; + private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3; // Internal initialization states - private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 3; + private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4; // Interaction finish states - private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 4; - private static final int STATE_SCALED_CONTROLLER_APP = 1 << 5; + private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5; + private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6; - private static final int STATE_HANDLER_INVALIDATED = 1 << 6; - private static final int STATE_GESTURE_STARTED = 1 << 7; + private static final int STATE_HANDLER_INVALIDATED = 1 << 7; + private static final int STATE_GESTURE_STARTED = 1 << 8; + private static final int STATE_GESTURE_CANCELLED = 1 << 9; // States for quick switch/scrub - private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 8; - private static final int STATE_QUICK_SWITCH = 1 << 9; - private static final int STATE_QUICK_SCRUB_START = 1 << 10; - private static final int STATE_QUICK_SCRUB_END = 1 << 11; + private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10; + private static final int STATE_QUICK_SWITCH = 1 << 11; + private static final int STATE_QUICK_SCRUB_START = 1 << 12; + private static final int STATE_QUICK_SCRUB_END = 1 << 13; private static final int LAUNCHER_UI_STATES = - STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE; + STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE + | STATE_LAUNCHER_STARTED; // For debugging, keep in sync with above states private static final String[] STATES = new String[] { "STATE_LAUNCHER_PRESENT", + "STATE_LAUNCHER_STARTED", "STATE_LAUNCHER_DRAWN", "STATE_ACTIVITY_MULTIPLIER_COMPLETE", "STATE_APP_CONTROLLER_RECEIVED", @@ -110,6 +114,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { "STATE_SCALED_CONTROLLER_APP", "STATE_HANDLER_INVALIDATED", "STATE_GESTURE_STARTED", + "STATE_GESTURE_CANCELLED", "STATE_SWITCH_TO_SCREENSHOT_COMPLETE", "STATE_QUICK_SWITCH", "STATE_QUICK_SCRUB_START", @@ -201,6 +206,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { this::launcherFrameDrawn); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED, this::notifyGestureStarted); + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED + | STATE_GESTURE_CANCELLED, this::resetStateForAnimationCancel); mStateCallback.addCallback(STATE_SCALED_CONTROLLER_APP | STATE_APP_CONTROLLER_RECEIVED, this::resumeLastTask); @@ -303,37 +310,53 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // For the duration of the gesture, lock the screen orientation to ensure that we do not // rotate mid-quickscrub mLauncher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + mRecentsView = mLauncher.getOverviewPanel(); + mQuickScrubController = mRecentsView.getQuickScrubController(); + mLauncherLayoutListener = new LauncherLayoutListener(mLauncher); + mStateCallback.setState(STATE_LAUNCHER_PRESENT); + if (alreadyOnHome) { + onLauncherStart(launcher); + } else { + launcher.setOnStartCallback(this::onLauncherStart); + } + return true; + } + + private void onLauncherStart(final Launcher launcher) { + if (mLauncher != launcher) { + return; + } + if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) { + return; + } + + mStateCallback.setState(STATE_LAUNCHER_STARTED); LauncherState startState = mLauncher.getStateManager().getState(); if (startState.disableRestore) { startState = mLauncher.getStateManager().getRestState(); } mLauncher.getStateManager().setRestState(startState); - AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome); + AbstractFloatingView.closeAllOpenViews(mLauncher, mWasLauncherAlreadyVisible); - mRecentsView = mLauncher.getOverviewPanel(); - mQuickScrubController = mRecentsView.getQuickScrubController(); - mLauncherLayoutListener = new LauncherLayoutListener(mLauncher); - final int state; if (mWasLauncherAlreadyVisible) { DeviceProfile dp = mLauncher.getDeviceProfile(); long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); - mLauncherTransitionController = launcher.getStateManager() + mLauncherTransitionController = mLauncher.getStateManager() .createAnimationToNewWorkspace(OVERVIEW, accuracy); mLauncherTransitionController.dispatchOnStart(); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); - state = STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN - | STATE_LAUNCHER_PRESENT; + mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN); } else { TraceHelper.beginSection("WTS-init"); - launcher.getStateManager().goToState(OVERVIEW, false); + mLauncher.getStateManager().goToState(OVERVIEW, false); TraceHelper.partitionSection("WTS-init", "State changed"); // TODO: Implement a better animation for fading in - View rootView = launcher.getRootView(); + View rootView = mLauncher.getRootView(); rootView.setAlpha(0); rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { @@ -349,17 +372,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mStateCallback.setState(STATE_LAUNCHER_DRAWN); } }); - state = STATE_LAUNCHER_PRESENT; // Optimization, hide the all apps view to prevent layout while initializing mLauncher.getAppsView().setVisibility(View.GONE); } mRecentsView.showTask(mRunningTaskId); + mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */); mLauncherLayoutListener.open(); - - mStateCallback.setState(state); - return true; } public void setLauncherOnDrawCallback(Runnable callback) { @@ -512,7 +532,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } } - public void setRecentsAnimation(RecentsAnimationControllerCompat controller, + public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) { if (apps != null) { // Use the top closing app to determine the insets for the animation @@ -553,11 +573,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); } - public void onGestureStarted() { - if (mLauncher != null) { - notifyGestureStarted(); - } + public void onRecentsAnimationCanceled() { + mRecentsAnimationWrapper.setController(null, null); + clearReference(); + setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); + } + public void onGestureStarted() { + notifyGestureStarted(); setStateOnUiThread(STATE_GESTURE_STARTED); mGestureStarted = true; mRecentsAnimationWrapper.enableInputConsumer(); @@ -568,15 +591,10 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { * on both background and UI threads */ private void notifyGestureStarted() { - mLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible); - - mMainExecutor.execute(() -> { - // Prepare to animate the first icon. - View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage()); - if (currentRecentsPage instanceof TaskView) { - ((TaskView) currentRecentsPage).setIconScale(0f); - } - }); + final Launcher curLauncher = mLauncher; + if (curLauncher != null) { + curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible); + } } @WorkerThread @@ -650,6 +668,13 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // Restore the requested orientation to the user preference after the gesture has ended mLauncher.updateRequestedOrientation(); + mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */); + } + + private void resetStateForAnimationCancel() { + LauncherState startState = mLauncher.getStateManager().getRestState(); + boolean animate = mWasLauncherAlreadyVisible || mGestureStarted; + mLauncher.getStateManager().goToState(startState, animate); } public void layoutListenerClosed() { @@ -682,10 +707,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mLauncher.getStateManager().reapplyState(); // Animate the first icon. - View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage()); - if (currentRecentsPage instanceof TaskView) { - ((TaskView) currentRecentsPage).animateIconToScale(1f); - } + mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */); } public void onQuickScrubEnd() { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b4093b7d1d..2275110e24 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -239,6 +239,7 @@ public class Launcher extends BaseActivity @Thunk boolean mWorkspaceLoading = true; + private OnStartCallback mOnStartCallback; private OnResumeCallback mOnResumeCallback; private ViewOnDrawExecutor mPendingExecutor; @@ -794,6 +795,10 @@ public class Launcher extends BaseActivity super.onStart(); FirstFrameAnimatorHelper.setIsVisible(true); + if (mOnStartCallback != null) { + mOnStartCallback.onLauncherStart(this); + mOnStartCallback = null; + } if (mLauncherCallbacks != null) { mLauncherCallbacks.onStart(); } @@ -2175,6 +2180,10 @@ public class Launcher extends BaseActivity mOnResumeCallback = callback; } + public void setOnStartCallback(OnStartCallback callback) { + mOnStartCallback = callback; + } + /** * Implementation of the method from LauncherModel.Callbacks. */ @@ -2880,4 +2889,12 @@ public class Launcher extends BaseActivity void onLauncherResume(); } + + /** + * Callback for listening for onStart + */ + public interface OnStartCallback { + + void onLauncherStart(Launcher launcher); + } } diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java index 7298383371..d3c0fef4e6 100644 --- a/src/com/android/launcher3/states/InternalStateHandler.java +++ b/src/com/android/launcher3/states/InternalStateHandler.java @@ -24,7 +24,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.MainThreadExecutor; -import com.android.launcher3.util.Preconditions; import java.lang.ref.WeakReference; @@ -38,7 +37,7 @@ public abstract class InternalStateHandler extends Binder { public static final String EXTRA_STATE_HANDLER = "launcher.state_handler"; - private static WeakReference sPendingHandler = new WeakReference<>(null); + private static final Scheduler sScheduler = new Scheduler(); /** * Initializes the handler when the launcher is ready. @@ -53,30 +52,12 @@ public abstract class InternalStateHandler extends Binder { return intent; } - public final void initWhenReady(MainThreadExecutor executor) { - sPendingHandler = new WeakReference<>(this); - executor.execute(this::initIfReadOnUIThread); - } - - private void initIfReadOnUIThread() { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - return; - } - Callbacks cb = app.getModel().getCallback(); - if (!(cb instanceof Launcher)) { - return; - } - Launcher launcher = (Launcher) cb; - if (!init(launcher, launcher.isStarted())) { - sPendingHandler.clear(); - } + public final void initWhenReady() { + sScheduler.schedule(this); } public void clearReference() { - if (sPendingHandler.get() == this) { - sPendingHandler.clear(); - } + sScheduler.clearReference(this); } public static boolean handleCreate(Launcher launcher, Intent intent) { @@ -101,14 +82,53 @@ public abstract class InternalStateHandler extends Binder { } } if (!result && !explicitIntent) { - InternalStateHandler pendingHandler = sPendingHandler.get(); - if (pendingHandler != null) { - if (!pendingHandler.init(launcher, alreadyOnHome)) { - sPendingHandler.clear(); - } - result = true; - } + result = sScheduler.initIfPending(launcher, alreadyOnHome); } return result; } + + private static class Scheduler implements Runnable { + + private WeakReference mPendingHandler = new WeakReference<>(null); + private MainThreadExecutor mMainThreadExecutor; + + public synchronized void schedule(InternalStateHandler handler) { + mPendingHandler = new WeakReference<>(handler); + if (mMainThreadExecutor == null) { + mMainThreadExecutor = new MainThreadExecutor(); + } + mMainThreadExecutor.execute(this); + } + + @Override + public void run() { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app == null) { + return; + } + Callbacks cb = app.getModel().getCallback(); + if (!(cb instanceof Launcher)) { + return; + } + Launcher launcher = (Launcher) cb; + initIfPending(launcher, launcher.isStarted()); + } + + public synchronized boolean initIfPending(Launcher launcher, boolean alreadyOnHome) { + InternalStateHandler pendingHandler = mPendingHandler.get(); + if (pendingHandler != null) { + if (!pendingHandler.init(launcher, alreadyOnHome)) { + mPendingHandler.clear(); + } + return true; + } + return false; + } + + public synchronized void clearReference(InternalStateHandler handler) { + if (mPendingHandler.get() == handler) { + mPendingHandler.clear(); + } + } + } } \ No newline at end of file