Refactoring a few things with the full screen overlay and enter animation.

- Fixing issue with configuration change triggering enter animation
- Using a proper target task rect when animating into a group

Change-Id: I856d29a663f5e8c000ca22fd1025c09f65032893
This commit is contained in:
Winson Chung
2014-07-08 14:39:08 -07:00
parent 0a4a89f622
commit 6057c91563
7 changed files with 169 additions and 94 deletions

View File

@@ -28,6 +28,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.view.View;
import com.android.systemui.R;
@@ -166,6 +167,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
public void onConfigurationChanged(Configuration newConfig) {
mConfig = RecentsConfiguration.reinitialize(mContext);
mConfig.updateOnConfigurationChange();
mWindowRect = mSystemServicesProxy.getWindowRect();
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
sLastScreenshot = null;
@@ -271,11 +273,12 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
* screenshot, then we will use that for the transition.
*/
ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask) {
// Recycle the last screenshot
consumeLastScreenshot();
// Take the full screenshot
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
// Recycle the last screenshot
consumeLastScreenshot();
// Take the full screenshot
sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
if (sLastScreenshot != null) {
return ActivityOptions.makeCustomAnimation(mContext,
@@ -287,21 +290,22 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
// If the screenshot fails, then load the first task thumbnail and use that
Bitmap firstThumbnail = mSystemServicesProxy.getTaskThumbnail(topTask.id);
if (firstThumbnail != null) {
Rect taskRect = getThumbnailTransitionRect(topTask.id);
// Update the destination rect
Rect toTaskRect = getThumbnailTransitionRect(topTask.id);
// Create the new thumbnail for the animation down
// XXX: We should find a way to optimize this so we don't need to create a new bitmap
Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
Bitmap thumbnail = Bitmap.createBitmap(toTaskRect.width(), toTaskRect.height(),
Bitmap.Config.ARGB_8888);
int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
Canvas c = new Canvas(thumbnail);
c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
new Rect(0, 0, taskRect.width(), taskRect.height()), null);
new Rect(0, 0, toTaskRect.width(), toTaskRect.height()), null);
c.setBitmap(null);
// Recycle the old thumbnail
firstThumbnail.recycle();
return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
thumbnail, taskRect.left, taskRect.top, this);
thumbnail, toTaskRect.left, toTaskRect.top, this);
}
// If both the screenshot and thumbnail fails, then just fall back to the default transition

View File

@@ -206,6 +206,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
mConfig.launchedWithNoRecentTasks = !root.hasTasks();
mConfig.launchedToTaskId = launchIntent.getIntExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1);
// Add the default no-recents layout
if (mEmptyView == null) {
@@ -372,11 +374,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Update the recent tasks
updateRecentsTasks(getIntent());
// Update if we are getting a configuration change
if (savedInstanceState != null) {
onConfigurationChange();
}
// Register the broadcast receiver to handle messages when the screen is turned off
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -402,6 +399,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mFullScreenOverlayView.setCallbacks(this);
mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
}
// Update if we are getting a configuration change
if (savedInstanceState != null) {
mConfig.updateOnConfigurationChange();
onConfigurationChange();
}
}
void onConfigurationChange() {
@@ -416,6 +419,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if (Console.Enabled) {
Console.logDivider(Constants.Log.App.SystemUIHandshake);
@@ -615,18 +619,14 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/
@Override
public void onEnterAnimationComplete(boolean canceled) {
if (!canceled) {
// Reset the full screenshot transition view
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
mFullScreenOverlayView.reset();
}
public void onEnterAnimationComplete() {
// Reset the full screenshot transition view
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
mFullScreenOverlayView.reset();
// XXX: We should clean up the screenshot in this case as well, but it needs to happen
// after to animate up
// Recycle the full screen screenshot
AlternateRecentsComponent.consumeLastScreenshot();
}
// Recycle the full screen screenshot
AlternateRecentsComponent.consumeLastScreenshot();
}
/**** RecentsView.RecentsViewCallbacks Implementation ****/

View File

@@ -100,6 +100,7 @@ public class RecentsConfiguration {
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromAppWithScreenshot;
public boolean launchedFromHome;
public int launchedToTaskId;
/** Dev options */
public boolean developerOptionsEnabled;
@@ -255,6 +256,7 @@ public class RecentsConfiguration {
launchedFromAppWithThumbnail = false;
launchedFromAppWithScreenshot = false;
launchedFromHome = false;
launchedToTaskId = -1;
}
/** Returns whether the search bar app widget exists. */

View File

@@ -23,16 +23,20 @@ import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Console;
/**
@@ -43,7 +47,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
/** The FullscreenTransitionOverlayView callbacks */
public interface FullScreenTransitionViewCallbacks {
void onEnterAnimationComplete(boolean canceled);
void onEnterAnimationComplete();
}
RecentsConfiguration mConfig;
@@ -53,6 +57,11 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
ImageView mScreenshotView;
Rect mClipRect = new Rect();
Paint mLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
boolean mIsAnimating;
AnimatorSet mEnterAnimation;
@@ -73,8 +82,10 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mMaxDim = mConfig.taskStackMaxDim;
setClipTop(getClipTop());
setClipBottom(getClipBottom());
setDim(getDim());
setWillNotDraw(false);
}
@@ -91,7 +102,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
/** Sets the top clip */
public void setClipTop(int clip) {
mClipRect.top = clip;
postInvalidateOnAnimation();
setClipBounds(mClipRect);
}
/** Gets the top clip */
@@ -102,7 +113,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
/** Sets the bottom clip */
public void setClipBottom(int clip) {
mClipRect.bottom = clip;
postInvalidateOnAnimation();
setClipBounds(mClipRect);
}
/** Gets the top clip */
@@ -110,20 +121,28 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
return mClipRect.bottom;
}
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
/*
int inverse = 255 - mDim;
mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
mLayerPaint.setColorFilter(mDimColorFilter);
setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
*/
}
/** Returns the current dim. */
public int getDim() {
return mDim;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
@Override
public void draw(Canvas canvas) {
int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
super.draw(canvas);
canvas.restoreToCount(restoreCount);
}
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -140,6 +159,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
setClipTop(0);
setClipBottom(getMeasuredHeight());
setDim(0);
setTranslationY(0f);
setScaleX(1f);
setScaleY(1f);
@@ -153,7 +173,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
/** Resets the transition view */
public void reset() {
setVisibility(View.INVISIBLE);
setVisibility(View.GONE);
mScreenshotView.setImageDrawable(null);
}
@@ -172,12 +192,20 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
}
// Calculate the bottom clip
Rect taskRect = ctx.taskRect;
float scale = (float) taskRect.width() / getMeasuredWidth();
Rect targetTaskRect = ctx.targetTaskTransform.rect;
float scale = (float) targetTaskRect.width() / getMeasuredWidth();
float scaleYOffset = ((1f - scale) * getMeasuredHeight()) / 2;
float scaledTopInset = (int) (scale * mConfig.systemInsets.top);
int translationY = (int) -scaleYOffset + (int) (mConfig.systemInsets.top - scaledTopInset) + taskRect.top;
int clipBottom = mConfig.systemInsets.top + (int) (taskRect.height() / scale);
int translationY = (int) -scaleYOffset + (int) (mConfig.systemInsets.top - scaledTopInset)
+ targetTaskRect.top;
int clipBottom = mConfig.systemInsets.top + (int) (targetTaskRect.height() / scale);
// Calculate the dim
float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
float scaleRange = 1f - minScale;
float dim = (1f - ctx.targetTaskTransform.scale) / scaleRange;
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
int toDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
// Enable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
@@ -187,31 +215,39 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Notify any callbacks
mCb.onEnterAnimationComplete(false);
// Run the given post-anim runnable
postAnimRunnable.run();
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on this view
setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
post(new Runnable() {
@Override
public void run() {
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on this view
setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
// Notify any callbacks
mCb.onEnterAnimationComplete();
// Run the given post-anim runnable
postAnimRunnable.run();
if (Console.Enabled) {
Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
}
if (Console.Enabled) {
Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
}
}
});
}
});
// XXX: Translation y should be negative initially to simulate moving from the top of the screen?
mEnterAnimation.setStartDelay(0);
mEnterAnimation.setDuration(475);
mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
mEnterAnimation.playTogether(
ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
// ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
ObjectAnimator.ofInt(this, "dim", toDim),
ObjectAnimator.ofFloat(this, "translationY", translationY),
ObjectAnimator.ofFloat(this, "scaleX", scale),
ObjectAnimator.ofFloat(this, "scaleY", scale)
);
setClipTop(mConfig.systemInsets.top);
mEnterAnimation.start();
mIsAnimating = true;
@@ -226,19 +262,27 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
mEnterAnimation.cancel();
}
// Enable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
// Compose the animation
mEnterAnimation = new AnimatorSet();
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Notify any callbacks
mCb.onEnterAnimationComplete(true);
// Run the given post-anim runnable
postAnimRunnable.run();
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
post(new Runnable() {
@Override
public void run() {
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
// Notify any callbacks
mCb.onEnterAnimationComplete();
// Run the given post-anim runnable
postAnimRunnable.run();
}
});
}
});
mEnterAnimation.setDuration(475);
@@ -246,6 +290,7 @@ public class FullscreenTransitionOverlayView extends FrameLayout {
mEnterAnimation.playTogether(
ObjectAnimator.ofInt(this, "clipTop", 0),
ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
ObjectAnimator.ofInt(this, "dim", 0),
ObjectAnimator.ofFloat(this, "translationY", 0f),
ObjectAnimator.ofFloat(this, "scaleX", 1f),
ObjectAnimator.ofFloat(this, "scaleY", 1f)

View File

@@ -25,6 +25,7 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -813,24 +814,47 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
if (mStack.getTaskCount() > 0) {
// Animate all the task views into view
TaskViewTransform transform = new TaskViewTransform();
TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
mStack.getGroupIndexForTask(mStack.getFrontMostTask(), groupTaskIndex);
mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
getInitialStackScroll(), transform);
ctx.taskRect = transform.rect;
ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
// Find the target task with the specified id
ArrayList<Task> tasks = mStack.getTasks();
Task targetTask = null;
int targetTaskId = mConfig.launchedToTaskId;
if (targetTaskId != -1) {
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
if (t.key.id == targetTaskId) {
targetTask = t;
break;
}
}
}
// Find the group and task index of the target task
if (targetTask != null) {
mStack.getGroupIndexForTask(targetTask, groupTaskIndex);
ctx.targetTaskTransform = new TaskViewTransform();
mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex,
groupTaskIndex.taskIndex, getStackScroll(), ctx.targetTaskTransform);
Rect taskStackBounds = new Rect();
mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
ctx.targetTaskTransform.rect.offset(taskStackBounds.left, taskStackBounds.top);
}
}
// Animate all the task views into view
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
ctx.stackViewIndex = i;
ctx.stackViewCount = childCount;
ctx.isFrontMost = (i == (getChildCount() - 1));
ctx.transform = new TaskViewTransform();
ctx.currentTaskTransform = new TaskViewTransform();
ctx.currentStackViewIndex = i;
ctx.currentStackViewCount = childCount;
ctx.isCurrentTaskFrontMost = (i == (getChildCount() - 1));
mStack.getGroupIndexForTask(tv.getTask(), groupTaskIndex);
mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
getStackScroll(), ctx.transform);
getStackScroll(), ctx.currentTaskTransform);
tv.startEnterRecentsAnimation(ctx);
}

View File

@@ -52,7 +52,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
int mDim;
int mMaxDim;
TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
Task mTask;
boolean mTaskDataLoaded;
@@ -279,12 +279,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Animates this task view as it enters recents */
public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
TaskViewTransform transform = ctx.transform;
TaskViewTransform transform = ctx.currentTaskTransform;
if (mConfig.launchedFromAppWithScreenshot) {
if (ctx.isFrontMost) {
if (ctx.isCurrentTaskFrontMost) {
// Animate the full screenshot down first, before swapping with this task view
ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
@Override
public void run() {
// Animate the task bar of the first task view
@@ -318,7 +318,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
ctx.postAnimationTrigger.increment();
} else if (mConfig.launchedFromAppWithThumbnail) {
if (ctx.isFrontMost) {
if (ctx.isCurrentTaskFrontMost) {
// Animate the task bar of the first task view
mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
@@ -342,7 +342,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
} else if (mConfig.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
int delay = mConfig.taskBarEnterAnimDelay +
frontIndex * mConfig.taskViewEnterFromHomeDelay;
if (Constants.DebugFlags.App.EnableShadows) {

View File

@@ -16,7 +16,6 @@
package com.android.systemui.recents.views;
import android.graphics.Rect;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/* Common code related to view animations */
@@ -25,25 +24,26 @@ public class ViewAnimation {
/* The animation context for a task view animation into Recents */
public static class TaskViewEnterContext {
// The full screenshot view that we are animating down
FullscreenTransitionOverlayView fullScreenshot;
// The transform of the current task view
TaskViewTransform transform;
// The stack rect that the transform is relative to
Rect stackRectSansPeek;
// The task rect
Rect taskRect;
// The view index of the current task view
int stackViewIndex;
// The total number of task views
int stackViewCount;
// Whether this is the front most task view
boolean isFrontMost;
FullscreenTransitionOverlayView fullScreenshotView;
// The transform of the target task view that we are animating into
TaskViewTransform targetTaskTransform;
// A trigger to run some logic when all the animations complete. This works around the fact
// that it is difficult to coordinate ViewPropertyAnimators
ReferenceCountedTrigger postAnimationTrigger;
// These following properties are updated for each task view we start the enter animation on
// The transform of the current task view
TaskViewTransform currentTaskTransform;
// Whether this is the front most task view
boolean isCurrentTaskFrontMost;
// The view index of the current task view
int currentStackViewIndex;
// The total number of task views
int currentStackViewCount;
public TaskViewEnterContext(FullscreenTransitionOverlayView fss, ReferenceCountedTrigger t) {
fullScreenshot = fss;
fullScreenshotView = fss;
postAnimationTrigger = t;
}
}
@@ -53,7 +53,7 @@ public class ViewAnimation {
// A trigger to run some logic when all the animations complete. This works around the fact
// that it is difficult to coordinate ViewPropertyAnimators
ReferenceCountedTrigger postAnimationTrigger;
// The translationY to apply to a TaskView to move it off screen
// The translationY to apply to a TaskView to move it off the bottom of the task stack
int offscreenTranslationY;
public TaskViewExitContext(ReferenceCountedTrigger t) {