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
This commit is contained in:
Sunny Goyal
2018-02-20 14:21:20 -08:00
parent 224f58c4bc
commit 60876ac0d7
6 changed files with 170 additions and 106 deletions
@@ -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());
@@ -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) {
@@ -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<TaskView, Float> SCALE_ICON_PROPERTY =
new Property<TaskView, Float>(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
@@ -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() {
+17
View File
@@ -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);
}
}
@@ -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<InternalStateHandler> 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<InternalStateHandler> 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();
}
}
}
}