Merge "Do not reset the previous state animation, if it is a part of the new state animaiton" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 74d455f..1620352 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -19,6 +19,13 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.launcher3.anim.Interpolators.APP_CLOSE_ALPHA;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+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;
@@ -31,7 +38,6 @@
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -45,7 +51,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
@@ -55,12 +60,10 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.quickstep.RecentsAnimationInterpolator;
-import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -79,7 +82,7 @@
implements OnDeviceProfileChangeListener {
private static final String TAG = "LauncherTransition";
- private static final int STATUS_BAR_TRANSITION_DURATION = 120;
+ public static final int STATUS_BAR_TRANSITION_DURATION = 120;
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
@@ -87,7 +90,7 @@
private static final int APP_LAUNCH_DURATION = 500;
// Use a shorter duration for x or y translation to create a curve effect
private static final int APP_LAUNCH_CURVED_DURATION = 233;
- private static final int RECENTS_LAUNCH_DURATION = 336;
+ public static final int RECENTS_LAUNCH_DURATION = 336;
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 350;
@@ -185,64 +188,6 @@
}
/**
- * Try to find a TaskView that corresponds with the component of the launched view.
- *
- * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
- * Otherwise, we will assume we are using a normal app transition, but it's possible that the
- * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
- */
- private TaskView findTaskViewToLaunch(
- BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
- if (v instanceof TaskView) {
- return (TaskView) v;
- }
- RecentsView recentsView = activity.getOverviewPanel();
-
- // It's possible that the launched view can still be resolved to a visible task view, check
- // the task id of the opening task and see if we can find a match.
- if (v.getTag() instanceof ItemInfo) {
- ItemInfo itemInfo = (ItemInfo) v.getTag();
- ComponentName componentName = itemInfo.getTargetComponent();
- if (componentName != null) {
- for (int i = 0; i < recentsView.getChildCount(); i++) {
- TaskView taskView = (TaskView) recentsView.getPageAt(i);
- if (recentsView.isTaskViewVisible(taskView)) {
- Task task = taskView.getTask();
- if (componentName.equals(task.key.getComponent())) {
- return taskView;
- }
- }
- }
- }
- }
-
- if (targets == null) {
- return null;
- }
- // Resolve the opening task id
- int openingTaskId = -1;
- for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == MODE_OPENING) {
- openingTaskId = target.taskId;
- break;
- }
- }
-
- // If there is no opening task id, fall back to the normal app icon launch animation
- if (openingTaskId == -1) {
- return null;
- }
-
- // If the opening task id is not currently visible in overview, then fall back to normal app
- // icon launch animation
- TaskView taskView = recentsView.getTaskView(openingTaskId);
- if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
- return null;
- }
- return taskView;
- }
-
- /**
* Composes the animations for a launch from the recents list if possible.
*/
private boolean composeRecentsLaunchAnimator(View v,
@@ -287,7 +232,8 @@
};
}
- target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets));
+ target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets)
+ .setDuration(RECENTS_LAUNCH_DURATION));
target.play(launcherAnim);
// Set the current animation first, before adding windowAnimEndListener. Setting current
@@ -299,83 +245,6 @@
}
/**
- * @return Animator that controls the window of the opening targets for the recents launch
- * animation.
- */
- private ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipLauncherChanges,
- RemoteAnimationTargetCompat[] targets) {
- final RecentsAnimationInterpolator recentsInterpolator = v.getRecentsInterpolator();
-
- Rect crop = new Rect();
- Matrix matrix = new Matrix();
-
- ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
- appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
- appAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
- appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- boolean isFirstFrame = true;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final Surface surface = getSurface(v);
- final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
- if (frameNumber == -1) {
- // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
- Log.w(TAG, "Failed to animate, surface got destroyed.");
- return;
- }
- final float percent = animation.getAnimatedFraction();
- TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
-
- float alphaDuration = 75;
- if (!skipLauncherChanges) {
- v.setScaleX(tw.taskScale);
- v.setScaleY(tw.taskScale);
- v.setTranslationX(tw.taskX);
- v.setTranslationY(tw.taskY);
- // Defer fading out the view until after the app window gets faded in
- v.setAlpha(getValue(1f, 0f, alphaDuration, alphaDuration,
- appAnimator.getDuration() * percent, Interpolators.LINEAR));
- }
-
- matrix.setScale(tw.winScale, tw.winScale);
- matrix.postTranslate(tw.winX, tw.winY);
- crop.set(tw.winCrop);
-
- // Fade in the app window.
- float alpha = getValue(0f, 1f, 0, alphaDuration,
- appAnimator.getDuration() * percent, Interpolators.LINEAR);
-
- TransactionCompat t = new TransactionCompat();
- for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
- t.setAlpha(target.leash, alpha);
-
- // TODO: This isn't correct at the beginning of the animation, but better
- // than nothing.
- matrix.postTranslate(target.position.x, target.position.y);
- t.setMatrix(target.leash, matrix);
- t.setWindowCrop(target.leash, crop);
-
- if (!skipLauncherChanges) {
- t.deferTransactionUntil(target.leash, surface, frameNumber);
- }
- }
- if (isFirstFrame) {
- t.show(target.leash);
- }
- }
- t.setEarlyWakeup();
- t.apply();
-
- matrix.reset();
- isFirstFrame = false;
- }
- });
- return appAnimator;
- }
-
- /**
* Content is everything on screen except the background and the floating view (if any).
*
* @param show If true: Animate the content so that it moves upwards and fades in.
@@ -401,9 +270,9 @@
ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
alpha.setDuration(217);
- alpha.setInterpolator(Interpolators.LINEAR);
+ alpha.setInterpolator(LINEAR);
ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
- transY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ transY.setInterpolator(AGGRESSIVE_EASE);
transY.setDuration(350);
launcherAnimator.play(alpha);
@@ -422,10 +291,10 @@
ObjectAnimator dragLayerAlpha = ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, alphas);
dragLayerAlpha.setDuration(217);
- dragLayerAlpha.setInterpolator(Interpolators.LINEAR);
+ dragLayerAlpha.setInterpolator(LINEAR);
ObjectAnimator dragLayerTransY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
trans);
- dragLayerTransY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ dragLayerTransY.setInterpolator(AGGRESSIVE_EASE);
dragLayerTransY.setDuration(350);
launcherAnimator.play(dragLayerAlpha);
@@ -517,8 +386,8 @@
boolean isBelowCenterY = lp.topMargin < centerY;
x.setDuration(isBelowCenterY ? APP_LAUNCH_DURATION : APP_LAUNCH_CURVED_DURATION);
y.setDuration(isBelowCenterY ? APP_LAUNCH_CURVED_DURATION : APP_LAUNCH_DURATION);
- x.setInterpolator(Interpolators.AGGRESSIVE_EASE);
- y.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ x.setInterpolator(AGGRESSIVE_EASE);
+ y.setInterpolator(AGGRESSIVE_EASE);
appIconAnimatorSet.play(x);
appIconAnimatorSet.play(y);
@@ -536,7 +405,7 @@
ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
alpha.setStartDelay(32);
alpha.setDuration(50);
- alpha.setInterpolator(Interpolators.LINEAR);
+ alpha.setInterpolator(LINEAR);
appIconAnimatorSet.play(alpha);
appIconAnimatorSet.addListener(new AnimatorListenerAdapter() {
@@ -571,11 +440,14 @@
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION);
- appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ // Fade alpha for the app window.
+ FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
+
boolean isFirstFrame = true;
@Override
- public void onAnimationUpdate(ValueAnimator animation) {
+ public void onUpdate(float percent) {
final Surface surface = getSurface(mFloatingView);
final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
if (frameNumber == -1) {
@@ -583,8 +455,7 @@
Log.w(TAG, "Failed to animate, surface got destroyed.");
return;
}
- final float percent = animation.getAnimatedFraction();
- final float easePercent = Interpolators.AGGRESSIVE_EASE.getInterpolation(percent);
+ final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent);
// Calculate app icon size.
float iconWidth = bounds.width() * mFloatingView.getScaleX();
@@ -609,11 +480,6 @@
float transY0 = floatingViewBounds[1] - offsetY;
matrix.postTranslate(transX0, transY0);
- // Fade in the app window.
- float alphaDuration = 60;
- float alpha = getValue(0f, 1f, 0, alphaDuration,
- appAnimator.getDuration() * percent, Interpolators.LINEAR);
-
// Animate the window crop so that it starts off as a square, and then reveals
// horizontally.
float cropHeight = deviceHeight * easePercent + deviceWidth * (1 - easePercent);
@@ -626,7 +492,7 @@
TransactionCompat t = new TransactionCompat();
for (RemoteAnimationTargetCompat target : targets) {
if (target.mode == MODE_OPENING) {
- t.setAlpha(target.leash, alpha);
+ t.setAlpha(target.leash, mAlpha.value);
// TODO: This isn't correct at the beginning of the animation, but better
// than nothing.
@@ -672,13 +538,7 @@
}
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
- int launcherTaskId = mLauncher.getTaskId();
- for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == mode && target.taskId == launcherTaskId) {
- return true;
- }
- }
- return false;
+ return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
}
/**
@@ -732,30 +592,23 @@
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+ closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ FloatProp mDx = new FloatProp(0, endX, 0, 350, AGGRESSIVE_EASE_IN_OUT);
+ FloatProp mScale = new FloatProp(1f, 0.8f, 0, 267, AGGRESSIVE_EASE);
+ FloatProp mAlpha = new FloatProp(1f, 0f, 0, 350, APP_CLOSE_ALPHA);
- closingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
boolean isFirstFrame = true;
@Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final float percent = animation.getAnimatedFraction();
- float currentPlayTime = percent * closingAnimator.getDuration();
-
- float scale = getValue(1f, 0.8f, 0, 267, currentPlayTime,
- Interpolators.AGGRESSIVE_EASE);
-
- float dX = getValue(0, endX, 0, 350, currentPlayTime,
- Interpolators.AGGRESSIVE_EASE_IN_OUT);
-
+ public void onUpdate(float percent) {
TransactionCompat t = new TransactionCompat();
for (RemoteAnimationTargetCompat app : targets) {
if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
- t.setAlpha(app.leash, getValue(1f, 0f, 0, 350, currentPlayTime,
- Interpolators.APP_CLOSE_ALPHA));
- matrix.setScale(scale, scale,
+ t.setAlpha(app.leash, mAlpha.value);
+ matrix.setScale(mScale.value, mScale.value,
app.sourceContainerBounds.centerX(),
app.sourceContainerBounds.centerY());
- matrix.postTranslate(dX, 0);
+ matrix.postTranslate(mDx.value, 0);
matrix.postTranslate(app.position.x, app.position.y);
t.setMatrix(app.leash, matrix);
}
@@ -774,6 +627,7 @@
isFirstFrame = false;
}
});
+
return closingAnimator;
}
@@ -834,16 +688,4 @@
return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
-
- /**
- * Helper method that allows us to get interpolated values for embedded
- * animations with a delay and/or different duration.
- */
- private static float getValue(float start, float end, float delay, float duration,
- float currentPlayTime, Interpolator i) {
- float time = Math.max(0, currentPlayTime - delay);
- float newPercent = Math.min(1f, time / duration);
- newPercent = i.getInterpolation(newPercent);
- return end * newPercent + start * (1 - newPercent);
- }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 84a60bd..4c9fd5a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -22,7 +22,6 @@
import android.animation.ValueAnimator;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.View;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
@@ -106,7 +105,7 @@
// Now figure out which direction scroll events the controller will start
// calling the callbacks.
- final int directionsToDetectScroll;
+ int directionsToDetectScroll = 0;
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
@@ -114,12 +113,19 @@
} else {
mTaskBeingDragged = null;
- View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage());
- if (view instanceof TaskView && mActivity.getDragLayer().isEventOverView(view, ev)) {
- // The tile can be dragged down to open the task.
- mTaskBeingDragged = (TaskView) view;
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- } else {
+ for (int i = 0; i < mRecentsView.getChildCount(); i++) {
+ TaskView view = mRecentsView.getPageAt(i);
+ if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
+ .isEventOverView(view, ev)) {
+ // The task can be dragged up to dismiss it,
+ // and down to open if it's the current page.
+ mTaskBeingDragged = view;
+ directionsToDetectScroll = i == mRecentsView.getCurrentPage()
+ ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
+ break;
+ }
+ }
+ if (mTaskBeingDragged == null) {
mNoIntercept = true;
return false;
}
@@ -142,10 +148,16 @@
return mDetector.onTouchEvent(ev);
}
- private void reInitAnimationController(boolean goingUp) {
+ private boolean reInitAnimationController(boolean goingUp) {
if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
// No need to init
- return;
+ return false;
+ }
+ int scrollDirections = mDetector.getScrollDirections();
+ if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
+ || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
+ // Trying to re-init in an unsupported direction.
+ return false;
}
if (mCurrentAnimation != null) {
mCurrentAnimation.setPlayFraction(0);
@@ -179,6 +191,7 @@
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
mProgressMultiplier = 1 / mEndDisplacement;
+ return true;
}
@Override
@@ -219,8 +232,7 @@
// Not allowed
goingToEnd = false;
} else {
- reInitAnimationController(goingUp);
- goingToEnd = true;
+ goingToEnd = reInitAnimationController(goingUp);
}
} else {
goingToEnd = true;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 19ef8ab..eb0be89 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,16 +15,29 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.SystemUiController;
@@ -32,12 +45,18 @@
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/**
* A simple activity to show the recently launched tasks
*/
public class RecentsActivity extends BaseDraggingActivity {
+ private Handler mUiHandler = new Handler(Looper.getMainLooper());
private RecentsRootView mRecentsRootView;
private FallbackRecentsView mFallbackRecentsView;
@@ -84,8 +103,49 @@
}
@Override
- public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
- return null;
+ public ActivityOptions getActivityLaunchOptions(final View v, boolean useDefaultLaunchOptions) {
+ if (useDefaultLaunchOptions || !(v instanceof TaskView)) {
+ return null;
+ }
+
+ final TaskView taskView = (TaskView) v;
+ RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler) {
+
+ @Override
+ public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ return composeRecentsLaunchAnimator(taskView, targetCompats);
+ }
+ };
+ return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
+ runner, RECENTS_LAUNCH_DURATION,
+ RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION));
+ }
+
+ /**
+ * Composes the animations for a launch from the recents list if possible.
+ */
+ private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
+ RemoteAnimationTargetCompat[] targets) {
+ AnimatorSet target = new AnimatorSet();
+ boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
+ target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets)
+ .setDuration(RECENTS_LAUNCH_DURATION));
+
+ // Found a visible recents task that matches the opening app, lets launch the app from there
+ if (activityClosing) {
+ Animator adjacentAnimation = mFallbackRecentsView
+ .createAdjacentPageAnimForTaskLaunch(taskView);
+ adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
+ adjacentAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFallbackRecentsView.resetTaskVisuals();
+ }
+ });
+ target.play(adjacentAnimation);
+ }
+ return target;
}
@Override
@@ -95,6 +155,7 @@
protected void onStart() {
super.onStart();
UiFactory.onStart(this);
+ mFallbackRecentsView.resetTaskVisuals();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 5bf1d07..c66f00f 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,25 +16,47 @@
package com.android.quickstep;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+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_OPENING;
+
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.Matrix;
+import android.graphics.Rect;
import android.os.UserHandle;
import android.util.Log;
+import android.view.Surface;
+import android.view.View;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
+import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
/**
* Contains helpful methods for retrieving data from {@link Task}s.
- * TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public class TaskUtils {
private static final String TAG = "TaskUtils";
+ /**
+ * TODO: remove this once we switch to getting the icon and label from IconCache.
+ */
public static CharSequence getTitle(Context context, Task task) {
LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context);
@@ -53,4 +75,148 @@
public static ComponentKey getComponentKeyForTask(Task.TaskKey taskKey) {
return new ComponentKey(taskKey.getComponent(), UserHandle.of(taskKey.userId));
}
+
+
+ /**
+ * Try to find a TaskView that corresponds with the component of the launched view.
+ *
+ * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
+ * Otherwise, we will assume we are using a normal app transition, but it's possible that the
+ * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
+ */
+ public static TaskView findTaskViewToLaunch(
+ BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
+ if (v instanceof TaskView) {
+ return (TaskView) v;
+ }
+ RecentsView recentsView = activity.getOverviewPanel();
+
+ // It's possible that the launched view can still be resolved to a visible task view, check
+ // the task id of the opening task and see if we can find a match.
+ if (v.getTag() instanceof ItemInfo) {
+ ItemInfo itemInfo = (ItemInfo) v.getTag();
+ ComponentName componentName = itemInfo.getTargetComponent();
+ if (componentName != null) {
+ for (int i = 0; i < recentsView.getChildCount(); i++) {
+ TaskView taskView = recentsView.getPageAt(i);
+ if (recentsView.isTaskViewVisible(taskView)) {
+ Task task = taskView.getTask();
+ if (componentName.equals(task.key.getComponent())) {
+ return taskView;
+ }
+ }
+ }
+ }
+ }
+
+ if (targets == null) {
+ return null;
+ }
+ // Resolve the opening task id
+ int openingTaskId = -1;
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == MODE_OPENING) {
+ openingTaskId = target.taskId;
+ break;
+ }
+ }
+
+ // If there is no opening task id, fall back to the normal app icon launch animation
+ if (openingTaskId == -1) {
+ return null;
+ }
+
+ // If the opening task id is not currently visible in overview, then fall back to normal app
+ // icon launch animation
+ TaskView taskView = recentsView.getTaskView(openingTaskId);
+ if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
+ return null;
+ }
+ return taskView;
+ }
+
+
+ /**
+ * @return Animator that controls the window of the opening targets for the recents launch
+ * animation.
+ */
+ public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+ RemoteAnimationTargetCompat[] targets) {
+ final RecentsAnimationInterpolator recentsInterpolator = v.getRecentsInterpolator();
+
+ Rect crop = new Rect();
+ Matrix matrix = new Matrix();
+
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+
+ // Defer fading out the view until after the app window gets faded in
+ FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
+ FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
+
+ boolean isFirstFrame = true;
+
+ @Override
+ public void onUpdate(float percent) {
+
+ final Surface surface = getSurface(v);
+ final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
+ if (frameNumber == -1) {
+ // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
+ Log.w(TAG, "Failed to animate, surface got destroyed.");
+ return;
+ }
+ TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
+
+ if (!skipViewChanges) {
+ v.setScaleX(tw.taskScale);
+ v.setScaleY(tw.taskScale);
+ v.setTranslationX(tw.taskX);
+ v.setTranslationY(tw.taskY);
+ v.setAlpha(mViewAlpha.value);
+ }
+
+ matrix.setScale(tw.winScale, tw.winScale);
+ matrix.postTranslate(tw.winX, tw.winY);
+ crop.set(tw.winCrop);
+
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ t.setAlpha(target.leash, mTaskAlpha.value);
+
+ // TODO: This isn't correct at the beginning of the animation, but better
+ // than nothing.
+ matrix.postTranslate(target.position.x, target.position.y);
+ t.setMatrix(target.leash, matrix);
+ t.setWindowCrop(target.leash, crop);
+
+ if (!skipViewChanges) {
+ t.deferTransactionUntil(target.leash, surface, frameNumber);
+ }
+ }
+ if (isFirstFrame) {
+ t.show(target.leash);
+ }
+ }
+ t.setEarlyWakeup();
+ t.apply();
+
+ matrix.reset();
+ isFirstFrame = false;
+ }
+ });
+ return appAnimator;
+ }
+
+ public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
+ int taskId, int mode) {
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == mode && target.taskId == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
new file mode 100644
index 0000000..e798d5c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.animation.ValueAnimator;
+import android.view.animation.Interpolator;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to update multiple values with different interpolators and durations during
+ * the same animation.
+ */
+public abstract class MultiValueUpdateListener implements ValueAnimator.AnimatorUpdateListener {
+
+ private final ArrayList<FloatProp> mAllProperties = new ArrayList<>();
+
+ @Override
+ public final void onAnimationUpdate(ValueAnimator animator) {
+ final float percent = animator.getAnimatedFraction();
+ final float currentPlayTime = percent * animator.getDuration();
+
+ for (int i = mAllProperties.size() - 1; i >= 0; i--) {
+ FloatProp prop = mAllProperties.get(i);
+ float time = Math.max(0, currentPlayTime - prop.mDelay);
+ float newPercent = Math.min(1f, time / prop.mDuration);
+ newPercent = prop.mInterpolator.getInterpolation(newPercent);
+ prop.value = prop.mEnd * newPercent + prop.mStart * (1 - newPercent);
+ }
+ onUpdate(percent);
+ }
+
+ public abstract void onUpdate(float percent);
+
+ public final class FloatProp {
+
+ public float value;
+
+ private final float mStart;
+ private final float mEnd;
+ private final float mDelay;
+ private final float mDuration;
+ private final Interpolator mInterpolator;
+
+ public FloatProp(float start, float end, float delay, float duration, Interpolator i) {
+ value = mStart = start;
+ mEnd = end;
+ mDelay = delay;
+ mDuration = duration;
+ mInterpolator = i;
+
+ mAllProperties.add(this);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7e81ba9..faaa40d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -659,14 +659,14 @@
int[] newScroll = new int[count];
getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView);
- int maxScrollDiff = 0;
- int lastPage = mIsRtl ? 0 : count - 1;
- if (getChildAt(lastPage) == taskView) {
- if (count > 1) {
- int secondLastPage = mIsRtl ? 1 : count - 2;
- maxScrollDiff = oldScroll[lastPage] - newScroll[secondLastPage];
- }
+ int scrollDiffPerPage = 0;
+ int leftmostPage = mIsRtl ? count -1 : 0;
+ int rightmostPage = mIsRtl ? 0 : count - 1;
+ if (count > 1) {
+ int secondRightmostPage = mIsRtl ? 1 : count - 2;
+ scrollDiffPerPage = oldScroll[rightmostPage] - oldScroll[secondRightmostPage];
}
+ int draggedIndex = indexOfChild(taskView);
boolean needsCurveUpdates = false;
for (int i = 0; i < count; i++) {
@@ -678,7 +678,26 @@
duration, LINEAR, anim);
}
} else {
- int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff;
+ // If we just take newScroll - oldScroll, everything to the right of dragged task
+ // translates to the left. We need to offset this in some cases:
+ // - In RTL, add page offset to all pages, since we want pages to move to the right
+ // Additionally, add a page offset if:
+ // - Current page is rightmost page (leftmost for RTL)
+ // - Dragging an adjacent page on the left side (right side for RTL)
+ int offset = mIsRtl ? scrollDiffPerPage : 0;
+ if (mCurrentPage == draggedIndex) {
+ int lastPage = mIsRtl ? leftmostPage : rightmostPage;
+ if (mCurrentPage == lastPage) {
+ offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
+ }
+ } else {
+ // Dragging an adjacent page.
+ int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR)
+ if (draggedIndex == negativeAdjacent) {
+ offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
+ }
+ }
+ int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff),
duration, ACCEL, anim);
@@ -710,9 +729,15 @@
TaskUtils.getComponentKeyForTask(task.key));
}
}
+ int pageToSnapTo = mCurrentPage;
+ if (draggedIndex < pageToSnapTo) {
+ pageToSnapTo -= 1;
+ }
removeView(taskView);
if (getChildCount() == 0) {
onAllTasksRemoved();
+ } else {
+ snapToPageImmediately(pageToSnapTo);
}
}
resetTaskVisuals();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cabccbf..ba96d4a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -105,7 +105,8 @@
* Indicates if the device has a debug build. Should only be used to store additional info or
* add extra logging and not for changing the app behavior.
*/
- public static final boolean IS_DEBUG_DEVICE = Build.TYPE.toLowerCase().contains("debug");
+ public static final boolean IS_DEBUG_DEVICE = Build.TYPE.toLowerCase().contains("debug")
+ || Build.TYPE.toLowerCase().equals("eng");
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 4b36ad9..703e4fd 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -194,6 +194,10 @@
mIgnoreSlopWhenSettling = ignoreSlop;
}
+ public int getScrollDirections() {
+ return mScrollConditions;
+ }
+
private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
// reject cases where the angle or slop condition is not met.
if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)