diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java index 0551938c1a..aa210b8abd 100644 --- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java +++ b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java @@ -15,6 +15,8 @@ */ package com.android.quickstep; +import android.support.annotation.WorkerThread; + import com.android.launcher3.states.InternalStateHandler; import com.android.quickstep.TouchInteractionService.InteractionType; @@ -28,8 +30,10 @@ public abstract class BaseSwipeInteractionHandler extends InternalStateHandler { public void reset() {} + @WorkerThread public abstract void onGestureStarted(); + @WorkerThread public abstract void onGestureEnded(float endVelocity); public abstract void updateInteractionType(@InteractionType int interactionType); @@ -38,5 +42,6 @@ public abstract class BaseSwipeInteractionHandler extends InternalStateHandler { public abstract void onQuickScrubProgress(float progress); + @WorkerThread public abstract void updateDisplacement(float displacement); } diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java index e3c3a1b893..fae9b66a2c 100644 --- a/quickstep/src/com/android/quickstep/MotionEventQueue.java +++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java @@ -18,6 +18,8 @@ package com.android.quickstep; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_MOVE; +import android.annotation.TargetApi; +import android.os.Build; import android.view.Choreographer; import android.view.MotionEvent; @@ -29,25 +31,64 @@ import java.util.function.Consumer; /** * Helper class for batching input events */ -public class MotionEventQueue implements Runnable { +@TargetApi(Build.VERSION_CODES.O) +public class MotionEventQueue { + + private final EventArray mEmptyArray = new EventArray(); + private final Object mExecutionLock = new Object(); // We use two arrays and swap the current index when one array is being consumed private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()}; private int mCurrentIndex = 0; - private final Choreographer mChoreographer; - private final Consumer mConsumer; + private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer; + private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer; + + private final Choreographer mMainChoreographer; + + private Consumer mConsumer; + + private Choreographer mInterimChoreographer; + private Choreographer mCurrentChoreographer; + + private Runnable mCurrentRunnable; public MotionEventQueue(Choreographer choreographer, Consumer consumer) { - mChoreographer = choreographer; + mMainChoreographer = choreographer; mConsumer = consumer; + + mCurrentChoreographer = mMainChoreographer; + mCurrentRunnable = mMainFrameCallback; + } + + public void setConsumer(Consumer consumer) { + synchronized (mExecutionLock) { + mConsumer = consumer; + } + } + + public void setInterimChoreographer(Choreographer choreographer) { + synchronized (mExecutionLock) { + synchronized (mArrays) { + mInterimChoreographer = choreographer; + if (choreographer == null) { + mCurrentChoreographer = mMainChoreographer; + mCurrentRunnable = mMainFrameCallback; + } else { + mCurrentChoreographer = mInterimChoreographer; + mCurrentRunnable = mInterimFrameCallback; + } + + ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable); + } + } } public void queue(MotionEvent event) { synchronized (mArrays) { EventArray array = mArrays[mCurrentIndex]; if (array.isEmpty()) { - ChoreographerCompat.postInputFrame(mChoreographer, this); + ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable); } int eventAction = event.getAction(); @@ -61,21 +102,33 @@ public class MotionEventQueue implements Runnable { } } - @Override - public void run() { - EventArray array = swapAndGetCurrentArray(); - int size = array.size(); - for (int i = 0; i < size; i++) { - MotionEvent event = array.get(i); - mConsumer.accept(event); - event.recycle(); - } - array.clear(); - array.lastEventAction = ACTION_CANCEL; + private void frameCallbackForMainChoreographer() { + runFor(mMainChoreographer); } - private EventArray swapAndGetCurrentArray() { + private void frameCallbackForInterimChoreographer() { + runFor(mInterimChoreographer); + } + + private void runFor(Choreographer caller) { + synchronized (mExecutionLock) { + EventArray array = swapAndGetCurrentArray(caller); + int size = array.size(); + for (int i = 0; i < size; i++) { + MotionEvent event = array.get(i); + mConsumer.accept(event); + event.recycle(); + } + array.clear(); + array.lastEventAction = ACTION_CANCEL; + } + } + + private EventArray swapAndGetCurrentArray(Choreographer caller) { synchronized (mArrays) { + if (caller != mCurrentChoreographer) { + return mEmptyArray; + } EventArray current = mArrays[mCurrentIndex]; mCurrentIndex = mCurrentIndex ^ 1; return current; diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java new file mode 100644 index 0000000000..7c983173fe --- /dev/null +++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +/** + * Wrapper around RecentsAnimationController to help with some synchronization + */ +public class RecentsAnimationWrapper { + + public RecentsAnimationControllerCompat controller; + public RemoteAnimationTargetCompat[] targets; + + private boolean mInputConsumerEnabled; + + public synchronized void setController( + RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] targets) { + this.controller = controller; + this.targets = targets; + + if (mInputConsumerEnabled) { + enableInputConsumer(); + } + } + + public void finish(boolean toHome) { + BackgroundExecutor.get().submit(() -> { + synchronized (this) { + if (controller != null) { + controller.setInputConsumerEnabled(false); + controller.finish(toHome); + } + } + }); + } + + public void enableInputConsumer() { + mInputConsumerEnabled = true; + if (mInputConsumerEnabled) { + BackgroundExecutor.get().submit(() -> { + synchronized (this) { + if (controller != null) { + controller.setInputConsumerEnabled(true); + } + } + }); + } + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index c0b12f78bc..a760b75042 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -40,6 +40,8 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.support.annotation.IntDef; import android.util.Log; @@ -92,11 +94,16 @@ public class TouchInteractionService extends Service { public static final int INTERACTION_QUICK_SWITCH = 1; public static final int INTERACTION_QUICK_SCRUB = 2; + /** + * A background thread used for handling UI for another window. + */ + private static HandlerThread sRemoteUiThread; + private final IBinder mMyBinder = new IOverviewProxy.Stub() { @Override public void onMotionEvent(MotionEvent ev) { - mEventQueue.queue(ev); + onBinderMotionEvent(ev); } @Override @@ -166,7 +173,8 @@ public class TouchInteractionService extends Service { private Rect mStableInsets = new Rect(); private ISystemUiProxy mISystemUiProxy; - private Consumer mCurrentConsumer = mNoOpTouchConsumer; + + private Choreographer mBackgroundThreadChoreographer; @Override public void onCreate() { @@ -184,8 +192,10 @@ public class TouchInteractionService extends Service { // Clear the packageName as system can fail to dedupe it b/64108432 mHomeIntent.setComponent(mLauncher).setPackage(null); - mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent); + mEventQueue = new MotionEventQueue(Choreographer.getInstance(), mNoOpTouchConsumer); sConnected = true; + + initBackgroundChoreographer(); } @Override @@ -201,19 +211,23 @@ public class TouchInteractionService extends Service { return mMyBinder; } - private void handleMotionEvent(MotionEvent ev) { + private void onBinderMotionEvent(MotionEvent ev) { if (ev.getActionMasked() == ACTION_DOWN) { mRunningTask = mAM.getRunningTask(); if (mRunningTask == null) { - mCurrentConsumer = mNoOpTouchConsumer; + mEventQueue.setConsumer(mNoOpTouchConsumer); + mEventQueue.setInterimChoreographer(null); } else if (mRunningTask.topActivity.equals(mLauncher)) { - mCurrentConsumer = getLauncherConsumer(); + mEventQueue.setConsumer(getLauncherConsumer()); + mEventQueue.setInterimChoreographer(null); } else { - mCurrentConsumer = mOtherActivityTouchConsumer; + mEventQueue.setConsumer(mOtherActivityTouchConsumer); + mEventQueue.setInterimChoreographer( + isUsingScreenShot() ? null : mBackgroundThreadChoreographer); } } - mCurrentConsumer.accept(ev); + mEventQueue.queue(ev); } private void handleTouchDownOnOtherActivity(MotionEvent ev) { @@ -235,7 +249,8 @@ public class TouchInteractionService extends Service { } mVelocityTracker.addMovement(ev); if (mInteractionHandler != null) { - mInteractionHandler.reset(); + final BaseSwipeInteractionHandler handler = mInteractionHandler; + mMainThreadExecutor.execute(handler::reset); mInteractionHandler = null; } mTouchThresholdCrossed = false; @@ -298,7 +313,7 @@ public class TouchInteractionService extends Service { TraceHelper.endSection("TouchInt"); finishTouchTracking(); - mCurrentConsumer = mNoOpTouchConsumer; + mEventQueue.setConsumer(mNoOpTouchConsumer); break; } } @@ -312,11 +327,15 @@ public class TouchInteractionService extends Service { return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0; } + private boolean isUsingScreenShot() { + return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true); + } + /** * Called when the gesture has started. */ private void startTouchTracking() { - if (Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true)) { + if (isUsingScreenShot()) { // Create the shared handler final NavBarSwipeInteractionHandler handler = new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL); @@ -343,7 +362,6 @@ public class TouchInteractionService extends Service { // Create the shared handler final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(mRunningTask, this); - BackgroundExecutor.get().submit(() -> { ActivityManagerWrapper.getInstance().startRecentsActivity(mHomeIntent, new AssistDataReceiver() { @@ -375,8 +393,17 @@ public class TouchInteractionService extends Service { // Preload the plan mRecentsModel.loadTasks(mRunningTask.id, null); mInteractionHandler = handler; - mInteractionHandler.initWhenReady(); - mInteractionHandler.setGestureEndCallback(() -> mInteractionHandler = null); + handler.setGestureEndCallback(() -> { + if (handler == mInteractionHandler) { + mInteractionHandler = null; + } + }); + handler.setLauncherOnDrawCallback(() -> { + if (handler == mInteractionHandler) { + mEventQueue.setInterimChoreographer(null); + } + }); + mMainThreadExecutor.execute(handler::initWhenReady); } } @@ -468,7 +495,7 @@ public class TouchInteractionService extends Service { case ACTION_POINTER_UP: case ACTION_POINTER_DOWN: if (!mTrackingStarted) { - mCurrentConsumer = mNoOpTouchConsumer; + mEventQueue.setConsumer(mNoOpTouchConsumer); } break; case ACTION_MOVE: { @@ -492,7 +519,7 @@ public class TouchInteractionService extends Service { } if (action == ACTION_UP || action == ACTION_CANCEL) { - mCurrentConsumer = mNoOpTouchConsumer; + mEventQueue.setConsumer(mNoOpTouchConsumer); } } @@ -506,6 +533,15 @@ public class TouchInteractionService extends Service { } } + private void initBackgroundChoreographer() { + if (sRemoteUiThread == null) { + sRemoteUiThread = new HandlerThread("remote-ui"); + sRemoteUiThread.start(); + } + new Handler(sRemoteUiThread.getLooper()).post(() -> + mBackgroundThreadChoreographer = Choreographer.getInstance()); + } + public static boolean isInteractionQuick(@InteractionType int interactionType) { return interactionType == INTERACTION_QUICK_SCRUB || interactionType == INTERACTION_QUICK_SWITCH; diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index e9b22a25e5..16b34f99f7 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -37,28 +37,31 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.os.Build; +import android.os.Looper; import android.os.UserHandle; import android.support.annotation.UiThread; +import android.support.annotation.WorkerThread; import android.view.View; +import android.view.ViewTreeObserver.OnDrawListener; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.states.InternalStateHandler; import com.android.launcher3.util.Preconditions; +import com.android.launcher3.util.TraceHelper; import com.android.quickstep.TouchInteractionService.InteractionType; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.BackgroundExecutor; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -70,17 +73,18 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { // Launcher UI related states private static final int STATE_LAUNCHER_READY = 1 << 0; - private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 1; + private static final int STATE_LAUNCHER_DRAWN = 1 << 1; + private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 2; // Internal initialization states - private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 2; + private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 3; // Interaction finish states - private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 3; - private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 4; + private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 4; + private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 5; private static final int LAUNCHER_UI_STATES = - STATE_LAUNCHER_READY | STATE_ACTIVITY_MULTIPLIER_COMPLETE; + STATE_LAUNCHER_READY | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE; private static final long MAX_SWIPE_DURATION = 200; private static final long MIN_SWIPE_DURATION = 80; @@ -115,6 +119,8 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { private RecentsView mRecentsView; private QuickScrubController mQuickScrubController; + private Runnable mLauncherDrawnCallback; + private boolean mWasLauncherAlreadyVisible; private float mCurrentDisplacement; @@ -122,16 +128,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { private @InteractionType int mInteractionType = INTERACTION_NORMAL; private boolean mStartedQuickScrubFromHome; - private RecentsAnimationControllerCompat mRecentsAnimationController; - private RemoteAnimationTargetCompat[] mRecentsAnimationApps; - private boolean mRecentsAnimationInputConsumerEnabled; + private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper(); private Matrix mTmpMatrix = new Matrix(); private final InputConsumerController mInputConsumerController; private final InputConsumerController.TouchListener mInputConsumerTouchListener = (ev) -> { if (ev.getActionMasked() == ACTION_UP) { - onGestureInterruptEnd(); + // TODO: Handle touch event while the transition is in progress. } return true; }; @@ -144,7 +148,6 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mContext = context; mInputConsumerController = InputConsumerController.getRecentsAnimationInputConsumer(); - WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext); @@ -173,6 +176,15 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { this::reset); mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_RECENTS, this::reset); + + mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_LAUNCHER_DRAWN, + mLauncherDrawnCallback); + } + + public void setLauncherOnDrawCallback(Runnable callback) { + mLauncherDrawnCallback = callback; + mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_LAUNCHER_DRAWN, + mLauncherDrawnCallback); } private void initTransitionEndpoints(DeviceProfile dp) { @@ -220,17 +232,35 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { .createAnimationToNewWorkspace(OVERVIEW, accuracy); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); - mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE); + mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN); } else { + TraceHelper.beginSection("WTS-init"); launcher.getStateManager().goToState(OVERVIEW, false); + TraceHelper.partitionSection("WTS-init", "State changed"); // TODO: Implement a better animation for fading in View rootView = launcher.getRootView(); rootView.setAlpha(0); - rootView.animate().alpha(1) - .setDuration(getFadeInDuration()) - .withEndAction(() -> mStateCallback.setState( - launcher == mLauncher ? STATE_ACTIVITY_MULTIPLIER_COMPLETE : 0)); + rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { + + @Override + public void onDraw() { + TraceHelper.endSection("WTS-init", "Launcher frame is drawn"); + rootView.post(() -> + rootView.getViewTreeObserver().removeOnDrawListener(this)); + if (launcher != mLauncher) { + return; + } + + if ((mStateCallback.getState() & STATE_LAUNCHER_DRAWN) == 0) { + mStateCallback.setState(STATE_LAUNCHER_DRAWN); + rootView.animate().alpha(1) + .setDuration(getFadeInDuration()) + .withEndAction(() -> mStateCallback.setState(launcher == mLauncher + ? STATE_ACTIVITY_MULTIPLIER_COMPLETE : 0)); + } + } + }); } mRecentsView = mLauncher.getOverviewPanel(); @@ -334,48 +364,49 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { updateDisplacement(mCurrentDisplacement); } - @UiThread + @WorkerThread private void updateFinalShift() { if (mStartedQuickScrubFromHome) { return; } float shift = mCurrentShift.value; - mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); - float scale = (float) mCurrentRect.width() / mSourceRect.width(); - if (mRecentsAnimationApps != null) { - mClipRect.left = mSourceRect.left; - mClipRect.top = (int) (mStableInsets.top * shift); - mClipRect.bottom = (int) (mDp.heightPx - (mStableInsets.bottom * shift)); - mClipRect.right = mSourceRect.right; - mTmpMatrix.setScale(scale, scale, 0, 0); - mTmpMatrix.postTranslate(mCurrentRect.left - mStableInsets.left * scale * shift, - mCurrentRect.top - mStableInsets.top * scale * shift); - TransactionCompat transaction = new TransactionCompat(); - for (RemoteAnimationTargetCompat app : mRecentsAnimationApps) { - if (app.mode == MODE_CLOSING) { - transaction.setMatrix(app.leash, mTmpMatrix) - .setWindowCrop(app.leash, mClipRect) - .show(app.leash); + synchronized (mRecentsAnimationWrapper) { + if (mRecentsAnimationWrapper.controller != null) { + mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); + float scale = (float) mCurrentRect.width() / mSourceRect.width(); + + mClipRect.left = mSourceRect.left; + mClipRect.top = (int) (mStableInsets.top * shift); + mClipRect.bottom = (int) (mDp.heightPx - (mStableInsets.bottom * shift)); + mClipRect.right = mSourceRect.right; + + mTmpMatrix.setScale(scale, scale, 0, 0); + mTmpMatrix.postTranslate(mCurrentRect.left - mStableInsets.left * scale * shift, + mCurrentRect.top - mStableInsets.top * scale * shift); + TransactionCompat transaction = new TransactionCompat(); + for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) { + if (app.mode == MODE_CLOSING) { + transaction.setMatrix(app.leash, mTmpMatrix) + .setWindowCrop(app.leash, mClipRect) + .show(app.leash); + } } + transaction.apply(); } - transaction.apply(); } - if (mLauncherTransitionController != null) { - mLauncherTransitionController.setPlayFraction(shift); + if (Looper.getMainLooper() == Looper.myLooper()) { + if (mLauncherTransitionController != null) { + mLauncherTransitionController.setPlayFraction(shift); + } } } public void setRecentsAnimation(RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] apps) { - mRecentsAnimationController = controller; - if (mRecentsAnimationInputConsumerEnabled) { - BackgroundExecutor.get().submit(() -> - mRecentsAnimationController.setInputConsumerEnabled(true)); - } - mRecentsAnimationApps = apps; + mRecentsAnimationWrapper.setController(controller, apps); mStateCallback.setState(STATE_APP_CONTROLLER_RECEIVED); } @@ -384,12 +415,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mInputConsumerController.registerInputConsumer(); mInputConsumerController.setTouchListener(mInputConsumerTouchListener); - if (mRecentsAnimationController != null) { - BackgroundExecutor.get().submit(() -> - mRecentsAnimationController.setInputConsumerEnabled(true)); - } else { - mRecentsAnimationInputConsumerEnabled = true; - } + mRecentsAnimationWrapper.enableInputConsumer(); } @UiThread @@ -415,18 +441,6 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { } } - if (endShift == mCurrentShift.value) { - mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0) - ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS); - } else { - animateToProgress(endShift, duration); - } - } - - @UiThread - public void onGestureInterruptEnd() { - final float endShift = 0; - final long duration = MAX_SWIPE_DURATION; animateToProgress(endShift, duration); } @@ -437,8 +451,9 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { anim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { - mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0) - ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS); + new MainThreadExecutor().execute(() -> mStateCallback.setState( + (Float.compare(mCurrentShift.value, 0) == 0) + ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS)); } }); anim.start(); @@ -446,12 +461,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { @UiThread private void resumeLastTask() { - if (mRecentsAnimationController != null) { - BackgroundExecutor.get().submit(() -> { - mRecentsAnimationController.setInputConsumerEnabled(false); - mRecentsAnimationController.finish(false /* toHome */); - }); - } + mRecentsAnimationWrapper.finish(false /* toHome */); } public void reset() { @@ -497,22 +507,21 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler { mQuickScrubController.snapToPageForCurrentQuickScrubSection(); } } else { - if (mRecentsAnimationController != null) { - TransactionCompat transaction = new TransactionCompat(); - for (RemoteAnimationTargetCompat app : mRecentsAnimationApps) { - if (app.mode == MODE_CLOSING) { - // Update the screenshot of the task - final ThumbnailData thumbnail = - mRecentsAnimationController.screenshotTask(app.taskId); - mRecentsView.updateThumbnail(app.taskId, thumbnail); + 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 = + mRecentsAnimationWrapper.controller.screenshotTask(app.taskId); + mRecentsView.updateThumbnail(app.taskId, thumbnail); + } } + transaction.apply(); } - transaction.apply(); - BackgroundExecutor.get().submit(() -> { - mRecentsAnimationController.setInputConsumerEnabled(false); - mRecentsAnimationController.finish(true /* toHome */); - }); } + mRecentsAnimationWrapper.finish(true /* toHome */); } }