9/ Clean up swipe shared state

- Add TaskAnimationManager which keeps track of the animation state whose
  lifecycle can be longer than the gesture.  Move some of the logic related
  to cleaning up old animations into this class (called when the state is
  shared across gestures).
- Instead of calling into the shared state directly via UIFactory, add
  callback to cleanup the animation and shared state from Launcher

Bug: 141886704

Change-Id: Ib6140b37162f7460a20fa1046cfd4f4068e4a1c6
Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
Winson Chung 2019-10-04 15:33:18 -07:00
parent 9196cb11a2
commit c9bf6d45ac
18 changed files with 338 additions and 236 deletions

View File

@ -88,6 +88,4 @@ public abstract class RecentsUiFactory {
public static RotationMode getRotationMode(DeviceProfile dp) {
return RotationMode.NORMAL;
}
public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
}

View File

@ -18,7 +18,6 @@ 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;
@ -45,7 +44,6 @@ import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
@ -188,17 +186,6 @@ public abstract class RecentsUiFactory {
return new RecentsViewStateController(launcher);
}
/** 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.
*

View File

@ -493,22 +493,21 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
}
@Override
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
Runnable onFinishRunnable) {
Launcher launcher = getCreatedActivity();
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView == null) {
if (runnable != null) {
runnable.run();
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
return;
}
TaskView taskView = recentsView.getRunningTaskView();
if (taskView != null) {
taskView.setShowScreenshot(true);
taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
ViewUtils.postDraw(taskView, runnable);
} else if (runnable != null) {
runnable.run();
}
recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
}
@Override
public void setOnDeferredActivityLaunchCallback(Runnable r) {
getCreatedActivity().setOnDeferredActivityLaunchCallback(r);
}
}

View File

@ -15,32 +15,12 @@
*/
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.util.Log;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.io.PrintWriter;
/**
* Utility class used to store state information shared across multiple transitions.
*/
public class SwipeSharedState implements RecentsAnimationListener {
private OverviewComponentObserver mOverviewComponentObserver;
private RecentsAnimationCallbacks mRecentsAnimationListener;
private RecentsAnimationController mLastRecentsAnimationController;
private RecentsAnimationTargets mLastAnimationTarget;
private boolean mLastAnimationCancelled = false;
private boolean mLastAnimationRunning = false;
public class SwipeSharedState {
public boolean canGestureBeContinued;
public boolean goingToLauncher;
@ -48,106 +28,6 @@ public class SwipeSharedState implements RecentsAnimationListener {
public int nextRunningTaskId = -1;
private int mLogId;
public void setOverviewComponentObserver(OverviewComponentObserver observer) {
mOverviewComponentObserver = observer;
}
@Override
public final void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
mLastRecentsAnimationController = controller;
mLastAnimationTarget = targets;
mLastAnimationCancelled = false;
mLastAnimationRunning = true;
}
@Override
public final void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
if (thumbnailData != null) {
mOverviewComponentObserver.getActivityInterface().switchToScreenshot(thumbnailData,
() -> {
mLastRecentsAnimationController.cleanupScreenshot();
clearAnimationState();
});
} else {
clearAnimationState();
}
}
@Override
public final void onRecentsAnimationFinished(RecentsAnimationController controller) {
if (mLastRecentsAnimationController == controller) {
mLastAnimationRunning = false;
}
}
private void clearAnimationTarget() {
if (mLastAnimationTarget != null) {
mLastAnimationTarget.release();
mLastAnimationTarget = null;
}
}
private void clearAnimationState() {
clearAnimationTarget();
mLastAnimationCancelled = true;
mLastAnimationRunning = false;
}
private void clearListenerState(boolean finishAnimation) {
if (mRecentsAnimationListener != null) {
mRecentsAnimationListener.removeListener(this);
mRecentsAnimationListener.notifyAnimationCanceled();
if (mLastAnimationRunning && mLastRecentsAnimationController != null) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
finishAnimation
? mLastRecentsAnimationController::finishAnimationToHome
: mLastRecentsAnimationController::finishAnimationToApp);
mLastRecentsAnimationController = null;
mLastAnimationTarget = null;
}
}
mRecentsAnimationListener = null;
clearAnimationTarget();
mLastAnimationCancelled = false;
mLastAnimationRunning = false;
}
public RecentsAnimationCallbacks newRecentsAnimationCallbacks() {
Preconditions.assertUIThread();
if (mLastAnimationRunning) {
String msg = "New animation started before completing old animation";
if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("SwipeSharedState", msg, new Exception());
}
}
clearListenerState(false /* finishAnimation */);
boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
: mOverviewComponentObserver.getActivityInterface().shouldMinimizeSplitScreen();
mRecentsAnimationListener = new RecentsAnimationCallbacks(shouldMinimiseSplitScreen);
mRecentsAnimationListener.addListener(this);
return mRecentsAnimationListener;
}
public RecentsAnimationCallbacks getActiveListener() {
return mRecentsAnimationListener;
}
public void applyActiveRecentsAnimationState(RecentsAnimationListener listener) {
if (mLastRecentsAnimationController != null) {
listener.onRecentsAnimationStart(mLastRecentsAnimationController,
mLastAnimationTarget);
} else if (mLastAnimationCancelled) {
listener.onRecentsAnimationCanceled(null);
}
}
/**
* Called when a recents animation has finished, but was interrupted before the next task was
* launched. The given {@param runningTaskId} should be used as the running task for the
@ -156,11 +36,9 @@ public class SwipeSharedState implements RecentsAnimationListener {
public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
recentsAnimationFinishInterrupted = true;
nextRunningTaskId = runningTaskId;
mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
}
public void clearAllState(boolean finishAnimation) {
clearListenerState(finishAnimation);
public void clearAllState() {
canGestureBeContinued = false;
recentsAnimationFinishInterrupted = false;
nextRunningTaskId = -1;
@ -172,8 +50,6 @@ public class SwipeSharedState implements RecentsAnimationListener {
pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
pw.println(prefix + "logTraceId=" + mLogId);
}

View File

@ -250,10 +250,7 @@ public class TouchInteractionService extends Service implements
return sSwipeSharedState;
}
private final InputConsumer mResetGestureInputConsumer =
new ResetGestureInputConsumer(sSwipeSharedState);
private final BaseSwipeUpHandler.Factory mWindowTreansformFactory =
private final BaseSwipeUpHandler.Factory mWindowTransformFactory =
this::createWindowTransformSwipeHandler;
private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
this::createFallbackNoButtonSwipeHandler;
@ -264,10 +261,12 @@ public class TouchInteractionService extends Service implements
private OverviewComponentObserver mOverviewComponentObserver;
private InputConsumerController mInputConsumer;
private RecentsAnimationDeviceState mDeviceState;
private TaskAnimationManager mTaskAnimationManager;
private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
private InputConsumer mResetGestureInputConsumer;
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
@ -338,13 +337,13 @@ public class TouchInteractionService extends Service implements
@UiThread
public void onUserUnlocked() {
mTaskAnimationManager = new TaskAnimationManager();
mRecentsModel = RecentsModel.INSTANCE.get(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged();
onAssistantVisibilityChanged();
@ -356,6 +355,18 @@ public class TouchInteractionService extends Service implements
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
private void onDeferredActivityLaunch() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
null, () -> {
mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
sSwipeSharedState.clearAllState();
});
} else {
mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
}
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (!mDeviceState.isUserUnlocked() || !mMode.hasGestures) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
@ -509,7 +520,8 @@ public class TouchInteractionService extends Service implements
RunningTaskInfo runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(0));
if (!useSharedState) {
sSwipeSharedState.clearAllState(false /* finishAnimation */);
mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
sSwipeSharedState.clearAllState();
}
if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
@ -572,20 +584,20 @@ public class TouchInteractionService extends Service implements
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
factory = mWindowTreansformFactory;
factory = mWindowTransformFactory;
}
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
return new OtherActivityInputConsumer(this, mDeviceState, gestureState, runningTaskInfo,
shouldDefer, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat,
disableHorizontalSwipe, factory, mLogId);
return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, runningTaskInfo, shouldDefer, this::onConsumerInactive,
sSwipeSharedState, mInputMonitorCompat, disableHorizontalSwipe, factory, mLogId);
}
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState,
RunningTaskInfo taskInfo) {
if (mMode == Mode.NO_BUTTON && taskInfo != null) {
return new DeviceLockedInputConsumer(this, mDeviceState, gestureState,
sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
} else {
return mResetGestureInputConsumer;
}
@ -647,9 +659,8 @@ public class TouchInteractionService extends Service implements
return;
}
// Pass null animation handler to indicate this start is preload.
startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
null);
mTaskAnimationManager.preloadRecentsAnimation(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
}
@Override
@ -725,9 +736,9 @@ public class TouchInteractionService extends Service implements
private BaseSwipeUpHandler createWindowTransformSwipeHandler(GestureState gestureState,
RunningTaskInfo runningTask, long touchTimeMs, boolean continuingLastGesture,
boolean isLikelyToStartNewTask) {
return new WindowTransformSwipeHandler(this, mDeviceState, gestureState, runningTask,
touchTimeMs, mOverviewComponentObserver, continuingLastGesture, mInputConsumer,
mRecentsModel);
return new WindowTransformSwipeHandler(this, mDeviceState, mTaskAnimationManager,
gestureState, runningTask, touchTimeMs, mOverviewComponentObserver,
continuingLastGesture, mInputConsumer, mRecentsModel);
}
private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,

View File

@ -192,6 +192,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private static final int LOG_NO_OP_PAGE_INDEX = -1;
private final RecentsAnimationDeviceState mDeviceState;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private GestureEndTarget mGestureEndTarget;
@ -225,13 +226,17 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, RunningTaskInfo runningTaskInfo, long touchTimeMs,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
RunningTaskInfo runningTaskInfo, long touchTimeMs,
OverviewComponentObserver overviewComponentObserver, boolean continuingLastGesture,
InputConsumerController inputConsumer, RecentsModel recentsModel) {
super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
runningTaskInfo.id);
mDeviceState = deviceState;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@ -401,9 +406,26 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
// that time by a previous window transition.
setupRecentsViewUi();
// For the duration of the gesture, in cases where an activity is launched while the
// activity is not yet resumed, finish the animation to ensure we get resumed
mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch);
notifyGestureStartedAsync();
}
private void onDeferredActivityLaunch() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
null, () -> {
mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
TouchInteractionService.getSwipeSharedState().clearAllState();
});
} else {
mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
}
}
private void setupRecentsViewUi() {
if (mContinuingLastGesture) {
updateSysUiFlags(mCurrentShift.value);
@ -1091,6 +1113,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mRecentsView.onGestureAnimationEnd();
// Reset the callback for deferred activity launches
mActivityInterface.setOnDeferredActivityLaunchCallback(null);
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
removeLiveTileOverlay();
}

View File

@ -22,7 +22,6 @@ import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@ -48,6 +47,7 @@ import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
@ -76,6 +76,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private final float mTouchSlopSquared;
private final SwipeSharedState mSwipeSharedState;
@ -98,10 +99,12 @@ public class DeviceLockedInputConsumer implements InputConsumer,
private RecentsAnimationTargets mRecentsAnimationTargets;
public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, SwipeSharedState swipeSharedState,
InputMonitorCompat inputMonitorCompat, int runningTaskId, int logId) {
TaskAnimationManager taskAnimationManager, GestureState gestureState,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
int runningTaskId, int logId) {
mContext = context;
mDeviceState = deviceState;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
mSwipeSharedState = swipeSharedState;
@ -207,16 +210,14 @@ public class DeviceLockedInputConsumer implements InputConsumer,
private void startRecentsTransition() {
mThresholdCrossed = true;
RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
callbacks.addListener(this);
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
mInputMonitorCompat.pilferPointers();
startRecentsActivityAsync(intent, callbacks);
mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@Override

View File

@ -26,7 +26,6 @@ import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@ -58,6 +57,7 @@ import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
@ -80,7 +80,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
private final RecentsAnimationDeviceState mDeviceState;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final RunningTaskInfo mRunningTask;
private final SwipeSharedState mSwipeSharedState;
@ -95,6 +97,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
private VelocityTracker mVelocityTracker;
private BaseSwipeUpHandler mInteractionHandler;
@ -126,13 +129,15 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
private int mLogId;
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
GestureState gestureState, RunningTaskInfo runningTaskInfo,
boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
RunningTaskInfo runningTaskInfo, boolean isDeferredDownTarget,
Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
boolean disableHorizontalSwipe, Factory handlerFactory, int logId) {
super(base);
mLogId = logId;
mDeviceState = deviceState;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
@ -147,7 +152,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
mSwipeSharedState = swipeSharedState;
@ -329,25 +334,22 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
long touchTimeMs, boolean isLikelyToStartNewTask) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mGestureState, mRunningTask,
touchTimeMs, listenerSet != null, isLikelyToStartNewTask);
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, mRunningTask, touchTimeMs,
mTaskAnimationManager.isRecentsAnimationRunning(), isLikelyToStartNewTask);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
mInteractionHandler.initWhenReady();
mInteractionHandler = handler;
handler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
handler.initWhenReady();
if (listenerSet != null) {
listenerSet.addListener(handler);
mSwipeSharedState.applyActiveRecentsAnimationState(handler);
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
mActiveCallbacks.addListener(mInteractionHandler);
mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted();
} else {
RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
callbacks.addListener(handler);
Intent intent = handler.getLaunchIntent();
Intent intent = mInteractionHandler.getLaunchIntent();
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
startRecentsActivityAsync(intent, callbacks);
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
}
}
@ -415,9 +417,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private void removeListener() {
RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
if (listenerSet != null) {
listenerSet.removeListener(mInteractionHandler);
if (mActiveCallbacks != null) {
mActiveCallbacks.removeListener(mInteractionHandler);
}
}

View File

@ -18,17 +18,18 @@ package com.android.quickstep.inputconsumers;
import android.view.MotionEvent;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TouchInteractionService;
/**
* A NO_OP input consumer which also resets any pending gesture
*/
public class ResetGestureInputConsumer implements InputConsumer {
private final SwipeSharedState mSwipeSharedState;
private final TaskAnimationManager mTaskAnimationManager;
public ResetGestureInputConsumer(SwipeSharedState swipeSharedState) {
mSwipeSharedState = swipeSharedState;
public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
mTaskAnimationManager = taskAnimationManager;
}
@Override
@ -39,8 +40,9 @@ public class ResetGestureInputConsumer implements InputConsumer {
@Override
public void onMotionEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
&& mSwipeSharedState.getActiveListener() != null) {
mSwipeSharedState.clearAllState(false /* finishAnimation */);
&& mTaskAnimationManager.isRecentsAnimationRunning()) {
mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
TouchInteractionService.getSwipeSharedState().clearAllState();
}
}
}

View File

@ -102,8 +102,9 @@ 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)));
switchToScreenshot(null,
() -> finishRecentsAnimation(true /* toRecents */,
() -> mActivity.getStateManager().goToState(NORMAL)));
} else {
mActivity.getStateManager().goToState(NORMAL);
}

View File

@ -1851,20 +1851,20 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
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) {
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
if (taskView == null) {
if (onFinishRunnable != null) {
onFinishRunnable.run();
if (taskView != null) {
taskView.setShowScreenshot(true);
if (thumbnailData != null) {
taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
} else {
taskView.getThumbnail().refresh();
}
return;
ViewUtils.postDraw(taskView, onFinishRunnable);
} else {
onFinishRunnable.run();
}
taskView.setShowScreenshot(true);
taskView.getThumbnail().refresh();
ViewUtils.postDraw(taskView, onFinishRunnable);
}
@Override

View File

@ -60,6 +60,11 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
/**
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
*/
default void setOnDeferredActivityLaunchCallback(Runnable r) {}
@Nullable
T getCreatedActivity();
@ -96,7 +101,8 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
default void closeOverlay() { }
default void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {}
default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
Runnable runnable) {}
interface AnimationFactory {

View File

@ -16,12 +16,13 @@
package com.android.quickstep;
import com.android.launcher3.BaseDraggingActivity;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
* and fires events when the states change.
*/
public class GestureState {
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
// Needed to interact with the current activity
private BaseActivityInterface mActivityInterface;
@ -33,4 +34,20 @@ public class GestureState {
public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
return mActivityInterface;
}
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
// To be implemented
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
// To be implemented
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
// To be implemented
}
}

View File

@ -127,7 +127,7 @@ public class RecentsAnimationCallbacks implements
*/
public interface RecentsAnimationListener {
default void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targetSet) {}
RecentsAnimationTargets targets) {}
/**
* Callback from the system when the recents animation is canceled. {@param thumbnailData}
@ -135,6 +135,9 @@ public class RecentsAnimationCallbacks implements
*/
default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
/**
* Callback made whenever the recents animation is finished.
*/
default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
}
}

View File

@ -41,13 +41,4 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets {
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
/**
* Clones the target set without any actual targets. Used only when continuing a gesture after
* the actual recents animation has finished.
*/
public RecentsAnimationTargets cloneWithoutTargets() {
return new RecentsAnimationTargets(new RemoteAnimationTargetCompat[0],
new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds);
}
}

View File

@ -0,0 +1,173 @@
/*
* 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private ThumbnailData mCanceledThumbnail;
/**
* Preloads the recents animation.
*/
public void preloadRecentsAnimation(Intent intent) {
// Pass null animation handler to indicate this start is for preloading
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, null, null, null));
}
/**
* Starts a new recents animation for the activity with the given {@param intent}.
*/
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("TaskAnimationManager", msg, new Exception());
}
}
// But force-finish it anyways
finishRunningRecentsAnimation(false /* toHome */);
final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
mLastGestureState = gestureState;
mCallbacks = new RecentsAnimationCallbacks(activityInterface.shouldMinimizeSplitScreen());
mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
mController = controller;
mTargets = targets;
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
if (thumbnailData != null) {
// If a screenshot is provided, switch to the screenshot before cleaning up
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
() -> cleanUpRecentsAnimation());
} else {
cleanUpRecentsAnimation();
}
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
cleanUpRecentsAnimation();
}
});
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, mCallbacks, null, null));
return mCallbacks;
}
/**
* Continues the existing running recents animation for a new gesture.
*/
public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
return mCallbacks;
}
/**
* Finishes the running recents animation.
*/
public void finishRunningRecentsAnimation(boolean toHome) {
if (mController != null) {
mCallbacks.notifyAnimationCanceled();
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
: mController::finishAnimationToApp);
cleanUpRecentsAnimation();
}
}
/**
* Used to notify a listener of the current recents animation state (used if the listener was
* not yet added to the callbacks at the point that the listener callbacks would have been
* made).
*/
public void notifyRecentsAnimationState(
RecentsAnimationCallbacks.RecentsAnimationListener listener) {
if (isRecentsAnimationRunning()) {
listener.onRecentsAnimationStart(mController, mTargets);
}
// TODO: Do we actually need to report canceled/finished?
}
/**
* @return whether there is a recents animation running.
*/
public boolean isRecentsAnimationRunning() {
return mController != null;
}
/**
* Cleans up the recents animation entirely.
*/
private void cleanUpRecentsAnimation() {
// Clean up the screenshot if necessary
if (mController != null && mCanceledThumbnail != null) {
mController.cleanupScreenshot();
}
// Release all the target leashes
if (mTargets != null) {
mTargets.release();
}
// Remove gesture state from callbacks
if (mCallbacks != null && mLastGestureState != null) {
mCallbacks.removeListener(mLastGestureState);
}
mController = null;
mCallbacks = null;
mTargets = null;
mCanceledThumbnail = null;
mLastGestureState = null;
}
public void dump() {
// TODO
}
}

View File

@ -267,6 +267,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
// Used to notify when an activity launch has been deferred because launcher is not yet resumed
// TODO: See if we can remove this later
private Runnable mOnDeferredActivityLaunchCallback;
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
@ -1886,7 +1890,10 @@ 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(this, true /* finishAnimation */);
if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null;
}
return true;
}
@ -1947,6 +1954,14 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mOnResumeCallbacks.add(callback);
}
/**
* Persistant callback which notifies when an activity launch is deferred because the activity
* was not yet resumed.
*/
public void setOnDeferredActivityLaunchCallback(Runnable callback) {
mOnDeferredActivityLaunchCallback = callback;
}
/**
* Implementation of the method from LauncherModel.Callbacks.
*/

View File

@ -96,9 +96,6 @@ public class UiFactory {
public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
/** No-op. */
public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}