Updating the stack layout to use a parameterized curve.

- Fixing issue with search box not being layered on top of the task stack view (Bug 16643875)
- Fixing issue with there being no animation when dismissing recents while the stack is scrolling.

Change-Id: I990f3c527de655d62fbf8a4539dcbaed3ed422c8
This commit is contained in:
Winson Chung
2014-07-31 18:36:25 -07:00
committed by Winson Chung
parent 7c3a95633d
commit 012ef36a6c
14 changed files with 629 additions and 436 deletions

View File

@ -201,8 +201,8 @@
<!-- The min translation in the Z index for the last task. -->
<dimen name="recents_task_view_z_min">5dp</dimen>
<!-- The translation in the Z index for each task above the last task. -->
<dimen name="recents_task_view_z_increment">10dp</dimen>
<!-- The max translation in the Z index for the last task. -->
<dimen name="recents_task_view_z_max">65dp</dimen>
<!-- The amount to translate when animating the removal of a task. -->
<dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>

View File

@ -105,7 +105,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
public void onStart() {
// Do nothing
// Initialize some static datastructures
TaskStackViewLayoutAlgorithm.initializeCurve();
}
public void onBootCompleted() {
@ -322,7 +323,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mSystemInsets.bottom;
tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
tsv.setStackScrollToInitialState();
tsv.getScroller().setStackScrollToInitialState();
// Find the running task in the TaskStack
Task task = null;
@ -344,7 +345,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
// Get the transform for the running task
mTmpTransform = algo.getStackTransform(task, tsv.getStackScroll(), mTmpTransform);
mTmpTransform = algo.getStackTransform(task, tsv.getScroller().getStackScroll(), mTmpTransform, null);
return new Rect(mTmpTransform.rect);
}

View File

@ -25,8 +25,6 @@ public class Constants {
public static final boolean Verbose = false;
public static class App {
// Enables the simulated task affiliations
public static final boolean EnableSimulatedTaskGroups = false;
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
@ -43,11 +41,15 @@ public class Constants {
public static final boolean EnableShadows = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// For debugging, this enables us to create mock recents tasks
// Enables the simulated task affiliations
public static final boolean EnableSimulatedTaskGroups = false;
// Defines the number of mock task affiliations per group
public static final int TaskAffiliationsGroupCount = 12;
// Enables us to create mock recents tasks
public static final boolean EnableSystemServicesProxy = false;
// For debugging, this defines the number of mock recents packages to create
// Defines the number of mock recents packages to create
public static final int SystemServicesProxyMockPackageCount = 3;
// For debugging, this defines the number of mock recents tasks to create
// Defines the number of mock recents tasks to create
public static final int SystemServicesProxyMockTaskCount = 100;
}
}

View File

@ -76,7 +76,7 @@ public class RecentsConfiguration {
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
public int taskViewTranslationZIncrementPx;
public int taskViewTranslationZMaxPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
public int taskViewAffiliateGroupEnterOffsetPx;
@ -208,8 +208,7 @@ public class RecentsConfiguration {
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
taskViewTranslationZIncrementPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
taskViewAffiliateGroupEnterOffsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);

View File

@ -337,7 +337,7 @@ public class TaskStack {
String prevPackage = "";
int prevAffiliation = -1;
Random r = new Random();
int groupCountDown = 5;
int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
String packageName = t.key.baseIntent.getComponent().getPackageName();
@ -352,7 +352,7 @@ public class TaskStack {
addGroup(group);
prevAffiliation = affiliation;
prevPackage = packageName;
groupCountDown = 5;
groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
}
group.addTask(t);
taskMap.put(t.key, t);

View File

@ -26,6 +26,7 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import com.android.systemui.R;
import com.android.systemui.recents.RecentsConfiguration;
import java.util.ArrayList;
@ -42,11 +43,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
final static int sCornerRectSize = 50;
RecentsConfiguration mConfig;
DebugOverlayViewCallbacks mCb;
ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
String mText;
Paint mDebugOutline = new Paint();
Paint mTmpPaint = new Paint();
Rect mTmpRect = new Rect();
boolean mEnabled = true;
SeekBar mPrimarySeekBar;
@ -66,6 +70,7 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mDebugOutline.setColor(0xFFff0000);
mDebugOutline.setStyle(Paint.Style.STROKE);
mDebugOutline.setStrokeWidth(8f);
@ -124,6 +129,12 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
invalidate();
}
/** Sets the debug text at the bottom of the screen. */
void setText(String message) {
mText = message;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@ -145,6 +156,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
mTmpPaint.setColor(r.second);
canvas.drawRect(r.first, mTmpPaint);
}
// Draw the text
if (mText != null && mText.length() > 0) {
mTmpPaint.setColor(0xFFff0000);
mTmpPaint.setTextSize(60);
mTmpPaint.getTextBounds(mText, 0, 1, mTmpRect);
canvas.drawText(mText, 10f, getMeasuredHeight() - mTmpRect.height() - mConfig.systemInsets.bottom, mTmpPaint);
}
}
}

View File

@ -225,6 +225,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void setSearchBarVisibility(int visibility) {
if (mSearchBar != null) {
mSearchBar.setVisibility(visibility);
// Always bring the search bar to the top
mSearchBar.bringToFront();
}
}
@ -364,17 +366,17 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
View sourceView = tv;
int offsetX = 0;
int offsetY = 0;
int stackScroll = stackView.getStackScroll();
float stackScroll = stackView.getScroller().getStackScroll();
if (tv == null) {
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
offsetX = transform.rect.left;
offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
}
// Compute the thumbnail to scale up from

View File

@ -126,28 +126,6 @@ class TaskBarView extends FrameLayout {
mIsFullscreen = isFullscreen;
}
/** Synchronizes this bar view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
if (duration > 0 && (mDismissButton.getVisibility() == View.VISIBLE)) {
ViewPropertyAnimator anim = mDismissButton.animate();
// Animate to the final state
if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
anim.alpha(toTransform.dismissAlpha)
.setStartDelay(0)
.setDuration(duration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
}
} else {
// Set the changed properties
if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
mDismissButton.setAlpha(toTransform.dismissAlpha);
}
}
}
@Override
public boolean hasOverlappingRendering() {
return false;

View File

@ -16,25 +16,17 @@
package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.OverScroller;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@ -47,8 +39,8 @@ import java.util.HashSet;
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
RecentsPackageMonitor.PackageCallbacks {
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
@ -64,8 +56,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
RecentsConfiguration mConfig;
TaskStack mStack;
TaskStackViewLayoutAlgorithm mStackAlgorithm;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewFilterAlgorithm mFilterAlgorithm;
TaskStackViewScroller mStackScroller;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
@ -73,18 +66,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
DozeTrigger mUIDozeTrigger;
DebugOverlayView mDebugOverlay;
Rect mTaskStackBounds = new Rect();
// The virtual stack scroll that we use for the card layout
int mStackScroll;
int mMinScroll;
int mMaxScroll;
int mStashedScroll;
int mFocusedTaskIndex = -1;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
// Optimizations
ReferenceCountedTrigger mHwLayersTrigger;
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true;
@ -114,12 +98,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mConfig = RecentsConfiguration.getInstance();
mStack = stack;
mStack.setCallbacks(this);
mScroller = new OverScroller(context);
mTouchHandler = new TaskStackViewTouchHandler(context, this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
mStackAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
@Override
public void run() {
@ -149,7 +134,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
void requestSynchronizeStackViewsWithModel(int duration) {
if (!mStackViewsDirty) {
invalidate(mStackAlgorithm.mStackRect);
invalidate();
mStackViewsDirty = true;
}
if (mAwaitingFirstLayout) {
@ -174,7 +159,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Returns the stack algorithm for this task stack. */
public TaskStackViewLayoutAlgorithm getStackAlgorithm() {
return mStackAlgorithm;
return mLayoutAlgorithm;
}
/**
@ -182,7 +167,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks,
int stackScroll,
float stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
// XXX: We should be intelligent about where to look for the visible stack range using the
@ -207,8 +192,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Update the stack transforms
TaskViewTransform prevTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
TaskViewTransform transform = mStackAlgorithm.getStackTransform(tasks.get(i), stackScroll, taskTransforms.get(i));
TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(tasks.get(i),
stackScroll, taskTransforms.get(i), prevTransform);
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@ -228,8 +215,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
mStackAlgorithm.mViewRect.bottom);
mLayoutAlgorithm.mViewRect.bottom);
}
prevTransform = transform;
}
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
@ -243,7 +231,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
* call is less optimal than calling updateStackTransforms directly.
*/
private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
int stackScroll,
float stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
@ -257,9 +245,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (mStackViewsDirty) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
int stackScroll = getStackScroll();
float stackScroll = mStackScroller.getStackScroll();
int[] visibleRange = mTmpVisibleRange;
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
stackScroll, visibleRange, false);
if (mDebugOverlay != null) {
mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
}
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
@ -288,11 +280,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (mStackViewsAnimationDuration > 0) {
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
if (transform.t < 0) {
mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
if (Float.compare(transform.p, 0f) <= 0) {
mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
} else {
int nextTaskStackScroll = mStackAlgorithm.getStackScrollForTaskIndex(task, 1);
mStackAlgorithm.getStackTransform(nextTaskStackScroll, stackScroll, mTmpTransform);
mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
}
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
@ -357,143 +348,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mTaskStackBounds.set(r);
}
/** Sets the current stack scroll */
public void setStackScroll(int value) {
mStackScroll = value;
mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
}
/** Sets the current stack scroll without synchronizing the stack view with the model */
public void setStackScrollRaw(int value) {
mStackScroll = value;
mUIDozeTrigger.poke();
}
/** Sets the current stack scroll to the initial state when you first enter recents */
public void setStackScrollToInitialState() {
setStackScroll(getInitialStackScroll());
}
/** Computes the initial stack scroll for the stack. */
int getInitialStackScroll() {
if (mStack.getTaskCount() > 2) {
return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
}
return mMaxScroll;
}
/** Gets the current stack scroll */
public int getStackScroll() {
return mStackScroll;
}
/** Animates the stack scroll into bounds */
ObjectAnimator animateBoundScroll() {
int curScroll = getStackScroll();
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
if (newScroll != curScroll) {
// Start a new scroll animation
animateScroll(curScroll, newScroll, null);
}
return mScrollAnimator;
}
/** Animates the stack scroll */
void animateScroll(int curScroll, int newScroll, final Runnable postRunnable) {
// Abort any current animations
abortScroller();
abortBoundScrollAnimation();
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
curScroll, 250));
mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setStackScroll((Integer) animation.getAnimatedValue());
}
});
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (postRunnable != null) {
postRunnable.run();
}
mScrollAnimator.removeAllListeners();
}
});
mScrollAnimator.start();
}
/** Aborts any current stack scrolls */
void abortBoundScrollAnimation() {
if (mScrollAnimator != null) {
mScrollAnimator.cancel();
}
}
/** Aborts the scroller and any current fling */
void abortScroller() {
if (!mScroller.isFinished()) {
// Abort the scroller
mScroller.abortAnimation();
}
}
/** Bounds the current scroll if necessary */
public boolean boundScroll() {
int curScroll = getStackScroll();
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
if (newScroll != curScroll) {
setStackScroll(newScroll);
return true;
}
return false;
}
/**
* Bounds the current scroll if necessary, but does not synchronize the stack view with the
* model.
*/
public boolean boundScrollRaw() {
int curScroll = getStackScroll();
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
if (newScroll != curScroll) {
setStackScrollRaw(newScroll);
return true;
}
return false;
}
/** Returns the amount that the scroll is out of bounds */
int getScrollAmountOutOfBounds(int scroll) {
if (scroll < mMinScroll) {
return mMinScroll - scroll;
} else if (scroll > mMaxScroll) {
return scroll - mMaxScroll;
}
return 0;
}
/** Returns whether the specified scroll is out of bounds */
boolean isScrollOutOfBounds() {
return getScrollAmountOutOfBounds(mStackScroll) != 0;
}
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
mStackAlgorithm.computeMinMaxScroll(mStack.getTasks());
mMinScroll = mStackAlgorithm.mMinScroll;
mMaxScroll = mStackAlgorithm.mMaxScroll;
mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
// Debug logging
if (boundScrollToNewMinMax) {
boundScroll();
mStackScroller.boundScroll();
}
}
/** Returns the scroller. */
public TaskStackViewScroller getScroller() {
return mStackScroller;
}
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
@ -520,10 +390,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (scrollToNewPosition) {
// Scroll the view into position
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll,
mStackAlgorithm.getStackScrollForTaskIndex(t)));
animateScroll(getStackScroll(), newScroll, postScrollRunnable);
// XXX: We probably want this to be centered in view instead of p = 0f
float newScroll = mStackScroller.getBoundedStackScroll(
mLayoutAlgorithm.getStackScrollForTaskIndex(t));
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
} else {
if (postScrollRunnable != null) {
postScrollRunnable.run();
@ -558,24 +428,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
invalidate();
}
}
@Override
public void dispatchDraw(Canvas canvas) {
// Synchronize the views
if (synchronizeStackViewsWithModel()) {
clipTaskViews();
}
super.dispatchDraw(canvas);
mStackScroller.computeScroll();
}
/** Computes the stack and task rects */
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the rects in the stack algorithm
mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
updateMinMaxScroll(false);
@ -598,7 +461,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
if (mAwaitingFirstLayout) {
setStackScrollToInitialState();
mStackScroller.setStackScrollToInitialState();
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
}
@ -611,9 +474,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
tv.measure(widthMeasureSpec, heightMeasureSpec);
} else {
tv.measure(
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.width(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.height() +
tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
}
}
@ -635,8 +498,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (tv.isFullScreenView()) {
tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
} else {
tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
tv.layout(mLayoutAlgorithm.mTaskRect.left, mLayoutAlgorithm.mTaskRect.top,
mLayoutAlgorithm.mTaskRect.right, mLayoutAlgorithm.mTaskRect.bottom +
tv.getMaxFooterHeight());
}
}
@ -649,8 +512,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Handler for the first layout. */
void onFirstLayout() {
int offscreenY = mStackAlgorithm.mViewRect.bottom -
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
// Find the launch target task
Task launchTargetTask = null;
@ -717,10 +580,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ctx.currentTaskTransform = new TaskViewTransform();
ctx.currentStackViewIndex = i;
ctx.currentStackViewCount = childCount;
ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null);
tv.startEnterRecentsAnimation(ctx);
}
@ -737,9 +600,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Requests this task stacks to start it's exit-recents animation. */
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Animate all the task views into view
ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
// Stop any scrolling
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
// Animate all the task views out of view
ctx.offscreenTranslationY = mLayoutAlgorithm.mViewRect.bottom -
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
@ -753,8 +619,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Animates a task view in this stack as it launches. */
public void startLaunchTaskAnimation(TaskView tv, final Runnable r) {
Task launchTargetTask = tv.getTask();
int offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView t = (TaskView) getChildAt(i);
@ -788,17 +652,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackTaskAdded(TaskStack stack, Task t) {
// Update the task offsets
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
requestSynchronizeStackViewsWithModel();
}
@Override
public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
// Update the task offsets
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
// Remove the view associated with this task, we can't rely on updateTransforms
// to work here because the task is no longer in the list
TaskView tv = getChildViewForTask(removedTask);
@ -811,8 +669,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true);
int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
requestSynchronizeStackViewsWithModel(200);
// Update the new front most task
if (newFrontMostTask != null) {
@ -839,6 +696,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
Task filteredTask) {
/*
// Stash the scroll and filtered task for us to restore to when we unfilter
mStashedScroll = getStackScroll();
@ -847,11 +705,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
getStackTransforms(curTasks, getStackScroll(), null, true);
// Update the task offsets
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
updateMinMaxScroll(false);
float overlapHeight = mStackAlgorithm.getTaskOverlapHeight();
float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight();
setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
boundScrollRaw();
@ -865,16 +723,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Notify any callbacks
mCb.onTaskStackFilterTriggered();
*/
}
@Override
public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
/*
// Calculate the current task transforms
final ArrayList<TaskViewTransform> curTaskTransforms =
getStackTransforms(curTasks, getStackScroll(), null, true);
// Update the task offsets
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Restore the stashed scroll
updateMinMaxScroll(false);
@ -894,6 +754,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Notify any callbacks
mCb.onTaskStackUnfilterTriggered();
*/
}
/**** ViewPoolConsumer Implementation ****/
@ -1014,7 +875,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onTaskViewClipStateChanged(TaskView tv) {
invalidate(mStackAlgorithm.mStackRect);
invalidate();
}
@Override
@ -1022,6 +883,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
requestSynchronizeStackViewsWithModel();
}
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
public void onScrollChanged(float p) {
mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
invalidate();
}
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override

View File

@ -18,184 +18,276 @@ package com.android.systemui.recents.views;
import android.graphics.Rect;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
import java.util.HashMap;
/* The layout logic for a TaskStackView */
/* The layout logic for a TaskStackView.
*
* We are using a curve that defines the curve of the tasks as that go back in the recents list.
* The curve is defined such that at curve progress p = 0 is the end of the curve (the top of the
* stack rect), and p = 1 at the start of the curve and the bottom of the stack rect.
*/
public class TaskStackViewLayoutAlgorithm {
// These are all going to change
static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
static final float StackPeekMinScale = 0.825f; // The min scale of the last card in the peek area
RecentsConfiguration mConfig;
// The various rects that define the stack view
Rect mViewRect = new Rect();
Rect mStackVisibleRect = new Rect();
Rect mStackRect = new Rect();
Rect mStackRectSansPeek = new Rect();
Rect mTaskRect = new Rect();
// The min/max scroll
int mMinScroll;
int mMaxScroll;
// The min/max scroll progress
float mMinScrollP;
float mMaxScrollP;
float mInitialScrollP;
int mWithinAffiliationOffset;
int mBetweenAffiliationOffset;
HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<Task.TaskKey, Float>();
HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>();
// Log function
static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
static final float LogBase = 300;
static final int PrecisionSteps = 250;
static float[] xp;
static float[] px;
public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
mConfig = config;
mWithinAffiliationOffset = mConfig.taskBarHeight;
mBetweenAffiliationOffset = 4 * mConfig.taskBarHeight;
// Precompute the path
initializeCurve();
}
/** Computes the stack and task rects */
public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
Rect taskStackBounds) {
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the stack rects
mViewRect.set(0, 0, windowWidth, windowHeight);
mStackRect.set(taskStackBounds);
mStackVisibleRect.set(taskStackBounds);
mStackVisibleRect.bottom = mViewRect.bottom;
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
int heightPadding = mConfig.taskStackTopPaddingPx;
mStackRect.inset(widthPadding, heightPadding);
mStackRectSansPeek.set(mStackRect);
mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
// Compute the task rect
int size = mStackRect.width();
int left = mStackRect.left + (mStackRect.width() - size) / 2;
mTaskRect.set(left, mStackRectSansPeek.top,
left + size, mStackRectSansPeek.top + size);
// Update the task offsets once the size changes
updateTaskOffsets(tasks);
mTaskRect.set(left, mStackRect.top,
left + size, mStackRect.top + size);
}
/** Computes the minimum and maximum scroll progress values */
void computeMinMaxScroll(ArrayList<Task> tasks) {
// Compute the min and max scroll values
int numTasks = Math.max(1, tasks.size());
int taskHeight = mTaskRect.height();
int stackHeight = mStackRectSansPeek.height();
// Clear the progress map
mTaskProgressMap.clear();
if (numTasks <= 1) {
// If there is only one task, then center the task in the stack rect (sans peek)
mMinScroll = mMaxScroll = -(stackHeight -
(taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
} else {
int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
+ taskHeight + mConfig.taskViewLockToAppButtonHeight;
mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
mMaxScroll = maxScrollHeight - stackHeight;
// Return early if we have no tasks
if (tasks.isEmpty()) {
mMinScrollP = mMaxScrollP = 0;
return;
}
int taskHeight = mTaskRect.height();
float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
float pWithinAffiliateOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - mWithinAffiliationOffset);
float pBetweenAffiliateOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
float pTaskHeightOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
float pNavBarOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
// Update the task offsets
float pAtBackMostCardTop = screenYToCurveProgress(mStackVisibleRect.top +
(mStackVisibleRect.height() - taskHeight) / 2);
float pAtFrontMostCardTop = pAtBackMostCardTop;
float pAtSecondFrontMostCardTop = pAtBackMostCardTop;
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
mTaskProgressMap.put(task.key, pAtFrontMostCardTop);
if (i < (taskCount - 1)) {
// Increment the peek height
float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset :
pWithinAffiliateOffset;
pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
pAtFrontMostCardTop += pPeek;
}
}
mMinScrollP = 0f;
mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
}
/** Update/get the transform */
public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut,
TaskViewTransform prevTransform) {
// Return early if we have an invalid index
if (task == null) {
transformOut.reset();
return transformOut;
}
return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut, prevTransform);
}
/** Update/get the transform */
public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
// Map the items to an continuous position relative to the specified scroll
int numPeekCards = StackPeekNumCards;
float overlapHeight = StackOverlapPct * mTaskRect.height();
float peekHeight = StackPeekHeightPct * mStackRect.height();
float t = (taskStackScroll - stackScroll) / overlapHeight;
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
int numFrontScaledCards = 3;
float minScale = StackPeekMinScale;
float scaleRange = 1f - minScale;
float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
float scale = Math.max(minScale, Math.min(1f, minScale +
((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
// Account for the bar offsets being scaled?
float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
transformOut.scale = scale;
// Set the y translation
if (boundedT < 0f) {
transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
} else {
transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) {
float pTaskRelative = taskProgress - stackScroll;
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
// If the task top is outside of the bounds below the screen, then immediately reset it
if (pTaskRelative > 1f) {
transformOut.reset();
return transformOut;
}
// Set the z translation
// The check for the top is trickier, since we want to show the next task if it is at all
// visible, even if p < 0.
if (pTaskRelative < 0f) {
if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
transformOut.reset();
return transformOut;
}
}
float scale = curveProgressToScale(pBounded);
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
int minZ = mConfig.taskViewTranslationZMinPx;
int incZ = mConfig.taskViewTranslationZIncrementPx;
transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
// transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
transformOut.dismissAlpha = 1f;
// Update the rect and visibility
int maxZ = mConfig.taskViewTranslationZMaxPx;
transformOut.scale = scale;
transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
scaleYOffset;
transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
transformOut.rect.set(mTaskRect);
if (t < -(numPeekCards + 1)) {
transformOut.visible = false;
} else {
transformOut.rect.offset(0, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
}
transformOut.t = t;
transformOut.rect.offset(0, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
transformOut.p = pTaskRelative;
return transformOut;
}
/**
* Returns the overlap between one task and the next.
* Returns the scroll to such task top = 1f;
*/
float getTaskOverlapHeight() {
return StackOverlapPct * mTaskRect.height();
float getStackScrollForTaskIndex(Task t) {
return mTaskProgressMap.get(t.key);
}
/**
* Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
* is not bounded)
*/
int getStackScrollForTaskIndex(Task t) {
return mTaskOffsetMap.get(t.key);
}
/** Initializes the curve. */
public static void initializeCurve() {
if (xp != null && px != null) return;
xp = new float[PrecisionSteps + 1];
px = new float[PrecisionSteps + 1];
/**
* Returns the scroll to such that the task transform at that task + index will have t=0.
* (If the scroll is not bounded)
*/
int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
}
/**
* Updates the cache of tasks to offsets.
*/
void updateTaskOffsets(ArrayList<Task> tasks) {
mTaskOffsetMap.clear();
int offset = 0;
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
mTaskOffsetMap.put(t.key, offset);
if (t.group.isFrontMostTask(t)) {
offset += getTaskOverlapHeight();
} else {
offset += mConfig.taskBarHeight;
// Approximate f(x)
float[] fx = new float[PrecisionSteps + 1];
float step = 1f / PrecisionSteps;
float x = 0;
for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
fx[xStep] = logFunc(x);
x += step;
}
// Calculate the arc length for x:1->0
float pLength = 0;
float[] dx = new float[PrecisionSteps + 1];
dx[0] = 0;
for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
pLength += dx[xStep];
}
// Approximate p(x), a function of cumulative progress with x, normalized to 0..1
float p = 0;
px[0] = 0f;
px[PrecisionSteps] = 1f;
for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
p += Math.abs(dx[xStep] / pLength);
px[xStep] = p;
}
// Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
// function.
int xStep = 0;
p = 0;
xp[0] = 0f;
xp[PrecisionSteps] = 1f;
for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
// Walk forward in px and find the x where px <= p && p < px+1
while (xStep < PrecisionSteps) {
if (px[xStep] > p) break;
xStep++;
}
// Now, px[xStep-1] <= p < px[xStep]
if (xStep == 0) {
xp[pStep] = 0;
} else {
// Find x such that proportionally, x is correct
float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
x = (xStep - 1 + fraction) * step;
xp[pStep] = x;
}
p += step;
}
}
/** Reverses and scales out x. */
static float reverse(float x) {
return (-x * XScale) + 1;
}
/** The log function describing the curve. */
static float logFunc(float x) {
return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
}
/** The inverse of the log function describing the curve. */
float invLogFunc(float y) {
return (float) (Math.log((1f - reverse(y)) * (LogBase - 1) + 1) / Math.log(LogBase));
}
/** Converts from the progress along the curve to a screen coordinate. */
int curveProgressToScreenY(float p) {
if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
float pIndex = p * PrecisionSteps;
int pFloorIndex = (int) Math.floor(pIndex);
int pCeilIndex = (int) Math.ceil(pIndex);
float xFraction = 0;
if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
}
float x = xp[pFloorIndex] + xFraction;
return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
}
/** Converts from the progress along the curve to a scale. */
float curveProgressToScale(float p) {
if (p < 0) return StackPeekMinScale;
if (p > 1) return 1f;
float scaleRange = (1f - StackPeekMinScale);
float scale = StackPeekMinScale + (p * scaleRange);
return scale;
}
/** Converts from a screen coordinate to the progress along the curve. */
float screenYToCurveProgress(int screenY) {
float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
if (x < 0 || x > 1) return x;
float xIndex = x * PrecisionSteps;
int xFloorIndex = (int) Math.floor(xIndex);
int xCeilIndex = (int) Math.ceil(xIndex);
float pFraction = 0;
if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
}
return px[xFloorIndex] + pFraction;
}
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (C) 2014 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.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.widget.OverScroller;
import com.android.systemui.recents.RecentsConfiguration;
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
public interface TaskStackViewScrollerCallbacks {
public void onScrollChanged(float p);
}
RecentsConfiguration mConfig;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
float mStackScrollP;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
mConfig = config;
mScroller = new OverScroller(context);
mLayoutAlgorithm = layoutAlgorithm;
setStackScroll(getStackScroll());
}
/** Sets the callbacks */
void setCallbacks(TaskStackViewScrollerCallbacks cb) {
mCb = cb;
}
/** Gets the current stack scroll */
public float getStackScroll() {
return mStackScrollP;
}
/** Sets the current stack scroll */
public void setStackScroll(float s) {
mStackScrollP = s;
if (mCb != null) {
mCb.onScrollChanged(mStackScrollP);
}
}
/** Sets the current stack scroll without calling the callback. */
void setStackScrollRaw(float s) {
mStackScrollP = s;
}
/** Sets the current stack scroll to the initial state when you first enter recents */
public void setStackScrollToInitialState() {
setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
}
/** Bounds the current scroll if necessary */
public boolean boundScroll() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
setStackScroll(newScroll);
return true;
}
return false;
}
/** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
public boolean boundScrollRaw() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
setStackScrollRaw(newScroll);
return true;
}
return false;
}
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
}
/** Returns the amount that the aboslute value of how much the scroll is out of bounds. */
float getScrollAmountOutOfBounds(float scroll) {
if (scroll < mLayoutAlgorithm.mMinScrollP) {
return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
} else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
}
return 0f;
}
/** Returns whether the specified scroll is out of bounds */
boolean isScrollOutOfBounds() {
return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
}
/** Animates the stack scroll into bounds */
ObjectAnimator animateBoundScroll() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
// Start a new scroll animation
animateScroll(curScroll, newScroll, null);
}
return mScrollAnimator;
}
/** Animates the stack scroll */
void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
// Abort any current animations
stopScroller();
stopBoundScrollAnimation();
mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(250);
// We would have to project the difference into the screen coords, and then use that as the
// duration
// mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
// curScroll, 250));
mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setStackScroll((Float) animation.getAnimatedValue());
}
});
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (postRunnable != null) {
postRunnable.run();
}
mScrollAnimator.removeAllListeners();
}
});
mScrollAnimator.start();
}
/** Aborts any current stack scrolls */
void stopBoundScrollAnimation() {
if (mScrollAnimator != null) {
mScrollAnimator.removeAllListeners();
mScrollAnimator.cancel();
}
}
/**** OverScroller ****/
int progressToScrollRange(float p) {
return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
}
float scrollRangeToProgress(int s) {
return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
}
/** Called from the view draw, computes the next scroll. */
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
float scroll = scrollRangeToProgress(mScroller.getCurrY());
setStackScrollRaw(scroll);
if (mCb != null) {
mCb.onScrollChanged(scroll);
}
return true;
}
return false;
}
/** Returns whether the overscroller is scrolling. */
boolean isScrolling() {
return !mScroller.isFinished();
}
/** Stops the scroller and any current fling. */
void stopScroller() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
}
}

View File

@ -29,16 +29,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
static int INACTIVE_POINTER_ID = -1;
TaskStackView mSv;
TaskStackViewScroller mScroller;
VelocityTracker mVelocityTracker;
boolean mIsScrolling;
float mInitialP;
float mLastP;
float mTotalPMotion;
int mInitialMotionX, mInitialMotionY;
int mLastMotionX, mLastMotionY;
int mActivePointerId = INACTIVE_POINTER_ID;
TaskView mActiveTaskView = null;
int mTotalScrollMotion;
int mMinimumVelocity;
int mMaximumVelocity;
// The scroll touch slop is used to calculate when we start scrolling
@ -49,14 +52,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
SwipeHelper mSwipeHelper;
boolean mInterceptedBySwipeHelper;
public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
public TaskStackViewTouchHandler(Context context, TaskStackView sv, TaskStackViewScroller scroller) {
ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mSv = sv;
mScroller = scroller;
float densityScale = context.getResources().getDisplayMetrics().density;
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
@ -97,6 +100,13 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return null;
}
/** Constructs a simulated motion event for the current stack scroll. */
MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
MotionEvent pev = MotionEvent.obtainNoHistory(ev);
pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
return pev;
}
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Return early if we have no children
@ -111,24 +121,25 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return true;
}
boolean wasScrolling = !mSv.mScroller.isFinished() ||
(mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
mSv.abortScroller();
mSv.abortBoundScrollAnimation();
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Check if the scroller is finished yet
mIsScrolling = !mSv.mScroller.isFinished();
mIsScrolling = mScroller.isScrolling();
break;
}
case MotionEvent.ACTION_MOVE: {
@ -142,7 +153,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mIsScrolling = true;
// Initialize the velocity tracker if necessary
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@ -152,17 +163,18 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mLastMotionX = x;
mLastMotionY = y;
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
// Animate the scroll back if we've cancelled
mSv.animateBoundScroll();
mScroller.animateBoundScroll();
// Reset the drag state and the velocity tracker
mIsScrolling = false;
mActivePointerId = INACTIVE_POINTER_ID;
mActiveTaskView = null;
mTotalScrollMotion = 0;
mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
@ -186,7 +198,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Update the velocity tracker
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
@ -194,14 +205,15 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
mSv.abortScroller();
mSv.abortBoundScrollAnimation();
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@ -214,6 +226,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(index);
mLastMotionX = (int) ev.getX(index);
mLastMotionY = (int) ev.getY(index);
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
break;
}
case MotionEvent.ACTION_MOVE: {
@ -223,13 +236,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
int yTotal = Math.abs(y - mInitialMotionY);
int deltaY = mLastMotionY - y;
float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
float deltaP = mLastP - curP;
if (!mIsScrolling) {
if (yTotal > mScrollTouchSlop) {
mIsScrolling = true;
// Initialize the velocity tracker
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@ -238,23 +252,26 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
}
}
if (mIsScrolling) {
int curStackScroll = mSv.getStackScroll();
int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
if (overScrollAmount != 0) {
float curStackScroll = mScroller.getStackScroll();
float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
if (Float.compare(overScrollAmount, 0f) != 0) {
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
// relative to how close we are to the max overscroll
float maxOverScroll = mSv.mStackAlgorithm.mTaskRect.height() / 3f;
deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
/ maxOverScroll)));
float maxOverScroll = 0.25f;
deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
/ maxOverScroll));
}
mSv.setStackScroll(curStackScroll + deltaY);
if (mSv.isScrollOutOfBounds()) {
mScroller.setStackScroll(curStackScroll + deltaP);
if (mScroller.isScrollOutOfBounds()) {
mVelocityTracker.clear();
} else {
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
}
}
mLastMotionX = x;
mLastMotionY = y;
mTotalScrollMotion += Math.abs(deltaY);
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mTotalPMotion += Math.abs(deltaP);
break;
}
case MotionEvent.ACTION_UP: {
@ -263,25 +280,27 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
// XXX: Should this be calculated as a percentage of a curve?
int overscrollRange = (int) (Math.min(1f,
Math.abs((float) velocity / mMaximumVelocity)) *
Constants.Values.TaskStackView.TaskStackOverscrollRange);
// Fling scroll
mSv.mScroller.fling(0, mSv.getStackScroll(),
0, -velocity,
mScroller.mScroller.fling(0, mScroller.progressToScrollRange(mScroller.getStackScroll()),
0, velocity,
0, 0,
mSv.mMinScroll, mSv.mMaxScroll,
mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
0, overscrollRange);
// Invalidate to kick off computeScroll
mSv.invalidate(mSv.mStackAlgorithm.mStackRect);
} else if (mSv.isScrollOutOfBounds()) {
mSv.invalidate();
} else if (mScroller.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
mSv.animateBoundScroll();
mScroller.animateBoundScroll();
}
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
mTotalScrollMotion = 0;
mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
@ -294,18 +313,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastMotionX = (int) ev.getX(newPointerIndex);
mLastMotionY = (int) ev.getY(newPointerIndex);
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mVelocityTracker.clear();
}
break;
}
case MotionEvent.ACTION_CANCEL: {
if (mSv.isScrollOutOfBounds()) {
if (mScroller.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
mSv.animateBoundScroll();
mScroller.animateBoundScroll();
}
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
mTotalScrollMotion = 0;
mTotalPMotion = 0;
recycleVelocityTracker();
break;
}

View File

@ -52,9 +52,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
RecentsConfiguration mConfig;
float mTaskProgress;
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1.25f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
Task mTask;
@ -76,7 +78,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
updateDimOverlayFromScale();
setTaskProgress((Float) animation.getAnimatedValue());
}
};
@ -96,10 +98,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mMaxDim = mConfig.taskStackMaxDim;
mMaxDimScale = mConfig.taskStackMaxDim / 255f;
mClipViewInStack = true;
mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
setOutlineProvider(mViewBounds);
setTaskProgress(getTaskProgress());
setDim(getDim());
}
@ -159,9 +162,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
// If we are a full screen view, then only update the Z to keep it in order
// XXX: Also update/animate the dim as well
if (mIsFullScreenView) {
@ -173,8 +173,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
// Apply the transform
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
mUpdateDimListener);
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false);
// Update the task progress
if (mTaskProgressAnimator != null) {
mTaskProgressAnimator.removeAllListeners();
mTaskProgressAnimator.cancel();
}
if (duration <= 0) {
setTaskProgress(toTransform.p);
} else {
mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
mTaskProgressAnimator.setDuration(duration);
mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
mTaskProgressAnimator.start();
}
}
/** Resets this view's properties */
@ -325,7 +338,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
// Animate the dim into view as well
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimFromTaskProgress());
anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
anim.setDuration(mConfig.taskBarEnterAnimDuration);
anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
@ -556,6 +569,17 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
}
/** Sets the current task progress. */
public void setTaskProgress(float p) {
mTaskProgress = p;
updateDimFromTaskProgress();
}
/** Returns the current task progress. */
public float getTaskProgress() {
return mTaskProgress;
}
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
@ -571,17 +595,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
/** Compute the dim as a function of the scale of this view. */
int getDimOverlayFromScale() {
float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
float scaleRange = 1f - minScale;
float dim = (1f - getScaleX()) / scaleRange;
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
int getDimFromTaskProgress() {
float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
return (int) (dim * 255);
}
/** Update the dim as a function of the scale of this view. */
void updateDimOverlayFromScale() {
setDim(getDimOverlayFromScale());
void updateDimFromTaskProgress() {
setDim(getDimFromTaskProgress());
}
/**** View focus state ****/
@ -650,9 +671,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
mBarView.rebindToTask(mTask);
// Rebind any listeners
if (Constants.DebugFlags.App.EnableTaskFiltering) {
mBarView.mApplicationIcon.setOnClickListener(this);
}
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (mFooterView != null) {
mFooterView.setOnClickListener(this);
@ -675,9 +694,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.unbindFromTask();
mBarView.unbindFromTask();
// Unbind any listeners
if (Constants.DebugFlags.App.EnableTaskFiltering) {
mBarView.mApplicationIcon.setOnClickListener(null);
}
mBarView.mApplicationIcon.setOnClickListener(null);
mBarView.mDismissButton.setOnClickListener(null);
if (mFooterView != null) {
mFooterView.setOnClickListener(null);
@ -717,7 +734,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
postDelayed(new Runnable() {
@Override
public void run() {
if (v == mBarView.mApplicationIcon) {
if (Constants.DebugFlags.App.EnableTaskFiltering && v == mBarView.mApplicationIcon) {
mCb.onTaskViewAppIconClicked(tv);
} else if (v == mBarView.mDismissButton) {
// Animate out the view and call the callback
@ -729,7 +746,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
});
// Hide the footer
tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration);
} else if (v == tv || (v == mFooterView || v == mActionButtonView)) {
} else {
mCb.onTaskViewClicked(tv, tv.getTask(),
(v == mFooterView || v == mActionButtonView));
}

View File

@ -28,13 +28,12 @@ import com.android.systemui.recents.Constants;
public class TaskViewTransform {
public int startDelay = 0;
public int translationY = 0;
public int translationZ = 0;
public float translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
public float dismissAlpha = 1f;
public boolean visible = false;
public Rect rect = new Rect();
float t = 0f;
float p = 0f;
public TaskViewTransform() {
// Do nothing
@ -46,10 +45,9 @@ public class TaskViewTransform {
translationZ = o.translationZ;
scale = o.scale;
alpha = o.alpha;
dismissAlpha = o.dismissAlpha;
visible = o.visible;
rect.set(o.rect);
t = o.t;
p = o.p;
}
/** Resets the current transform */
@ -59,19 +57,15 @@ public class TaskViewTransform {
translationZ = 0;
scale = 1f;
alpha = 1f;
dismissAlpha = 1f;
visible = false;
rect.setEmpty();
t = 0f;
p = 0f;
}
/** Convenience functions to compare against current property values */
public boolean hasAlphaChangedFrom(float v) {
return (Float.compare(alpha, v) != 0);
}
public boolean hasDismissAlphaChangedFrom(float v) {
return (Float.compare(dismissAlpha, v) != 0);
}
public boolean hasScaleChangedFrom(float v) {
return (Float.compare(scale, v) != 0);
}
@ -83,8 +77,7 @@ public class TaskViewTransform {
}
/** Applies this transform to a view. */
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers) {
// Check to see if any properties have changed, and update the task view
if (duration > 0) {
ViewPropertyAnimator anim = v.animate();
@ -100,8 +93,7 @@ public class TaskViewTransform {
}
if (hasScaleChangedFrom(v.getScaleX())) {
anim.scaleX(scale)
.scaleY(scale)
.setUpdateListener(scaleUpdateListener);
.scaleY(scale);
requiresLayers = true;
}
if (hasAlphaChangedFrom(v.getAlpha())) {
@ -128,7 +120,6 @@ public class TaskViewTransform {
if (hasScaleChangedFrom(v.getScaleX())) {
v.setScaleX(scale);
v.setScaleY(scale);
scaleUpdateListener.onAnimationUpdate(null);
}
if (hasAlphaChangedFrom(v.getAlpha())) {
v.setAlpha(alpha);
@ -152,6 +143,6 @@ public class TaskViewTransform {
public String toString() {
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
" dismissAlpha: " + dismissAlpha;
" p: " + p;
}
}