diff options
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 } |