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