summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java263
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java173
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java476
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java5
9 files changed, 561 insertions, 408 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index b1cc27a3445a..aee3623352ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -254,8 +254,6 @@ public class Recents extends SystemUI
null, mHandler);
}
- // Initialize some static datastructures
- TaskStackViewLayoutAlgorithm.initializeCurve();
// Initialize the static configuration resources
mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -524,15 +522,16 @@ public class Recents extends SystemUI
mStatusBarHeight, mSearchBarBounds);
}
}
- mConfig.getAvailableTaskStackBounds(windowRect,
- mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ Rect systemInsets = new Rect(0, mStatusBarHeight,
+ (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
+ mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
mSearchBarBounds, mTaskStackBounds);
- int systemBarBottomInset = mConfig.hasTransposedNavBar ? 0 : mNavBarHeight;
// Rebind the header bar and draw it for the transition
TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
- taskStackBounds.bottom -= systemBarBottomInset;
+ algo.setSystemInsets(systemInsets);
algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
if (!taskViewBounds.equals(mLastTaskViewBounds)) {
@@ -665,8 +664,11 @@ public class Recents extends SystemUI
TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
preloadIcon(topTask);
+ // Update the header bar if necessary
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
// Update the destination rect
- mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+ mDummyStackView.updateMinMaxScrollForStack(stack);
final Task toTask = new Task();
final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
topTask.id, toTask);
@@ -759,14 +761,13 @@ public class Recents extends SystemUI
TaskStack stack = sInstanceLoadPlan.getTaskStack();
// Prepare the dummy stack for the transition
- mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+ mDummyStackView.updateMinMaxScrollForStack(stack);
TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
boolean hasRecentTasks = stack.getTaskCount() > 0;
boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
if (useThumbnailTransition) {
-
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
mDummyStackView);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index b6f4a3c61383..37439e7fedc4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -154,7 +154,7 @@ public class RecentsConfiguration {
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getAvailableTaskStackBounds(Rect windowBounds, int topInset,
+ public void getTaskStackBounds(Rect windowBounds, int topInset,
int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
if (hasTransposedNavBar) {
// In landscape phones, the search bar appears on the left, but we overlay it on top
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
new file mode 100644
index 000000000000..8e85bfc4aca6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
@@ -0,0 +1,263 @@
+/*
+ * 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.misc;
+
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * Represents a 2d curve that is parameterized along the arc length of the curve (p), and allows the
+ * conversions of x->p and p->x.
+ */
+public class ParametricCurve {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ParametricCurve";
+
+ private static final int PrecisionSteps = 250;
+
+ /**
+ * A 2d function, representing the curve.
+ */
+ public interface CurveFunction {
+ float f(float x);
+ float invF(float y);
+ }
+
+ /**
+ * A function that returns a value given a parametric value.
+ */
+ public interface ParametricCurveFunction {
+ float f(float p);
+ }
+
+ float[] xp;
+ float[] px;
+
+ CurveFunction mFn;
+ ParametricCurveFunction mScaleFn;
+
+ public ParametricCurve(CurveFunction fn, ParametricCurveFunction scaleFn) {
+ long t1;
+ if (DEBUG) {
+ t1 = SystemClock.currentThreadTimeMicro();
+ Log.d(TAG, "initializeCurve");
+ }
+ mFn = fn;
+ mScaleFn = scaleFn;
+ xp = new float[PrecisionSteps + 1];
+ px = new float[PrecisionSteps + 1];
+
+ // 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] = fn.f(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.hypot(fx[xStep] - fx[xStep - 1], step);
+ 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;
+ if (DEBUG) {
+ Log.d(TAG, "p[0]=0");
+ Log.d(TAG, "p[" + PrecisionSteps + "]=1");
+ }
+ for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
+ p += Math.abs(dx[xStep] / pLength);
+ px[xStep] = p;
+ if (DEBUG) {
+ Log.d(TAG, "p[" + 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;
+ if (DEBUG) {
+ Log.d(TAG, "x[0]=0");
+ Log.d(TAG, "x[" + PrecisionSteps + "]=1");
+ }
+ 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;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "x[" + pStep + "]=" + xp[pStep]);
+ }
+ p += step;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
+ }
+ }
+
+ /**
+ * Converts from the progress along the arc-length of the curve to a coordinate within the
+ * bounds. Note, p=0 represents the top of the bounds, and p=1 represents the bottom.
+ */
+ public int pToX(float p, Rect bounds) {
+ int top = bounds.top;
+ int height = bounds.height();
+
+ if (p <= 0f) return top;
+ if (p >= 1f) return top + (int) (p * height);
+
+ float pIndex = p * PrecisionSteps;
+ int pFloorIndex = (int) Math.floor(pIndex);
+ int pCeilIndex = (int) Math.ceil(pIndex);
+ float x = xp[pFloorIndex];
+ if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
+ // Interpolate between the two precalculated positions
+ x += (xp[pCeilIndex] - xp[pFloorIndex]) * (pIndex - pFloorIndex);
+ }
+ return top + (int) (x * height);
+ }
+
+ /**
+ * Converts from the progress along the arc-length of the curve to a scale.
+ */
+ public float pToScale(float p) {
+ return mScaleFn.f(p);
+ }
+
+ /**
+ * Converts from a bounds coordinate to the progress along the arc-length of the curve.
+ * Note, p=0 represents the top of the bounds, and p=1 represents the bottom.
+ */
+ public float xToP(int x, Rect bounds) {
+ int top = bounds.top;
+
+ float xf = (float) (x - top) / bounds.height();
+ if (xf <= 0f) return 0f;
+ if (xf >= 1f) return xf;
+
+ float xIndex = xf * PrecisionSteps;
+ int xFloorIndex = (int) Math.floor(xIndex);
+ int xCeilIndex = (int) Math.ceil(xIndex);
+ float p = px[xFloorIndex];
+ if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
+ // Interpolate between the two precalculated positions
+ p += (px[xCeilIndex] - px[xFloorIndex]) * (xIndex - xFloorIndex);
+ }
+ return p;
+ }
+
+ /**
+ * Computes the progress offset from the bottom of the curve (p=1) such that the given height
+ * is visible when scaled at the that progress.
+ */
+ public float computePOffsetForScaledHeight(int height, Rect bounds) {
+ int top = bounds.top;
+ int bottom = bounds.bottom;
+
+ if (bounds.height() == 0) {
+ return 0;
+ }
+
+ // Find the next p(x) such that (bottom-x) == scale(p(x))*height
+ int minX = top;
+ int maxX = bottom;
+ long t1;
+ if (DEBUG) {
+ t1 = SystemClock.currentThreadTimeMicro();
+ Log.d(TAG, "computePOffsetForScaledHeight: " + height);
+ }
+ while (minX <= maxX) {
+ int midX = minX + ((maxX - minX) / 2);
+ float pMidX = xToP(midX, bounds);
+ float scaleMidX = mScaleFn.f(pMidX);
+ int scaledHeight = (int) (scaleMidX * height);
+ if ((bottom - midX) < scaledHeight) {
+ maxX = midX - 1;
+ } else if ((bottom - midX) > scaledHeight) {
+ minX = midX + 1;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
+ }
+ return 1f - pMidX;
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "\t2t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
+ }
+ return 1f - xToP(maxX, bounds);
+ }
+
+ /**
+ * Computes the progress offset from the bottom of the curve (p=1) that allows the given height,
+ * unscaled at the progress, will be visible.
+ */
+ public float computePOffsetForHeight(int height, Rect bounds) {
+ int top = bounds.top;
+ int bottom = bounds.bottom;
+
+ if (bounds.height() == 0) {
+ return 0;
+ }
+
+ // Find the next p(x) such that (bottom-x) == height
+ int minX = top;
+ int maxX = bottom;
+ long t1;
+ if (DEBUG) {
+ t1 = SystemClock.currentThreadTimeMicro();
+ Log.d(TAG, "computePOffsetForHeight: " + height);
+ }
+ while (minX <= maxX) {
+ int midX = minX + ((maxX - minX) / 2);
+ if ((bottom - midX) < height) {
+ maxX = midX - 1;
+ } else if ((bottom - midX) > height) {
+ minX = midX + 1;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
+ }
+ return 1f - xToP(midX, bounds);
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "\t2t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
+ }
+ return 1f - xToP(maxX, bounds);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index e4061553e631..15bd2326b95c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -317,10 +317,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
Rect taskStackBounds = new Rect();
- mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ mConfig.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
- mTaskStackView.setTaskStackBounds(taskStackBounds);
+ mTaskStackView.setTaskStackBounds(taskStackBounds, mSystemInsets);
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 78b986263213..2ef5f9f4f3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -30,7 +30,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
@@ -43,7 +42,6 @@ import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.statusbar.DismissView;
import java.util.ArrayList;
import java.util.Collections;
@@ -77,8 +75,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ViewPool<TaskView, Task> mViewPool;
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
DozeTrigger mUIDozeTrigger;
- DismissView mDismissAllButton;
- boolean mDismissAllButtonAnimating;
int mFocusedTaskIndex = -1;
int mPrevAccessibilityFocusedIndex = -1;
// Optimizations
@@ -318,7 +314,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
- mLayoutAlgorithm.mViewRect.bottom);
+ mLayoutAlgorithm.mStackRect.bottom);
}
prevTransform = transform;
}
@@ -342,19 +338,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
stackScroll, visibleRange, false);
- // Inflate and add the dismiss button if necessary
- if (Constants.DebugFlags.App.EnableDismissAll && mDismissAllButton == null) {
- mDismissAllButton = (DismissView)
- mInflater.inflate(R.layout.recents_dismiss_button, this, false);
- mDismissAllButton.setOnButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mStack.removeAllTasks();
- }
- });
- addView(mDismissAllButton, 0);
- }
-
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
List<TaskView> taskViews = getTaskViews();
@@ -369,11 +352,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} else {
mViewPool.returnViewToPool(tv);
reaquireAccessibilityFocus |= (i == mPrevAccessibilityFocusedIndex);
-
- // Hide the dismiss button if the front most task is invisible
- if (task == mStack.getFrontMostTask()) {
- hideDismissAllButton(null);
- }
}
}
@@ -399,12 +377,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
-
- // If we show the front most task view then ensure that the dismiss button
- // is visible too.
- if (!mAwaitingFirstLayout && (task == mStack.getFrontMostTask())) {
- showDismissAllButton();
- }
}
// Animate the task into place
@@ -482,10 +454,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
/** Updates the min and max virtual scroll bounds */
- void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab,
- boolean launchedFromHome) {
+ void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
- mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks(), launchedWithAltTab, launchedFromHome);
+ mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
// Debug logging
if (boundScrollToNewMinMax) {
@@ -545,8 +516,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (findClosestToCenter) {
// If there is no task focused, then find the task that is closes to the center
// of the screen and use that as the currently focused task
- int x = mLayoutAlgorithm.mStackVisibleRect.centerX();
- int y = mLayoutAlgorithm.mStackVisibleRect.centerY();
+ int x = mLayoutAlgorithm.mStackRect.centerX();
+ int y = mLayoutAlgorithm.mStackRect.centerY();
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
tv.getHitRect(mTmpRect);
@@ -697,7 +668,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Synchronize the views
synchronizeStackViewsWithModel();
clipTaskViews();
- updateDismissButtonPosition();
// Notify accessibility
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
}
@@ -709,17 +679,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
- updateMinMaxScroll(false, launchedWithAltTab, launchedFromHome);
+ updateMinMaxScroll(false);
}
/**
* This is ONLY used from AlternateRecentsComponent to update the dummy stack view for purposes
* of getting the task rect to animate to.
*/
- public void updateMinMaxScrollForStack(TaskStack stack, boolean launchedWithAltTab,
- boolean launchedFromHome) {
+ public void updateMinMaxScrollForStack(TaskStack stack) {
mStack = stack;
- updateMinMaxScroll(false, launchedWithAltTab, launchedFromHome);
+ updateMinMaxScroll(false);
}
/**
@@ -730,8 +699,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
}
- public void setTaskStackBounds(Rect taskStackBounds) {
+ public void setTaskStackBounds(Rect taskStackBounds, Rect systemInsets) {
mTaskStackBounds.set(taskStackBounds);
+ mLayoutAlgorithm.setSystemInsets(systemInsets);
}
/**
@@ -775,16 +745,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
MeasureSpec.EXACTLY));
}
- // Measure the dismiss button
- if (mDismissAllButton != null) {
- int taskRectWidth = mLayoutAlgorithm.mTaskRect.width();
- int dismissAllButtonHeight = getResources().getDimensionPixelSize(
- R.dimen.recents_dismiss_all_button_size);
- mDismissAllButton.measure(
- MeasureSpec.makeMeasureSpec(taskRectWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(dismissAllButtonHeight, MeasureSpec.EXACTLY));
- }
-
setMeasuredDimension(width, height);
}
@@ -811,15 +771,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mLayoutAlgorithm.mTaskRect.bottom + mTmpRect.bottom);
}
- // Layout the dismiss button at the top of the screen, and just translate it accordingly
- // when synchronizing the views with the model to attach it to the bottom of the front-most
- // task view
- if (mDismissAllButton != null) {
- mDismissAllButton.layout(mLayoutAlgorithm.mTaskRect.left, 0,
- mLayoutAlgorithm.mTaskRect.left + mDismissAllButton.getMeasuredWidth(),
- mDismissAllButton.getMeasuredHeight());
- }
-
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
onFirstLayout();
@@ -828,8 +779,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Handler for the first layout. */
void onFirstLayout() {
- int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
- (mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
+ int offscreenY = mLayoutAlgorithm.mStackRect.bottom;
// Find the launch target task
Task launchTargetTask = null;
@@ -948,9 +898,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
tv.setFocusedTask(true);
}
}
-
- // Show the dismiss button
- showDismissAllButton();
}
});
}
@@ -962,10 +909,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
// Animate all the task views out of view
- ctx.offscreenTranslationY = mLayoutAlgorithm.mViewRect.bottom -
- (mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
- // Animate the dismiss-all button
- hideDismissAllButton(null);
+ ctx.offscreenTranslationY = mLayoutAlgorithm.mStackRect.bottom;
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
@@ -979,20 +923,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void startDismissAllAnimation(final Runnable postAnimationRunnable) {
// Clear the focused task
resetFocusedTask();
- // Animate the dismiss-all button
- hideDismissAllButton(new Runnable() {
- @Override
- public void run() {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- int count = 0;
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- tv.startDeleteTaskAnimation(i > 0 ? null : postAnimationRunnable, count * 50);
- count++;
- }
- }
- });
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ int count = 0;
+ for (int i = taskViewCount - 1; i >= 0; i--) {
+ TaskView tv = taskViews.get(i);
+ tv.startDeleteTaskAnimation(i > 0 ? null : postAnimationRunnable, count * 50);
+ count++;
+ }
}
/** Animates a task view in this stack as it launches. */
@@ -1013,69 +951,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
- /** Shows the dismiss button */
- void showDismissAllButton() {
- if (mDismissAllButton == null) return;
-
- if (mDismissAllButtonAnimating || mDismissAllButton.getVisibility() != View.VISIBLE ||
- Float.compare(mDismissAllButton.getAlpha(), 0f) == 0) {
- mDismissAllButtonAnimating = true;
- mDismissAllButton.setVisibility(View.VISIBLE);
- mDismissAllButton.showClearButton();
- mDismissAllButton.findViewById(R.id.dismiss_text).setAlpha(1f);
- mDismissAllButton.setAlpha(0f);
- mDismissAllButton.animate()
- .alpha(1f)
- .setDuration(250)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mDismissAllButtonAnimating = false;
- }
- })
- .start();
- }
- }
-
- /** Hides the dismiss button */
- void hideDismissAllButton(final Runnable postAnimRunnable) {
- if (mDismissAllButton == null) return;
-
- mDismissAllButtonAnimating = true;
- mDismissAllButton.animate()
- .alpha(0f)
- .setDuration(200)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mDismissAllButtonAnimating = false;
- mDismissAllButton.setVisibility(View.GONE);
- if (postAnimRunnable != null) {
- postAnimRunnable.run();
- }
- }
- })
- .start();
- }
-
- /** Updates the dismiss button position */
- void updateDismissButtonPosition() {
- if (mDismissAllButton == null) return;
-
- // Update the position of the clear-all button to hang it off the first task view
- if (mStack.getTaskCount() > 0) {
- mTmpCoord[0] = mTmpCoord[1] = 0;
- TaskView tv = getChildViewForTask(mStack.getFrontMostTask());
- TaskViewTransform transform = mCurrentTaskTransforms.get(mStack.getTaskCount() - 1);
- if (tv != null && transform.visible) {
- Utilities.mapCoordInDescendentToSelf(tv, this, mTmpCoord, false);
- mDismissAllButton.setTranslationY(mTmpCoord[1] + (tv.getScaleY() * tv.getHeight()));
- mDismissAllButton.setTranslationX(-(mLayoutAlgorithm.mStackRect.width() -
- transform.rect.width()) / 2f);
- }
- }
- }
-
/** Final callback after Recents is finally hidden. */
void onRecentsHidden() {
reset();
@@ -1133,7 +1008,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Update the min/max scroll and animate other task views into their new positions
RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- updateMinMaxScroll(true, launchState.launchedWithAltTab, launchState.launchedFromHome);
+ updateMinMaxScroll(true);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
@@ -1167,9 +1042,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (shouldFinishActivity) {
mCb.onAllTaskViewsDismissed(null);
}
- } else {
- // Fade the dismiss button back in
- showDismissAllButton();
}
}
@@ -1313,8 +1185,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
for (int i = 0; i < taskViewCount; i++) {
Task tvTask = taskViews.get(i).getTask();
if (taskIndex < mStack.indexOfTask(tvTask)) {
- // Offset by 1 if we have a dismiss-all button
- insertIndex = i + (Constants.DebugFlags.App.EnableDismissAll ? 1 : 0);
+ insertIndex = i;
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index a8e6f4703177..9a5d9bdf7902 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -17,26 +17,33 @@
package com.android.systemui.recents.views;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
+import android.util.Log;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.ParametricCurve;
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.
- *
- * 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.
+
+/**
+ * The layout logic for a TaskStackView.
*/
public class TaskStackViewLayoutAlgorithm {
- // These are all going to change
- static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TaskStackViewLayoutAlgorithm";
+
+ // The min scale of the last task at the top of the curve
+ private static final float STACK_PEEK_MIN_SCALE = 0.75f;
+ // The scale of the last task
+ private static final float SINGLE_TASK_SCALE = 0.95f;
+ // The percentage of height of task to show between tasks
+ private static final float VISIBLE_TASK_HEIGHT_BETWEEN_TASKS = 0.5f;
// A report of the visibility state of the stack
public class VisibilityReport {
@@ -53,67 +60,169 @@ public class TaskStackViewLayoutAlgorithm {
Context mContext;
RecentsConfiguration mConfig;
- // The various rects that define the stack view
- Rect mViewRect = new Rect();
- Rect mStackVisibleRect = new Rect();
- Rect mStackRect = new Rect();
- Rect mTaskRect = new Rect();
+ // This is the view bounds inset exactly by the search bar, but without the bottom inset
+ // see RecentsConfiguration.getTaskStackBounds()
+ public Rect mStackRect = new Rect();
+ // This is the task view bounds for layout (untransformed), the rect is top-aligned to the top
+ // of the stack rect
+ public Rect mTaskRect = new Rect();
+ // This is the current system insets
+ public Rect mSystemInsets = new Rect();
- // The min/max scroll progress
+ // The smallest scroll progress, at this value, the back most task will be visible
float mMinScrollP;
+ // The largest scroll progress, at this value, the front most task will be visible above the
+ // navigation bar
float mMaxScrollP;
+ // The initial progress that the scroller is set
float mInitialScrollP;
- int mWithinAffiliationOffset;
- int mBetweenAffiliationOffset;
- HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<Task.TaskKey, Float>();
+
+ // The relative progress to ensure that the height between affiliated tasks is respected
+ float mWithinAffiliationPOffset;
+ // The relative progress to ensure that the height between non-affiliated tasks is
+ // respected
+ float mBetweenAffiliationPOffset;
+ // The relative progress to ensure that the task height is respected
+ float mTaskHeightPOffset;
+ // The relative progress to ensure that the half task height is respected
+ float mTaskHalfHeightPOffset;
+ // The relative progress to ensure that the offset from the bottom of the stack to the bottom
+ // of the task is respected
+ float mTaskBottomPOffset;
+ // The front-most task bottom offset
+ int mTaskBottomOffset;
+
+ // The last computed task count
+ int mNumTasks;
+ // The min/max z translations
+ int mMinTranslationZ;
+ int mMaxTranslationZ;
+
+ // Optimization, allows for quick lookup of task -> progress
+ HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<>();
// Log function
- static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
- static final float LogBase = 3000;
- static final int PrecisionSteps = 250;
- static float[] xp;
- static float[] px;
+ static ParametricCurve sCurve;
public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
+ Resources res = context.getResources();
+ mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+ mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
mContext = context;
mConfig = config;
+ if (sCurve == null) {
+ sCurve = new ParametricCurve(new ParametricCurve.CurveFunction() {
+ // The large the XScale, the longer the flat area of the curve
+ private static final float XScale = 1.75f;
+ private static final float LogBase = 3000;
+
+ float reverse(float x) {
+ return (-x * XScale) + 1;
+ }
+
+ @Override
+ public float f(float x) {
+ return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
+ }
+
+ @Override
+ public float invF(float y) {
+ return (float) (Math.log(1f - reverse(y)) / (-Math.log(LogBase) * XScale));
+ }
+ }, new ParametricCurve.ParametricCurveFunction() {
+ @Override
+ public float f(float p) {
+ if (p < 0) return STACK_PEEK_MIN_SCALE;
+ if (p > 1) return 1f;
+ float scaleRange = (1f - STACK_PEEK_MIN_SCALE);
+ float scale = STACK_PEEK_MIN_SCALE + (p * scaleRange);
+ return scale;
+ }
+ });
+ }
+ }
- // Precompute the path
- initializeCurve();
+ /**
+ * Sets the system insets.
+ */
+ public void setSystemInsets(Rect systemInsets) {
+ mSystemInsets.set(systemInsets);
+ if (DEBUG) {
+ Log.d(TAG, "setSystemInsets: " + systemInsets);
+ }
}
- /** Computes the stack and task rects */
+ /**
+ * Computes the stack and task rects
+ */
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 widthPadding = (int) (mConfig.taskStackWidthPaddingPct * taskStackBounds.width());
int heightPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_stack_top_padding);
- mStackRect.inset(widthPadding, heightPadding);
- // Compute the task rect
- int size = mStackRect.width();
- int left = mStackRect.left + (mStackRect.width() - size) / 2;
- mTaskRect.set(left, mStackRect.top,
- left + size, mStackRect.top + size);
+ // Compute the stack rect, inset from the given task stack bounds
+ mStackRect.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding,
+ taskStackBounds.right - widthPadding, windowHeight);
+ mTaskBottomOffset = mSystemInsets.bottom + heightPadding;
- // Update the affiliation offsets
- float visibleTaskPct = 0.5f;
- mWithinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
+ // Compute the task rect, align it to the top-center square in the stack rect
+ int size = Math.min(mStackRect.width(), taskStackBounds.height() - mTaskBottomOffset);
+ int xOffset = (mStackRect.width() - size) / 2;
+ mTaskRect.set(mStackRect.left + xOffset, mStackRect.top,
+ mStackRect.right - xOffset, mStackRect.top + size);
+
+ // Compute the progress offsets
+ int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
- mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
+ int betweenAffiliationOffset = (int) (VISIBLE_TASK_HEIGHT_BETWEEN_TASKS * mTaskRect.height());
+ mWithinAffiliationPOffset = sCurve.computePOffsetForScaledHeight(withinAffiliationOffset,
+ mStackRect);
+ mBetweenAffiliationPOffset = sCurve.computePOffsetForScaledHeight(betweenAffiliationOffset,
+ mStackRect);
+ mTaskHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height(),
+ mStackRect);
+ mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
+ mStackRect);
+ mTaskBottomPOffset = sCurve.computePOffsetForHeight(mTaskBottomOffset, mStackRect);
+
+ if (DEBUG) {
+ Log.d(TAG, "computeRects");
+ Log.d(TAG, "\tmStackRect: " + mStackRect);
+ Log.d(TAG, "\tmTaskRect: " + mTaskRect);
+ Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
+
+ Log.d(TAG, "\tpWithinAffiliateOffset: " + mWithinAffiliationPOffset);
+ Log.d(TAG, "\tpBetweenAffiliateOffset: " + mBetweenAffiliationPOffset);
+ Log.d(TAG, "\tmTaskHeightPOffset: " + mTaskHeightPOffset);
+ Log.d(TAG, "\tmTaskHalfHeightPOffset: " + mTaskHalfHeightPOffset);
+ Log.d(TAG, "\tmTaskBottomPOffset: " + mTaskBottomPOffset);
+
+ Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mStackRect));
+ Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mStackRect));
+
+ for (int height = 0; height <= 1000; height += 50) {
+ float p = sCurve.computePOffsetForScaledHeight(height, mStackRect);
+ float p2 = sCurve.computePOffsetForHeight(height, mStackRect);
+ Log.d(TAG, "offset: " + height + ", " +
+ p + " => " + (mStackRect.bottom - sCurve.pToX(1f - p, mStackRect)) /
+ sCurve.pToScale(1f - p) + ", " +
+ p2 + " => " + (mStackRect.bottom - sCurve.pToX(1f - p2, mStackRect)));
+ }
+ }
}
- /** Computes the minimum and maximum scroll progress values. This method may be called before
- * the RecentsConfiguration is set, so we need to pass in the alt-tab state. */
- void computeMinMaxScroll(ArrayList<Task> tasks, boolean launchedWithAltTab,
- boolean launchedFromHome) {
+ /**
+ * Computes the minimum and maximum scroll progress values. This method may be called before
+ * the RecentsConfiguration is set, so we need to pass in the alt-tab state.
+ */
+ void computeMinMaxScroll(ArrayList<Task> tasks) {
+ if (DEBUG) {
+ Log.d(TAG, "computeMinMaxScroll");
+ }
+
// Clear the progress map
mTaskProgressMap.clear();
+ mNumTasks = tasks.size();
// Return early if we have no tasks
if (tasks.isEmpty()) {
@@ -121,57 +230,41 @@ public class TaskStackViewLayoutAlgorithm {
return;
}
- // Note that we should account for the scale difference of the offsets at the screen bottom
- int taskHeight = mTaskRect.height();
- float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
- float pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
- mWithinAffiliationOffset);
- float scale = curveProgressToScale(pWithinAffiliateTop);
- int scaleYOffset = (int) (((1f - scale) * taskHeight) / 2);
- pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
- mWithinAffiliationOffset + scaleYOffset);
- float pWithinAffiliateOffset = pAtBottomOfStackRect - pWithinAffiliateTop;
- float pBetweenAffiliateOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
- float pTaskHeightOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
- float pNavBarOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom -
- mStackRect.bottom));
- float pDismissAllButtonOffset = 0f;
- if (Constants.DebugFlags.App.EnableDismissAll) {
- int dismissAllButtonHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_dismiss_all_button_size);
- pDismissAllButtonOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - dismissAllButtonHeight);
- }
-
- // Update the task offsets
- float pAtBackMostCardTop = 0.5f;
- float pAtFrontMostCardTop = 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;
- pAtFrontMostCardTop += pPeek;
+ // We calculate the progress by taking the progress of the element from the bottom of the
+ // screen
+ if (mNumTasks == 1) {
+ // Just center the task in the visible stack rect
+ mMinScrollP = mMaxScrollP = mInitialScrollP = 0f;
+ mTaskProgressMap.put(tasks.get(0).key, 0f);
+ } else {
+ // Update the tasks from back to front with the new progresses. We set the initial
+ // progress to the progress at which the top of the last task is near the center of the
+ // visible stack rect.
+ float pAtBackMostTaskTop = 0;
+ float pAtFrontMostTaskTop = pAtBackMostTaskTop;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
+
+ if (i < (taskCount - 1)) {
+ // Increment the peek height
+ float pPeek = task.group.isFrontMostTask(task) ?
+ mBetweenAffiliationPOffset : mWithinAffiliationPOffset;
+ pAtFrontMostTaskTop += pPeek;
+ }
}
- }
- mMaxScrollP = pAtFrontMostCardTop + pDismissAllButtonOffset -
- ((1f - pTaskHeightOffset - pNavBarOffset));
- mMinScrollP = tasks.size() == 1 ? Math.max(mMaxScrollP, 0f) : 0f;
- if (launchedWithAltTab && launchedFromHome) {
- // Center the top most task, since that will be focused first
- mInitialScrollP = mMaxScrollP;
- } else {
- mInitialScrollP = pAtFrontMostCardTop - 0.825f;
+ // Set the max scroll progress to the point at which the bottom of the front-most task
+ // is aligned to the bottom of the stack (including nav bar and stack padding)
+ mMaxScrollP = pAtFrontMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
+ // Basically align the back-most task such that its progress is the same as the top of
+ // the front most task at the max scroll
+ mMinScrollP = pAtBackMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
+ // The offset the inital scroll position to the front of the stack, with half the front
+ // task height visible
+ mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
}
- mInitialScrollP = Math.min(mMaxScrollP, Math.max(0, mInitialScrollP));
}
/**
@@ -190,7 +283,7 @@ public class TaskStackViewLayoutAlgorithm {
int numVisibleTasks = 1;
int numVisibleThumbnails = 1;
float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
- int prevScreenY = curveProgressToScreenY(progress);
+ int prevScreenY = sCurve.pToX(progress, mStackRect);
for (int i = tasks.size() - 2; i >= 0; i--) {
Task task = tasks.get(i);
progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
@@ -199,9 +292,9 @@ public class TaskStackViewLayoutAlgorithm {
}
boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task);
if (isFrontMostTaskInGroup) {
- float scaleAtP = curveProgressToScale(progress);
+ float scaleAtP = sCurve.pToScale(progress);
int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
- int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
+ int screenY = sCurve.pToX(progress, mStackRect) + scaleYOffsetAtP;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
@@ -227,7 +320,10 @@ public class TaskStackViewLayoutAlgorithm {
return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
}
- /** Update/get the transform */
+ /**
+ * Returns the transform for the given task. This transform is relative to the mTaskRect, which
+ * is what the view is measured and laid out with.
+ */
public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform prevTransform) {
// Return early if we have an invalid index
@@ -242,154 +338,72 @@ public class TaskStackViewLayoutAlgorithm {
/** Update/get the transform */
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();
+ if (DEBUG) {
+ Log.d(TAG, "getStackTransform: " + stackScroll);
+ }
+
+ if (mNumTasks == 1) {
+ // Center the task in the stack, changing the scale will not follow the curve, but just
+ // modulate some values directly
+ float pTaskRelative = -stackScroll;
+ float scale = SINGLE_TASK_SCALE;
+ int topOffset = (mStackRect.height() - mTaskBottomOffset - mTaskRect.height()) / 2;
+ transformOut.scale = scale;
+ transformOut.translationY = (int) (topOffset + (pTaskRelative * mStackRect.height()));
+ transformOut.translationZ = mMaxTranslationZ;
transformOut.rect.set(mTaskRect);
+ transformOut.rect.offset(0, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = pTaskRelative;
return transformOut;
- }
- // 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) {
+ } else {
+ 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();
transformOut.rect.set(mTaskRect);
return transformOut;
}
+ // 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();
+ transformOut.rect.set(mTaskRect);
+ return transformOut;
+ }
+ }
+ float scale = sCurve.pToScale(pBounded);
+ int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
+ transformOut.scale = scale;
+ transformOut.translationY = sCurve.pToX(pBounded, mStackRect) - mStackRect.top -
+ scaleYOffset;
+ transformOut.translationZ = Math.max(mMinTranslationZ,
+ mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
+ transformOut.rect.set(mTaskRect);
+ transformOut.rect.offset(0, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = pTaskRelative;
+ return transformOut;
}
- float scale = curveProgressToScale(pBounded);
- int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
- int minZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- int maxZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- transformOut.scale = scale;
- transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
- scaleYOffset;
- transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- transformOut.p = pTaskRelative;
- return transformOut;
}
- /** Returns the untransformed task view bounds. */
+ /**
+ * Returns the untransformed task view bounds.
+ */
public Rect getUntransformedTaskViewBounds() {
return new Rect(mTaskRect);
}
- /** Returns the scroll to such task top = 1f; */
+ /**
+ * Returns the scroll progress to scroll to such that the top of the task is at the top of the
+ * stack.
+ */
float getStackScrollForTask(Task t) {
if (!mTaskProgressMap.containsKey(t.key)) return 0f;
return mTaskProgressMap.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];
-
- // 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;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index e4fbc76f9d3b..3d3b13dd3290 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -175,11 +175,11 @@ public class TaskStackViewScroller {
/**** OverScroller ****/
int progressToScrollRange(float p) {
- return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
+ return (int) (p * mLayoutAlgorithm.mStackRect.height());
}
float scrollRangeToProgress(int s) {
- return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
+ return (float) s / mLayoutAlgorithm.mStackRect.height();
}
/** Called from the view draw, computes the next scroll. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 48c5b46c34d7..18e07a39e81a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -136,6 +136,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return true;
}
+ TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
int action = ev.getAction();
@@ -144,7 +145,8 @@ 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);
+ mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
+ layoutAlgorithm.mStackRect);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
@@ -177,7 +179,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mLastMotionX = x;
mLastMotionY = y;
- mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+ mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
break;
}
case MotionEvent.ACTION_CANCEL:
@@ -213,13 +215,15 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Update the velocity tracker
initVelocityTrackerIfNotExists();
+ TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
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);
+ mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
+ layoutAlgorithm.mStackRect);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
@@ -240,7 +244,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(index);
mLastMotionX = (int) ev.getX(index);
mLastMotionY = (int) ev.getY(index);
- mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+ mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
+ layoutAlgorithm.mStackRect);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -252,7 +257,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
int yTotal = Math.abs(y - mInitialMotionY);
- float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
+ float curP = layoutAlgorithm.sCurve.xToP(y, layoutAlgorithm.mStackRect);
float deltaP = mLastP - curP;
if (!mIsScrolling) {
if (yTotal > mScrollTouchSlop) {
@@ -279,7 +284,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
}
mLastMotionX = x;
mLastMotionY = y;
- mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+ mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
mTotalPMotion += Math.abs(deltaP);
break;
}
@@ -324,7 +329,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastMotionX = (int) ev.getX(newPointerIndex);
mLastMotionY = (int) ev.getY(newPointerIndex);
- mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+ mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
mVelocityTracker.clear();
}
break;
@@ -420,8 +425,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // Fade out the dismiss button
- mSv.hideDismissAllButton(null);
}
@Override
@@ -450,8 +453,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
tv.setTouchEnabled(true);
- // Restore the dismiss button
- mSv.showDismissAllButton();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index a55e0266ce23..f8f7052e1c67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -31,9 +31,12 @@ public class TaskViewTransform {
public float scale = 1f;
public float alpha = 1f;
public boolean visible = false;
- public Rect rect = new Rect();
float p = 0f;
+ // This is a window-space rect that is purely used for coordinating the animation of an app
+ // window into Recents.
+ public Rect rect = new Rect();
+
public TaskViewTransform() {
// Do nothing
}