Merge "Adding task open animation in fallback activity" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index ad0b734..8fbb512 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,
@@ -285,7 +230,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
@@ -297,83 +243,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.
@@ -399,9 +268,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);
@@ -420,10 +289,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);
@@ -515,8 +384,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);
@@ -534,7 +403,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() {
@@ -569,11 +438,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) {
@@ -581,8 +453,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();
@@ -607,11 +478,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);
@@ -624,7 +490,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.
@@ -670,13 +536,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);
}
/**
@@ -730,30 +590,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);
}
@@ -772,6 +625,7 @@
isFirstFrame = false;
}
});
+
return closingAnimator;
}
@@ -832,16 +686,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/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);
+ }
+ }
+}