Merge "Reapply back button alpha at end of state animation" into ub-launcher3-edmonton
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 308e92f..27de1e9 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 0674c61..4c90cbe 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
+import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -31,8 +33,6 @@
import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
-import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
-import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -53,9 +53,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
import android.util.Pair;
-import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -672,6 +670,11 @@
return;
}
+ if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
+ mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
+ mLauncher.getStateManager().moveToRestState();
+ }
+
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 3fb7cd4..717179d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -25,7 +25,6 @@
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
@@ -143,6 +142,8 @@
TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
&& taskView != null) {
+ // Reset the state manager, when changing the interaction mode
+ mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
@@ -190,7 +191,7 @@
// Update all apps interpolator to add a bit of overshoot starting from currFraction
final float currFraction = mCurrentAnimation.getProgressFraction();
mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
- new OvershootInterpolator(Math.min(Math.abs(velocity), 3f)), currFraction, 1);
+ Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
.setInterpolator(LINEAR);
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 52a6dd5..f1aff05 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -15,7 +15,10 @@
*/
package com.android.quickstep;
+import static android.view.View.TRANSLATION_Y;
+
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -25,6 +28,7 @@
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
+import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
@@ -49,20 +53,22 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.uioverrides.FastOverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.TouchConsumer.InteractionType;
+import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.views.LauncherLayoutListener;
-import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.Objects;
@@ -243,34 +249,65 @@
if (wasVisible) {
DeviceProfile dp = activity.getDeviceProfile();
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- activity.getStateManager().goToState(startState, false);
callback.accept(activity.getStateManager()
- .createAnimationToNewWorkspace(endState, accuracy));
+ .createAnimationToNewWorkspace(startState, endState, accuracy));
return;
}
- if (activity.getDeviceProfile().isVerticalBarLayout()) {
- return;
- }
-
- AllAppsTransitionController controller = activity.getAllAppsController();
AnimatorSet anim = new AnimatorSet();
- float scrollRange = Math.max(controller.getShiftRange(), 1);
- float progressDelta = (transitionLength / scrollRange);
+ if (!activity.getDeviceProfile().isVerticalBarLayout()) {
+ AllAppsTransitionController controller = activity.getAllAppsController();
+ float scrollRange = Math.max(controller.getShiftRange(), 1);
+ float progressDelta = (transitionLength / scrollRange);
- float endProgress = endState.getVerticalProgress(activity);
- float startProgress = endProgress + progressDelta;
- ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
- controller, ALL_APPS_PROGRESS, startProgress, endProgress);
- shiftAnim.setInterpolator(LINEAR);
- anim.play(shiftAnim);
+ float endProgress = endState.getVerticalProgress(activity);
+ float startProgress = endProgress + progressDelta;
+ ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
+ controller, ALL_APPS_PROGRESS, startProgress, endProgress);
+ shiftAnim.setInterpolator(LINEAR);
+ anim.play(shiftAnim);
+
+ // Since we are changing the start position of the UI, reapply the state, at the end
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ activity.getStateManager().reapplyState();
+ }
+ });
+ }
+
+ if (interactionType == INTERACTION_NORMAL) {
+ playScaleDownAnim(anim, activity);
+ }
anim.setDuration(transitionLength * 2);
activity.getStateManager().setCurrentAnimation(anim);
callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2));
}
+ /**
+ * Scale down recents from the center task being full screen to being in overview.
+ */
+ private void playScaleDownAnim(AnimatorSet anim, Launcher launcher) {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ TaskView v = recentsView.getPageAt(recentsView.getCurrentPage());
+ ClipAnimationHelper clipHelper = new ClipAnimationHelper();
+ clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
+ if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
+ float fromScale = clipHelper.getSourceRect().width()
+ / clipHelper.getTargetRect().width();
+ float fromTranslationY = clipHelper.getSourceRect().centerY()
+ - clipHelper.getTargetRect().centerY();
+ Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, 1);
+ Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
+ fromTranslationY, 0);
+ scale.setInterpolator(LINEAR);
+ translateY.setInterpolator(LINEAR);
+ anim.playTogether(scale, translateY);
+ }
+ }
+
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<Launcher, Boolean> onInitListener) {
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
index fbcde8b..336be2b 100644
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -20,11 +20,8 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
-import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
-import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
import android.animation.ValueAnimator;
-import android.view.Surface;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
@@ -39,7 +36,6 @@
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Utility class to handle long swipe from an app.
@@ -65,7 +61,6 @@
}
private void init() {
- setTargetAlpha(0, true);
mFlingBlockCheck.blockFling();
// Init animations
@@ -83,8 +78,7 @@
}
public void destroy() {
- // TODO: We can probably also hide the task view
- setTargetAlpha(1, false);
+ // TODO: We can probably also show the task view
mLauncher.getStateManager().goToState(OVERVIEW, false);
}
@@ -136,31 +130,6 @@
animator.start();
}
- private void setTargetAlpha(float alpha, boolean defer) {
- final Surface surface = getSurface(mLauncher.getDragLayer());
- final long frameNumber = defer && surface != null ? getNextFrameNumber(surface) : -1;
- if (defer) {
- if (frameNumber == -1) {
- defer = false;
- } else {
- mLauncher.getDragLayer().invalidate();
- }
- }
-
- TransactionCompat transaction = new TransactionCompat();
- for (RemoteAnimationTargetCompat app : mTargetSet.apps) {
- if (!(app.isNotInRecents
- || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
- transaction.setAlpha(app.leash, alpha);
- if (defer) {
- transaction.deferTransactionUntil(app.leash, surface, frameNumber);
- }
- }
- }
- transaction.setEarlyWakeup();
- transaction.apply();
- }
-
private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
if (!toAllApps) {
@@ -176,4 +145,12 @@
callback.run();
}
+
+ public float getTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
+ if (!(app.isNotInRecents
+ || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
+ return 0;
+ }
+ return expectedAlpha;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 7a79c6f..e0089c6 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -108,7 +108,7 @@
} else {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
LauncherLogProto.Action.Direction.NONE, page,
- TaskUtils.getComponentKeyForTask(taskView.getTask().key));
+ TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key));
}
mWaitingForTaskLaunch = false;
}, taskView.getHandler());
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 34d42ac..b0313fc 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -49,6 +49,9 @@
this.mController = controller;
this.targetSet = targetSet;
+ if (controller == null) {
+ return;
+ }
if (mInputConsumerEnabled) {
enableInputConsumer();
}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index ec2c318..c9ba7dc 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -75,8 +75,11 @@
applicationInfo.loadLabel(packageManager), user);
}
- public static ComponentKey getComponentKeyForTask(Task.TaskKey taskKey) {
- return new ComponentKey(taskKey.getComponent(), UserHandle.of(taskKey.userId));
+ public static ComponentKey getLaunchComponentKeyForTask(Task.TaskKey taskKey) {
+ final ComponentName cn = taskKey.sourceComponent != null
+ ? taskKey.sourceComponent
+ : taskKey.getComponent();
+ return new ComponentKey(cn, UserHandle.of(taskKey.userId));
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index c94174b..66bc501 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -57,6 +58,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -84,6 +86,7 @@
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.StringJoiner;
+import java.util.function.BiFunction;
@TargetApi(Build.VERSION_CODES.O)
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
@@ -356,9 +359,9 @@
// Override the visibility of the activity until the gesture actually starts and we swipe
// up, or until we transition home and the home animation is composed
if (alreadyOnHome) {
- mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+ mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
} else {
- mActivity.addForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+ mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
}
mRecentsView = activity.getOverviewPanel();
@@ -567,7 +570,8 @@
}
private void updateFinalShiftUi() {
- if (mLauncherTransitionController == null) {
+ if (mLauncherTransitionController == null || mLauncherTransitionController
+ .getAnimationPlayer().isStarted()) {
return;
}
mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
@@ -589,7 +593,15 @@
new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
dp.updateInsets(homeContentInsets);
} else {
- overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+ if (mActivity != null) {
+ int loc[] = new int[2];
+ View rootView = mActivity.getRootView();
+ rootView.getLocationOnScreen(loc);
+ overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
+ loc[1] + rootView.getHeight());
+ } else {
+ overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+ }
// If we are not in multi-window mode, home insets should be same as system insets.
Rect insets = new Rect();
WindowManagerWrapper.getInstance().getStableInsets(insets);
@@ -632,7 +644,7 @@
if (curActivity != null) {
// Once the gesture starts, we can no longer transition home through the button, so
// reset the force override of the activity visibility
- mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+ mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
}
}
@@ -653,17 +665,23 @@
}
private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
+ float velocityPxPerMs = endVelocity / 1000;
long duration = MAX_SWIPE_DURATION;
final float endShift;
final float startShift;
+ final Interpolator interpolator;
if (!isFling) {
endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted ? 1 : 0;
long expectedDuration = Math.abs(Math.round((endShift - mCurrentShift.value)
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = mCurrentShift.value;
+ interpolator = DEACCEL;
} else {
endShift = endVelocity < 0 ? 1 : 0;
+ interpolator = endVelocity < 0
+ ? Interpolators.overshootInterpolatorForVelocity(velocityPxPerMs, 2f)
+ : DEACCEL;
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
@@ -672,14 +690,13 @@
// we want the page's snap velocity to approximately match the velocity at
// which the user flings, so we scale the duration by a value near to the
// derivative of the scroll interpolator at zero, ie. 2.
- long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+ long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs));
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
}
- startShift = Utilities.boundToRange(mCurrentShift.value - endVelocity * SINGLE_FRAME_MS
- / (mTransitionDragLength * 1000), 0, 1);
+ startShift = Utilities.boundToRange(mCurrentShift.value - velocityPxPerMs
+ * SINGLE_FRAME_MS / (mTransitionDragLength), 0, 1);
}
-
- animateToProgress(startShift, endShift, duration, DEACCEL);
+ animateToProgress(startShift, endShift, duration, interpolator);
}
private void doLogGesture(boolean toLauncher) {
@@ -706,6 +723,12 @@
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
private void animateToProgress(float start, float end, long duration,
Interpolator interpolator) {
+ mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
+ interpolator));
+ }
+
+ private void animateToProgressInternal(float start, float end, long duration,
+ Interpolator interpolator) {
mIsGoingToHome = Float.compare(end, 1) == 0;
ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
anim.setInterpolator(interpolator);
@@ -717,7 +740,26 @@
: STATE_SCALED_CONTROLLER_APP);
}
});
- mRecentsAnimationWrapper.runOnInit(anim::start);
+ anim.start();
+ long startMillis = SystemClock.uptimeMillis();
+ executeOnUiThread(() -> {
+ // Animate the launcher components at the same time as the window, always on UI thread.
+ if (mLauncherTransitionController != null && !mWasLauncherAlreadyVisible
+ && start != end && duration > 0) {
+ // Adjust start progress and duration in case we are on a different thread.
+ long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
+ float elapsedProgress = (float) elapsedMillis / duration;
+ float adjustedStart = Utilities.mapRange(elapsedProgress, start, end);
+ long adjustedDuration = duration - elapsedMillis;
+ // We want to use the same interpolator as the window, but need to adjust it to
+ // interpolate over the remaining progress (end - start).
+ mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
+ interpolator, adjustedStart, end));
+ mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
+ mLauncherTransitionController.getAnimationPlayer().start();
+ }
+ });
}
@UiThread
@@ -769,6 +811,9 @@
private void resetStateForAnimationCancel() {
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
+
+ // Leave the pending invisible flag, as it may be used by wallpaper open animation.
+ mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
}
public void layoutListenerClosed() {
@@ -915,6 +960,7 @@
if (mLongSwipeController != null) {
mLongSwipeController.destroy();
+ setTargetAlphaProvider((t, a1) -> a1);
// Rebuild animations
buildAnimationController();
@@ -956,6 +1002,7 @@
mLongSwipeController = mActivityControlHelper.getLongSwipeController(
mActivity, mRecentsAnimationWrapper.targetSet);
onLongSwipeDisplacementUpdated();
+ setTargetAlphaProvider(mLongSwipeController::getTargetAlpha);
}
private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
@@ -970,4 +1017,12 @@
() -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
}
+
+ private void setTargetAlphaProvider(
+ BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
+ mClipAnimationHelper.setTaskAlphaCallback(provider);
+ // TODO: For some reason, when calling updateFinalShift multiple times on the same frame,
+ // only the first callback is executed.
+ Utilities.postAsyncCallback(mMainThreadHandler, this::updateFinalShift);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d550edc..1ea1a52 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -16,7 +16,7 @@
package com.android.quickstep.views;
-import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -199,7 +199,7 @@
public void onPinnedStackAnimationStarted() {
// Needed for activities that auto-enter PiP, which will not trigger a remote
// animation to be created
- mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+ mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
}
};
@@ -667,9 +667,10 @@
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
- mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(), 0, 0), null,
- null, "", "", 0, 0, false, true, false, false,
- new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false);
+ mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
+ new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
+ false, true, false, false, new ActivityManager.TaskDescription(), 0,
+ new ComponentName("", ""), false);
taskView.bind(mTmpRunningTask);
}
setCurrentTask(runningTaskId);
@@ -792,7 +793,7 @@
if (shouldLog) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
onEndListener.logAction, Direction.UP, index,
- TaskUtils.getComponentKeyForTask(task.key));
+ TaskUtils.getLaunchComponentKeyForTask(task.key));
}
}
}
@@ -1229,7 +1230,7 @@
if (task != null) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
- TaskUtils.getComponentKeyForTask(task.key));
+ TaskUtils.getLaunchComponentKeyForTask(task.key));
}
} else {
onTaskLaunchFinish.accept(false);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b5f31b8..a7690e1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -118,7 +118,7 @@
launchTask(true /* animate */);
BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
- TaskUtils.getComponentKeyForTask(getTask().key));
+ TaskUtils.getLaunchComponentKeyForTask(getTask().key));
});
setOutlineProvider(new TaskOutlineProvider(getResources()));
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index e117deb..a4b6f5b 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -44,13 +44,25 @@
public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
+ public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2;
+
+ // This is not treated as invisibility flag, but adds as a hint for an incomplete transition.
+ // When the wallpaper animation runs, it replaces this flag with a proper invisibility
+ // flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation.
+ public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3;
+
+ private static final int INVISIBLE_FLAGS =
+ INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS;
+ public static final int STATE_HANDLER_INVISIBILITY_FLAGS =
+ INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
public static final int INVISIBLE_ALL =
- INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS;
+ INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
@Retention(SOURCE)
@IntDef(
flag = true,
- value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS})
+ value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS,
+ INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION})
public @interface InvisibilityFlags{}
private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
@@ -208,7 +220,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see LauncherAppTransitionManagerImpl.getWallpaperOpenRunner()
+ * @see LauncherAppTransitionManagerImpl#getWallpaperOpenRunner()
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
@@ -218,12 +230,15 @@
mForceInvisible &= ~flag;
}
-
/**
* @return Wether this activity should be considered invisible regardless of actual visibility.
*/
public boolean isForceInvisible() {
- return mForceInvisible != 0;
+ return hasSomeInvisibleFlag(INVISIBLE_FLAGS);
+ }
+
+ public boolean hasSomeInvisibleFlag(int mask) {
+ return (mForceInvisible & mask) != 0;
}
public interface MultiWindowModeChangedListener {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 05c515b..8b415d6 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -298,6 +298,24 @@
/**
* Creates a {@link AnimatorPlaybackController} that can be used for a controlled
+ * state transition. The UI is force-set to fromState before creating the controller.
+ * @param fromState the initial state for the transition.
+ * @param state the final state for the transition.
+ * @param duration intended duration for normal playback. Use higher duration for better
+ * accuracy.
+ */
+ public AnimatorPlaybackController createAnimationToNewWorkspace(
+ LauncherState fromState, LauncherState state, long duration) {
+ mConfig.reset();
+ for (StateHandler handler : getStateHandlers()) {
+ handler.setState(fromState);
+ }
+
+ return createAnimationToNewWorkspace(state, duration);
+ }
+
+ /**
+ * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
* state transition.
* @param state the final state for the transition.
* @param duration intended duration for normal playback. Use higher duration for better
@@ -348,12 +366,6 @@
}
@Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- mState = mCurrentStableState;
- }
-
- @Override
public void onAnimationSuccess(Animator animator) {
// Run any queued runnables
if (onCompleteRunnable != null) {
@@ -438,6 +450,7 @@
}
public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
+ clearCurrentAnimation();
setCurrentAnimation(controller.getTarget());
mConfig.userControlled = true;
mConfig.playbackController = controller;
@@ -538,6 +551,9 @@
@Override
public void onAnimationEnd(Animator animation) {
+ if (playbackController != null && playbackController.getTarget() == animation) {
+ playbackController = null;
+ }
if (mCurrentAnimation == animation) {
mCurrentAnimation = null;
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 4bd9a9b..5355c5e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -272,6 +272,24 @@
return scale;
}
+ /**
+ * Maps t from one range to another range.
+ * @param t The value to map.
+ * @param fromMin The lower bound of the range that t is being mapped from.
+ * @param fromMax The upper bound of the range that t is being mapped from.
+ * @param toMin The lower bound of the range that t is being mapped to.
+ * @param toMax The upper bound of the range that t is being mapped to.
+ * @return The mapped value of t.
+ */
+ public static float mapToRange(float t, float fromMin, float fromMax, float toMin, float toMax) {
+ if (fromMin == fromMax || toMin == toMax) {
+ Log.e(TAG, "mapToRange: range has 0 length");
+ return toMin;
+ }
+ float progress = Math.abs(t - fromMin) / Math.abs(fromMax - fromMin);
+ return mapRange(progress, toMin, toMax);
+ }
+
public static float mapRange(float value, float min, float max) {
return min + (value * (max - min));
}
@@ -463,6 +481,13 @@
}
/**
+ * @see #boundToRange(int, int, int).
+ */
+ public static long boundToRange(long value, long lowerBound, long upperBound) {
+ return Math.max(lowerBound, Math.min(value, upperBound));
+ }
+
+ /**
* Wraps a message with a TTS span, so that a different message is spoken than
* what is getting displayed.
* @param msg original message
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 84085cb..50fb0a5 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -202,6 +202,19 @@
}
}
+ public void dispatchSetInterpolator(TimeInterpolator interpolator) {
+ dispatchSetInterpolatorRecursively(mAnim, interpolator);
+ }
+
+ private void dispatchSetInterpolatorRecursively(Animator anim, TimeInterpolator interpolator) {
+ anim.setInterpolator(interpolator);
+ if (anim instanceof AnimatorSet) {
+ for (Animator child : nonNullList(((AnimatorSet) anim).getChildAnimations())) {
+ dispatchSetInterpolatorRecursively(child, interpolator);
+ }
+ }
+ }
+
public void setOnCancelRunnable(Runnable runnable) {
mOnCancelRunnable = runnable;
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index bace7df..d17572e 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -24,6 +24,8 @@
import android.view.animation.OvershootInterpolator;
import android.view.animation.PathInterpolator;
+import com.android.launcher3.Utilities;
+
/**
* Common interpolators used in Launcher
@@ -116,6 +118,19 @@
return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
}
+ public static Interpolator overshootInterpolatorForVelocity(float velocity) {
+ return overshootInterpolatorForVelocity(velocity, 1f);
+ }
+
+ /**
+ * Create an OvershootInterpolator with tension directly related to the velocity (in px/ms).
+ * @param velocity The start velocity of the animation we want to overshoot.
+ * @param dampFactor An optional factor to reduce the amount of tension (how far we overshoot).
+ */
+ public static Interpolator overshootInterpolatorForVelocity(float velocity, float dampFactor) {
+ return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f) / dampFactor);
+ }
+
/**
* Runs the given interpolator such that the entire progress is set between the given bounds.
* That is, we set the interpolation to 0 until lowerBound and reach 1 by upperBound.
@@ -135,4 +150,15 @@
return interpolator.getInterpolation((t - lowerBound) / (upperBound - lowerBound));
};
}
+
+ /**
+ * Runs the given interpolator such that the interpolated value is mapped to the given range.
+ * This is useful, for example, if we only use this interpolator for part of the animation,
+ * such as to take over a user-controlled animation when they let go.
+ */
+ public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound,
+ float upperBound) {
+ return t -> Utilities.mapToRange(interpolator.getInterpolation(t), 0, 1,
+ lowerBound, upperBound);
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 453810c..898b1b7 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -86,6 +86,9 @@
private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
private AnimatorSet mAtomicAnim;
+ // True if we want to resume playing atomic components when mAtomicAnim completes.
+ private boolean mScheduleResumeAtomicComponent;
+
private boolean mPassedOverviewAtomicThreshold;
// mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
// However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
@@ -93,6 +96,8 @@
// interfere with the atomic animation. When the atomic animation ends, we start controlling
// the atomic components as well, using this controller.
private AnimatorPlaybackController mAtomicComponentsController;
+ private LauncherState mAtomicComponentsTargetState = NORMAL;
+
private float mAtomicComponentsStartProgress;
public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
@@ -191,27 +196,21 @@
}
int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
? NON_ATOMIC_COMPONENT : ANIM_ALL;
+ mScheduleResumeAtomicComponent = false;
if (mAtomicAnim != null) {
+ animComponents = NON_ATOMIC_COMPONENT;
// Control the non-atomic components until the atomic animation finishes, then control
// the atomic components as well.
- animComponents = NON_ATOMIC_COMPONENT;
- mAtomicAnim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animation) {
- cancelAtomicComponentsController();
- if (mCurrentAnimation != null) {
- mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
- long duration = (long) (getShiftRange() * 2);
- mAtomicComponentsController = AnimatorPlaybackController.wrap(
- createAtomicAnimForState(mFromState, mToState, duration), duration);
- mAtomicComponentsController.dispatchOnStart();
- }
- }
- });
+ mScheduleResumeAtomicComponent = true;
}
- if (goingBetweenNormalAndOverview(mFromState, mToState)) {
+ if (goingBetweenNormalAndOverview(mFromState, mToState)
+ || mAtomicComponentsTargetState != mToState) {
cancelAtomicComponentsController();
}
+
+ if (mAtomicComponentsController != null) {
+ animComponents &= ~ATOMIC_COMPONENT;
+ }
mProgressMultiplier = initCurrentAnimation(animComponents);
mCurrentAnimation.dispatchOnStart();
return true;
@@ -302,10 +301,28 @@
mAtomicAnim.cancel();
}
mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
- mAtomicAnim.addListener(new AnimatorListenerAdapter() {
+ mAtomicAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
mAtomicAnim = null;
+ mScheduleResumeAtomicComponent = false;
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (!mScheduleResumeAtomicComponent) {
+ return;
+ }
+ cancelAtomicComponentsController();
+ if (mCurrentAnimation != null) {
+ mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
+ long duration = (long) (getShiftRange() * 2);
+ mAtomicComponentsController = AnimatorPlaybackController.wrap(
+ createAtomicAnimForState(mFromState, mToState, duration), duration);
+ mAtomicComponentsController.dispatchOnStart();
+ mAtomicComponentsTargetState = mToState;
+ }
}
});
mAtomicAnim.start();
@@ -457,7 +474,7 @@
}
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- clearState();
+ cancelAnimationControllers();
boolean shouldGoToTargetState = true;
if (mPendingAnimation != null) {
boolean reachedTarget = mToState == targetState;
@@ -484,6 +501,15 @@
}
protected void clearState() {
+ cancelAnimationControllers();
+ if (mAtomicAnim != null) {
+ mAtomicAnim.cancel();
+ mAtomicAnim = null;
+ }
+ mScheduleResumeAtomicComponent = false;
+ }
+
+ private void cancelAnimationControllers() {
mCurrentAnimation = null;
cancelAtomicComponentsController();
mDetector.finishedScrolling();