Exploring transitions to/from Recents.
- refactored hwlayers and change view property animations to use a reference counted trigger
- cleaned up RecentsConfiguration, and move it into classes using it
- moved task bar animations back into TaskBarView
- refactoring enter/exit animations to use an animation context
Change-Id: Ia66b622b094f22145c2fab07c2a9bdfd62344be2
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5df5e4d..9e4386f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -28,9 +28,11 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -45,10 +47,10 @@
public void onTaskAppInfoClicked(TaskView tv);
public void onTaskFocused(TaskView tv);
public void onTaskDismissed(TaskView tv);
-
- // public void onTaskViewReboundToTask(TaskView tv, Task t);
}
+ RecentsConfiguration mConfig;
+
int mDim;
int mMaxDim;
TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -59,11 +61,21 @@
boolean mClipViewInStack;
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
+ Rect mTmpRect = new Rect();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
TaskViewCallbacks mCb;
+ // Optimizations
+ ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateDimOverlayFromScale();
+ }
+ };
+
public TaskView(Context context) {
this(context, null);
@@ -79,13 +91,13 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
}
@Override
protected void onFinishInflate() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mMaxDim = config.taskStackMaxDim;
+ mMaxDim = mConfig.taskStackMaxDim;
// By default, all views are clipped to other views in their stack
mClipViewInStack = true;
@@ -104,8 +116,7 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Update the rounded rect clip path
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
mRoundedRectClipPath.reset();
mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
radius, radius, Path.Direction.CW);
@@ -113,7 +124,7 @@
// Update the outline
Outline o = new Outline();
o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- config.taskViewShadowOutlineBottomInsetPx, radius);
+ mConfig.taskViewShadowOutlineBottomInsetPx, radius);
setOutline(o);
}
@@ -141,7 +152,10 @@
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
+ "duration: " + duration, Console.AnsiPurple);
+ }
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
@@ -164,15 +178,10 @@
.scaleX(toTransform.scale)
.scaleY(toTransform.scale)
.alpha(toTransform.alpha)
+ .setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .withLayer()
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateDimOverlayFromScale();
- }
- })
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setUpdateListener(mUpdateDimListener)
.start();
} else {
setTranslationY(toTransform.translationY);
@@ -197,6 +206,7 @@
setScaleX(1f);
setScaleY(1f);
setAlpha(1f);
+ mDim = 0;
invalidate();
}
@@ -221,40 +231,103 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateOnEnterRecents() {
- mBarView.setVisibility(View.INVISIBLE);
+ public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (isTaskViewFrontMost) {
+ // Hide the task view as we are going to animate the full screenshot into view
+ // and then replace it with this view once we are done
+ setVisibility(View.INVISIBLE);
+ // Also hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ } else {
+ // Top align the task views
+ setTranslationY(offsetY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (isTaskViewFrontMost) {
+ // Hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Move the task view off screen (below) so we can animate it in
+ setTranslationY(offscreenY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.setVisibility(View.VISIBLE);
- mBarView.setTranslationY(-mBarView.getMeasuredHeight());
- mBarView.animate()
- .translationY(0)
- .setStartDelay(config.taskBarEnterAnimDelay)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskBarEnterAnimDuration)
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ TaskViewTransform transform = ctx.transform;
+
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (ctx.isFrontMost) {
+ // Animate the full screenshot down first, before swapping with this task view
+ ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
+ @Override
+ public void run() {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(0);
+ setVisibility(View.VISIBLE);
+ }
+ });
+ } else {
+ // Animate the tasks down behind the full screenshot
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.linearOutSlowInInterpolator)
+ .setDuration(475)
+ .withLayer()
+ .start();
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (ctx.isFrontMost) {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(-1);
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Animate the tasks up
+ int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
+ int delay = mConfig.taskBarEnterAnimDelay +
+ frontIndex * mConfig.taskViewEnterFromHomeDelay;
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(delay)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
+ /** Animates this task view as it leaves recents */
+ public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ animate()
+ .translationY(ctx.offscreenTranslationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
.withLayer()
+ .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
+ ctx.postAnimationTrigger.increment();
}
/** Animates this task view as it exits recents */
- public void animateOnLeavingRecents(final Runnable r) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.animate()
- .translationY(-mBarView.getMeasuredHeight())
- .setStartDelay(0)
- .setInterpolator(config.fastOutLinearInInterpolator)
- .setDuration(config.taskBarExitAnimDuration)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- post(r);
- }
- })
- .start();
+ public void animateOnLaunchingTask(final Runnable r) {
+ mBarView.animateOnLaunchingTask(r);
}
/** Animates the deletion of this task view */
@@ -262,20 +335,24 @@
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- animate().translationX(config.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskViewRemoveAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewRemoveAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
- post(r);
+ // We just throw this into a runnable because starting a view property
+ // animation using layers can cause inconsisten results if we try and
+ // update the layers while the animation is running. In some cases,
+ // the runnabled passed in may start an animation which also uses layers
+ // so we defer all this by posting this.
+ r.run();
// Re-enable clipping with the stack (we will reuse this view)
- setClipViewInStack(false);
+ setClipViewInStack(true);
}
})
.start();
@@ -293,11 +370,13 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mBarView.disableHwLayers();
}
/**
@@ -305,7 +384,7 @@
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack;
+ return mClipViewInStack && (getVisibility() == View.VISIBLE);
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -313,9 +392,8 @@
if (clip != mClipViewInStack) {
mClipViewInStack = clip;
if (getParent() instanceof View) {
- Rect r = new Rect();
- getHitRect(r);
- ((View) getParent()).invalidate(r);
+ getHitRect(mTmpRect);
+ ((View) getParent()).invalidate(mTmpRect);
}
}
}
@@ -391,8 +469,7 @@
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.developerOptionsEnabled) {
+ if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(this);
}
}