blob: 25083040df1d822053670415aa085bc72a9608d6 [file] [log] [blame]
Winson Chungd7b2cb12014-06-26 15:08:50 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
Winson59924fe2016-03-17 14:13:18 -070019import android.annotation.IntDef;
Winson35f30502015-09-28 11:24:36 -070020import android.content.Context;
Winson59924fe2016-03-17 14:13:18 -070021import android.content.res.Configuration;
Winson88f00ab2015-10-05 17:24:00 -070022import android.content.res.Resources;
Winson4993c2f2015-11-19 10:06:06 -080023import android.graphics.Path;
Winson Chungd7b2cb12014-06-26 15:08:50 -070024import android.graphics.Rect;
Winson55003902016-01-12 12:00:37 -080025import android.util.ArraySet;
Winson66474132016-02-23 18:45:47 -080026import android.util.SparseArray;
27import android.util.SparseIntArray;
Winson231bc9c2016-02-09 12:31:00 -080028import android.view.ViewDebug;
Winsonc0d70582016-01-29 10:24:39 -080029
Winson2536c7e2015-10-01 15:49:31 -070030import com.android.systemui.R;
Winson23afcae2015-10-28 11:14:54 -070031import com.android.systemui.recents.Recents;
Winson4993c2f2015-11-19 10:06:06 -080032import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chungd7b2cb12014-06-26 15:08:50 -070033import com.android.systemui.recents.RecentsConfiguration;
Winsone693aaf2016-03-01 12:05:59 -080034import com.android.systemui.recents.RecentsDebugFlags;
Winson4993c2f2015-11-19 10:06:06 -080035import com.android.systemui.recents.misc.FreePathInterpolator;
Winsona5e6b362015-11-02 17:17:20 -080036import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungffa2ec62014-07-03 15:54:42 -070037import com.android.systemui.recents.misc.Utilities;
Winson Chunga433fa92014-07-08 21:50:31 -070038import com.android.systemui.recents.model.Task;
Winson36a5a2c2015-10-29 18:04:39 -070039import com.android.systemui.recents.model.TaskStack;
Winson Chunga433fa92014-07-08 21:50:31 -070040
Winson59924fe2016-03-17 14:13:18 -070041import java.lang.annotation.Retention;
42import java.lang.annotation.RetentionPolicy;
Winson Chunga433fa92014-07-08 21:50:31 -070043import java.util.ArrayList;
Winsone693aaf2016-03-01 12:05:59 -080044import java.util.List;
Winson Chungd7b2cb12014-06-26 15:08:50 -070045
Winson4993c2f2015-11-19 10:06:06 -080046/**
47 * Used to describe a visible range that can be normalized to [0, 1].
48 */
49class Range {
50 final float relativeMin;
51 final float relativeMax;
52 float origin;
53 float min;
54 float max;
55
56 public Range(float relMin, float relMax) {
57 min = relativeMin = relMin;
58 max = relativeMax = relMax;
59 }
60
61 /**
62 * Offsets this range to a given absolute position.
63 */
64 public void offset(float x) {
65 this.origin = x;
66 min = x + relativeMin;
67 max = x + relativeMax;
68 }
69
70 /**
71 * Returns x normalized to the range 0 to 1 such that 0 = min, 0.5 = origin and 1 = max
72 *
73 * @param x is an absolute value in the same domain as origin
74 */
75 public float getNormalizedX(float x) {
76 if (x < origin) {
77 return 0.5f + 0.5f * (x - origin) / -relativeMin;
78 } else {
79 return 0.5f + 0.5f * (x - origin) / relativeMax;
80 }
81 }
82
83 /**
84 * Given a normalized {@param x} value in this range, projected onto the full range to get an
85 * absolute value about the given {@param origin}.
86 */
87 public float getAbsoluteX(float normX) {
88 if (normX < 0.5f) {
89 return (normX - 0.5f) / 0.5f * -relativeMin;
90 } else {
91 return (normX - 0.5f) / 0.5f * relativeMax;
92 }
93 }
94
95 /**
96 * Returns whether a value at an absolute x would be within range.
97 */
98 public boolean isInRange(float absX) {
99 return (absX >= Math.floor(min)) && (absX <= Math.ceil(max));
100 }
101}
Winson88f00ab2015-10-05 17:24:00 -0700102
103/**
Winson59924fe2016-03-17 14:13:18 -0700104 * The layout logic for a TaskStackView. This layout needs to be able to calculate the stack layout
105 * without an activity-specific context only with the information passed in. This layout can have
106 * two states focused and unfocused, and in the focused state, there is a task that is displayed
107 * more prominently in the stack.
Winson Chung012ef362014-07-31 18:36:25 -0700108 */
Winson36a5a2c2015-10-29 18:04:39 -0700109public class TaskStackLayoutAlgorithm {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700110
Winson14991502016-02-15 15:40:08 -0800111 // The distribution of view bounds alpha
112 // XXX: This is a hack because you can currently set the max alpha to be > 1f
113 public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
114 public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
115
Winson59924fe2016-03-17 14:13:18 -0700116 // The medium/maximum dim on the tasks
117 private static final float MED_DIM = 0.15f;
Winson12858a62016-02-24 11:06:01 -0800118 private static final float MAX_DIM = 0.25f;
Winson1bcf3c42016-02-10 13:29:39 -0800119
Winson8b1871d2015-11-20 09:56:20 -0800120 // The various focus states
Winson66474132016-02-23 18:45:47 -0800121 public static final int STATE_FOCUSED = 1;
122 public static final int STATE_UNFOCUSED = 0;
Winson8b1871d2015-11-20 09:56:20 -0800123
Winson59924fe2016-03-17 14:13:18 -0700124 // The side that an offset is anchored
125 @Retention(RetentionPolicy.SOURCE)
126 @IntDef({FROM_TOP, FROM_BOTTOM})
127 public @interface AnchorSide {}
128 private static final int FROM_TOP = 0;
129 private static final int FROM_BOTTOM = 1;
130
131 // The extent that we care about when calculating fractions
132 @Retention(RetentionPolicy.SOURCE)
133 @IntDef({WIDTH, HEIGHT})
134 public @interface Extent {}
135 private static final int WIDTH = 0;
136 private static final int HEIGHT = 1;
137
Winson1c846142016-01-22 11:34:38 -0800138 public interface TaskStackLayoutAlgorithmCallbacks {
Winson66474132016-02-23 18:45:47 -0800139 void onFocusStateChanged(int prevFocusState, int curFocusState);
Winson1c846142016-01-22 11:34:38 -0800140 }
141
Winson8b1871d2015-11-20 09:56:20 -0800142 /**
Winsonf0d1c442015-12-01 11:04:45 -0800143 * The various stack/freeform states.
144 */
145 public static class StackState {
146
Winson Chungaa4f8002015-12-17 10:27:55 -0500147 public static final StackState FREEFORM_ONLY = new StackState(1f, 255);
Winsona78a8f32015-12-03 10:55:01 -0800148 public static final StackState STACK_ONLY = new StackState(0f, 0);
149 public static final StackState SPLIT = new StackState(0.5f, 255);
Winsonf0d1c442015-12-01 11:04:45 -0800150
151 public final float freeformHeightPct;
Winsona78a8f32015-12-03 10:55:01 -0800152 public final int freeformBackgroundAlpha;
Winsonf0d1c442015-12-01 11:04:45 -0800153
154 /**
155 * @param freeformHeightPct the percentage of the stack height (not including paddings) to
156 * allocate to the freeform workspace
Winsona78a8f32015-12-03 10:55:01 -0800157 * @param freeformBackgroundAlpha the background alpha for the freeform workspace
Winsonf0d1c442015-12-01 11:04:45 -0800158 */
Winsonef064132016-01-05 12:11:31 -0800159 private StackState(float freeformHeightPct, int freeformBackgroundAlpha) {
Winsonf0d1c442015-12-01 11:04:45 -0800160 this.freeformHeightPct = freeformHeightPct;
Winsona78a8f32015-12-03 10:55:01 -0800161 this.freeformBackgroundAlpha = freeformBackgroundAlpha;
Winsonf0d1c442015-12-01 11:04:45 -0800162 }
163
164 /**
165 * Resolves the stack state for the layout given a task stack.
166 */
167 public static StackState getStackStateForStack(TaskStack stack) {
168 SystemServicesProxy ssp = Recents.getSystemServices();
169 boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport();
Winson4b057c62016-01-12 15:01:52 -0800170 int freeformCount = stack.getFreeformTaskCount();
171 int stackCount = stack.getStackTaskCount();
Winsonf0d1c442015-12-01 11:04:45 -0800172 if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) {
173 return SPLIT;
174 } else if (hasFreeformWorkspaces && freeformCount > 0) {
175 return FREEFORM_ONLY;
176 } else {
177 return STACK_ONLY;
178 }
179 }
180
181 /**
182 * Computes the freeform and stack rect for this state.
183 *
184 * @param freeformRectOut the freeform rect to be written out
185 * @param stackRectOut the stack rect, we only write out the top of the stack
186 * @param taskStackBounds the full rect that the freeform rect can take up
187 */
188 public void computeRects(Rect freeformRectOut, Rect stackRectOut,
Winson59924fe2016-03-17 14:13:18 -0700189 Rect taskStackBounds, int topMargin, int freeformGap, int stackBottomOffset) {
190 // The freeform height is the visible height (not including system insets) - padding
191 // above freeform and below stack - gap between the freeform and stack
192 int availableHeight = taskStackBounds.height() - topMargin - stackBottomOffset;
Winsonf0d1c442015-12-01 11:04:45 -0800193 int ffPaddedHeight = (int) (availableHeight * freeformHeightPct);
Winson59924fe2016-03-17 14:13:18 -0700194 int ffHeight = Math.max(0, ffPaddedHeight - freeformGap);
195 freeformRectOut.set(taskStackBounds.left,
196 taskStackBounds.top + topMargin,
197 taskStackBounds.right,
198 taskStackBounds.top + topMargin + ffHeight);
199 stackRectOut.set(taskStackBounds.left,
Winsonf0d1c442015-12-01 11:04:45 -0800200 taskStackBounds.top,
Winson59924fe2016-03-17 14:13:18 -0700201 taskStackBounds.right,
Winsonf0d1c442015-12-01 11:04:45 -0800202 taskStackBounds.bottom);
203 if (ffPaddedHeight > 0) {
204 stackRectOut.top += ffPaddedHeight;
205 } else {
Winson59924fe2016-03-17 14:13:18 -0700206 stackRectOut.top += topMargin;
Winsonf0d1c442015-12-01 11:04:45 -0800207 }
208 }
209 }
210
Winson Chunga91c2932014-11-07 15:02:38 -0800211 // A report of the visibility state of the stack
212 public class VisibilityReport {
213 public int numVisibleTasks;
214 public int numVisibleThumbnails;
215
216 /** Package level ctor */
217 VisibilityReport(int tasks, int thumbnails) {
218 numVisibleTasks = tasks;
219 numVisibleThumbnails = thumbnails;
220 }
221 }
222
Winson35f30502015-09-28 11:24:36 -0700223 Context mContext;
Winsonf0d1c442015-12-01 11:04:45 -0800224 private StackState mState = StackState.SPLIT;
Winson1c846142016-01-22 11:34:38 -0800225 private TaskStackLayoutAlgorithmCallbacks mCb;
Winson Chungd7b2cb12014-06-26 15:08:50 -0700226
Winsona5e6b362015-11-02 17:17:20 -0800227 // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
Winson231bc9c2016-02-09 12:31:00 -0800228 @ViewDebug.ExportedProperty(category="recents")
Winson88f00ab2015-10-05 17:24:00 -0700229 public Rect mTaskRect = new Rect();
Winson008ee15f2016-03-18 17:17:25 -0700230 // The freeform workspace bounds, inset by the top system insets and is a fixed height
Winson231bc9c2016-02-09 12:31:00 -0800231 @ViewDebug.ExportedProperty(category="recents")
Winson23afcae2015-10-28 11:14:54 -0700232 public Rect mFreeformRect = new Rect();
Winson008ee15f2016-03-18 17:17:25 -0700233 // The stack bounds, inset from the top system insets, and runs to the bottom of the screen
Winson231bc9c2016-02-09 12:31:00 -0800234 @ViewDebug.ExportedProperty(category="recents")
Winsonf0d1c442015-12-01 11:04:45 -0800235 public Rect mStackRect = new Rect();
Winson88f00ab2015-10-05 17:24:00 -0700236 // This is the current system insets
Winson231bc9c2016-02-09 12:31:00 -0800237 @ViewDebug.ExportedProperty(category="recents")
Winson88f00ab2015-10-05 17:24:00 -0700238 public Rect mSystemInsets = new Rect();
Winson8f6ee482016-03-18 17:51:48 -0700239 // This is the bounds of the stack action above the stack rect
Winson231bc9c2016-02-09 12:31:00 -0800240 @ViewDebug.ExportedProperty(category="recents")
Winson8f6ee482016-03-18 17:51:48 -0700241 public Rect mStackActionButtonRect = new Rect();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700242
Winson4993c2f2015-11-19 10:06:06 -0800243 // The visible ranges when the stack is focused and unfocused
244 private Range mUnfocusedRange;
245 private Range mFocusedRange;
246
Winson59924fe2016-03-17 14:13:18 -0700247 // The base top margin for the stack from the system insets
Winsone693aaf2016-03-01 12:05:59 -0800248 @ViewDebug.ExportedProperty(category="recents")
Winson59924fe2016-03-17 14:13:18 -0700249 private int mBaseTopMargin;
250 // The base side margin for the stack from the system insets
Winson73492c52016-03-09 17:44:54 -0800251 @ViewDebug.ExportedProperty(category="recents")
Winson59924fe2016-03-17 14:13:18 -0700252 private int mBaseSideMargin;
253 // The base bottom margin for the stack from the system insets
254 @ViewDebug.ExportedProperty(category="recents")
255 private int mBaseBottomMargin;
256 private int mMinMargin;
Winson008ee15f2016-03-18 17:17:25 -0700257
Winson59924fe2016-03-17 14:13:18 -0700258 // The gap between the freeform and stack layouts
259 @ViewDebug.ExportedProperty(category="recents")
260 private int mFreeformStackGap;
Winsone693aaf2016-03-01 12:05:59 -0800261
Winson59924fe2016-03-17 14:13:18 -0700262 // The initial offset that the focused task is from the top
263 @ViewDebug.ExportedProperty(category="recents")
264 private int mInitialTopOffset;
265 private int mBaseInitialTopOffset;
266 // The initial offset that the launch-from task is from the bottom
267 @ViewDebug.ExportedProperty(category="recents")
268 private int mInitialBottomOffset;
269 private int mBaseInitialBottomOffset;
270
271 // The height between the top margin and the top of the focused task
Winson231bc9c2016-02-09 12:31:00 -0800272 @ViewDebug.ExportedProperty(category="recents")
Winson68088812016-02-12 16:06:04 -0800273 private int mFocusedTopPeekHeight;
Winson59924fe2016-03-17 14:13:18 -0700274 // The height between the bottom margin and the top of task in front of the focused task
Winson68088812016-02-12 16:06:04 -0800275 @ViewDebug.ExportedProperty(category="recents")
Winson59924fe2016-03-17 14:13:18 -0700276 private int mFocusedBottomPeekHeight;
Winsone693aaf2016-03-01 12:05:59 -0800277
Winson250608a2015-11-24 15:00:31 -0800278 // The offset from the bottom of the stack to the bottom of the bounds when the stack is
279 // scrolled to the front
Winson231bc9c2016-02-09 12:31:00 -0800280 @ViewDebug.ExportedProperty(category="recents")
Winson4993c2f2015-11-19 10:06:06 -0800281 private int mStackBottomOffset;
282
283 // The paths defining the motion of the tasks when the stack is focused and unfocused
284 private Path mUnfocusedCurve;
285 private Path mFocusedCurve;
286 private FreePathInterpolator mUnfocusedCurveInterpolator;
287 private FreePathInterpolator mFocusedCurveInterpolator;
288
Winson12858a62016-02-24 11:06:01 -0800289 // The paths defining the distribution of the dim to apply to tasks in the stack when focused
290 // and unfocused
291 private Path mUnfocusedDimCurve;
292 private Path mFocusedDimCurve;
293 private FreePathInterpolator mUnfocusedDimCurveInterpolator;
294 private FreePathInterpolator mFocusedDimCurveInterpolator;
295
Winsone693aaf2016-03-01 12:05:59 -0800296 // Indexed from the front of the stack, the normalized x in the unfocused range for each task
297 private float[] mInitialNormX;
298
Winson4993c2f2015-11-19 10:06:06 -0800299 // The state of the stack focus (0..1), which controls the transition of the stack from the
300 // focused to non-focused state
Winson231bc9c2016-02-09 12:31:00 -0800301 @ViewDebug.ExportedProperty(category="recents")
Winson66474132016-02-23 18:45:47 -0800302 private int mFocusState;
Winson8b1871d2015-11-20 09:56:20 -0800303
Winson88f00ab2015-10-05 17:24:00 -0700304 // The smallest scroll progress, at this value, the back most task will be visible
Winson231bc9c2016-02-09 12:31:00 -0800305 @ViewDebug.ExportedProperty(category="recents")
Winson Chung012ef362014-07-31 18:36:25 -0700306 float mMinScrollP;
Winson88f00ab2015-10-05 17:24:00 -0700307 // The largest scroll progress, at this value, the front most task will be visible above the
308 // navigation bar
Winson231bc9c2016-02-09 12:31:00 -0800309 @ViewDebug.ExportedProperty(category="recents")
Winson Chung012ef362014-07-31 18:36:25 -0700310 float mMaxScrollP;
Winson36a5a2c2015-10-29 18:04:39 -0700311 // The initial progress that the scroller is set when you first enter recents
Winson231bc9c2016-02-09 12:31:00 -0800312 @ViewDebug.ExportedProperty(category="recents")
Winson Chung012ef362014-07-31 18:36:25 -0700313 float mInitialScrollP;
Winson23afcae2015-10-28 11:14:54 -0700314 // The task progress for the front-most task in the stack
Winson231bc9c2016-02-09 12:31:00 -0800315 @ViewDebug.ExportedProperty(category="recents")
Winson23afcae2015-10-28 11:14:54 -0700316 float mFrontMostTaskP;
Winson88f00ab2015-10-05 17:24:00 -0700317
Winson23afcae2015-10-28 11:14:54 -0700318 // The last computed task counts
Winson231bc9c2016-02-09 12:31:00 -0800319 @ViewDebug.ExportedProperty(category="recents")
Winson23afcae2015-10-28 11:14:54 -0700320 int mNumStackTasks;
Winson231bc9c2016-02-09 12:31:00 -0800321 @ViewDebug.ExportedProperty(category="recents")
Winson23afcae2015-10-28 11:14:54 -0700322 int mNumFreeformTasks;
Winson4993c2f2015-11-19 10:06:06 -0800323
Winson88f00ab2015-10-05 17:24:00 -0700324 // The min/max z translations
Winson231bc9c2016-02-09 12:31:00 -0800325 @ViewDebug.ExportedProperty(category="recents")
Winson88f00ab2015-10-05 17:24:00 -0700326 int mMinTranslationZ;
Winson231bc9c2016-02-09 12:31:00 -0800327 @ViewDebug.ExportedProperty(category="recents")
Winson88f00ab2015-10-05 17:24:00 -0700328 int mMaxTranslationZ;
329
Winson4993c2f2015-11-19 10:06:06 -0800330 // Optimization, allows for quick lookup of task -> index
Winson66474132016-02-23 18:45:47 -0800331 private SparseIntArray mTaskIndexMap = new SparseIntArray();
332 private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700333
Winson36a5a2c2015-10-29 18:04:39 -0700334 // The freeform workspace layout
335 FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
336
Winsonf24f2162016-01-05 12:11:55 -0800337 // The transform to place TaskViews at the front and back of the stack respectively
338 TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
339 TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
340
Winson1c846142016-01-22 11:34:38 -0800341 public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
Winson59924fe2016-03-17 14:13:18 -0700342 Resources res = context.getResources();
Winson1c846142016-01-22 11:34:38 -0800343 mContext = context;
344 mCb = cb;
Winsone693aaf2016-03-01 12:05:59 -0800345 mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
Winson59924fe2016-03-17 14:13:18 -0700346 mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
347 mBaseTopMargin = getDimensionForDevice(res,
348 R.dimen.recents_layout_top_margin_phone,
349 R.dimen.recents_layout_top_margin_tablet,
350 R.dimen.recents_layout_top_margin_tablet_xlarge);
351 mBaseSideMargin = getDimensionForDevice(res,
352 R.dimen.recents_layout_side_margin_phone,
353 R.dimen.recents_layout_side_margin_tablet,
354 R.dimen.recents_layout_side_margin_tablet_xlarge);
355 mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
Winson59924fe2016-03-17 14:13:18 -0700356 mFreeformStackGap =
357 res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
358
Winsone693aaf2016-03-01 12:05:59 -0800359 reloadOnConfigurationChange(context);
360 }
Winson4993c2f2015-11-19 10:06:06 -0800361
Winsone693aaf2016-03-01 12:05:59 -0800362 /**
363 * Reloads the layout for the current configuration.
364 */
365 public void reloadOnConfigurationChange(Context context) {
366 Resources res = context.getResources();
Winson4993c2f2015-11-19 10:06:06 -0800367 mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
368 res.getFloat(R.integer.recents_layout_focused_range_max));
369 mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
370 res.getFloat(R.integer.recents_layout_unfocused_range_max));
Winsone693aaf2016-03-01 12:05:59 -0800371 mFocusState = getInitialFocusState();
Winson59924fe2016-03-17 14:13:18 -0700372 mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_top_peek_size);
373 mFocusedBottomPeekHeight =
374 res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
375 mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
376 mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
377 mBaseInitialTopOffset = getDimensionForDevice(res,
378 R.dimen.recents_layout_initial_top_offset_phone_port,
379 R.dimen.recents_layout_initial_top_offset_phone_land,
380 R.dimen.recents_layout_initial_top_offset_tablet,
381 R.dimen.recents_layout_initial_top_offset_tablet,
382 R.dimen.recents_layout_initial_top_offset_tablet,
383 R.dimen.recents_layout_initial_top_offset_tablet);
384 mBaseInitialBottomOffset = getDimensionForDevice(res,
385 R.dimen.recents_layout_initial_bottom_offset_phone_port,
386 R.dimen.recents_layout_initial_bottom_offset_phone_land,
387 R.dimen.recents_layout_initial_bottom_offset_tablet,
388 R.dimen.recents_layout_initial_bottom_offset_tablet,
389 R.dimen.recents_layout_initial_bottom_offset_tablet,
390 R.dimen.recents_layout_initial_bottom_offset_tablet);
Winsone693aaf2016-03-01 12:05:59 -0800391 mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
Winson8b1871d2015-11-20 09:56:20 -0800392 }
393
394 /**
395 * Resets this layout when the stack view is reset.
396 */
397 public void reset() {
Winson66474132016-02-23 18:45:47 -0800398 mTaskIndexOverrideMap.clear();
Winsone693aaf2016-03-01 12:05:59 -0800399 setFocusState(getInitialFocusState());
Winson Chungd7b2cb12014-06-26 15:08:50 -0700400 }
401
Winson88f00ab2015-10-05 17:24:00 -0700402 /**
403 * Sets the system insets.
404 */
405 public void setSystemInsets(Rect systemInsets) {
406 mSystemInsets.set(systemInsets);
Winson88f00ab2015-10-05 17:24:00 -0700407 }
Winson Chungd7b2cb12014-06-26 15:08:50 -0700408
Winson88f00ab2015-10-05 17:24:00 -0700409 /**
Winson4993c2f2015-11-19 10:06:06 -0800410 * Sets the focused state.
411 */
Winson66474132016-02-23 18:45:47 -0800412 public void setFocusState(int focusState) {
413 int prevFocusState = mFocusState;
Winson4993c2f2015-11-19 10:06:06 -0800414 mFocusState = focusState;
Winsonf24f2162016-01-05 12:11:55 -0800415 updateFrontBackTransforms();
Winson1c846142016-01-22 11:34:38 -0800416 if (mCb != null) {
417 mCb.onFocusStateChanged(prevFocusState, focusState);
418 }
Winson4993c2f2015-11-19 10:06:06 -0800419 }
420
421 /**
422 * Gets the focused state.
423 */
Winson66474132016-02-23 18:45:47 -0800424 public int getFocusState() {
Winson4993c2f2015-11-19 10:06:06 -0800425 return mFocusState;
426 }
427
428 /**
Winson59924fe2016-03-17 14:13:18 -0700429 * Computes the stack and task rects. The given task stack bounds already has the top/right
430 * insets and left/right padding already applied.
Winson88f00ab2015-10-05 17:24:00 -0700431 */
Winson59924fe2016-03-17 14:13:18 -0700432 public void initialize(Rect windowRect, Rect taskStackBounds, StackState state) {
433 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonf0d1c442015-12-01 11:04:45 -0800434 Rect lastStackRect = new Rect(mStackRect);
Winson59924fe2016-03-17 14:13:18 -0700435 Rect displayRect = ssp.getDisplayRect();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700436
Winson59924fe2016-03-17 14:13:18 -0700437 int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
438 int bottomMargin = getScaleForExtent(windowRect, displayRect, mBaseBottomMargin, mMinMargin,
439 HEIGHT);
440 mInitialTopOffset = getScaleForExtent(windowRect, displayRect, mBaseInitialTopOffset,
441 mMinMargin, HEIGHT);
442 mInitialBottomOffset = mBaseInitialBottomOffset;
443
444 // Compute the stack bounds
Winsona78a8f32015-12-03 10:55:01 -0800445 mState = state;
Winson59924fe2016-03-17 14:13:18 -0700446 mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
447 state.computeRects(mFreeformRect, mStackRect, taskStackBounds, topMargin,
448 mFreeformStackGap, mStackBottomOffset);
449
Winson8f6ee482016-03-18 17:51:48 -0700450 // The stack action button will take the full un-padded header space above the stack
451 mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
Winson68088812016-02-12 16:06:04 -0800452 mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
Winson8b1871d2015-11-20 09:56:20 -0800453
Winson59924fe2016-03-17 14:13:18 -0700454 // Anchor the task rect top aligned to the non-freeform stack rect
455 float aspect = (float) (windowRect.width() - (mSystemInsets.left + mSystemInsets.right)) /
456 (windowRect.height() - (mSystemInsets.top + mSystemInsets.bottom));
457 int minHeight = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
458 int height = (int) Math.min(mStackRect.width() / aspect, minHeight);
459 mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
Winson88f00ab2015-10-05 17:24:00 -0700460
Winson4993c2f2015-11-19 10:06:06 -0800461 // Short circuit here if the stack rects haven't changed so we don't do all the work below
Winson59924fe2016-03-17 14:13:18 -0700462 if (!lastStackRect.equals(mStackRect)) {
463 // Reinitialize the focused and unfocused curves
464 mUnfocusedCurve = constructUnfocusedCurve();
465 mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
466 mFocusedCurve = constructFocusedCurve();
467 mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
468 mUnfocusedDimCurve = constructUnfocusedDimCurve();
469 mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
470 mFocusedDimCurve = constructFocusedDimCurve();
471 mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
472
473 updateFrontBackTransforms();
Winson4993c2f2015-11-19 10:06:06 -0800474 }
Winson Chungd7b2cb12014-06-26 15:08:50 -0700475 }
476
Winson88f00ab2015-10-05 17:24:00 -0700477 /**
Winson36a5a2c2015-10-29 18:04:39 -0700478 * Computes the minimum and maximum scroll progress values and the progress values for each task
479 * in the stack.
Winson88f00ab2015-10-05 17:24:00 -0700480 */
Winson8aa99592016-01-19 15:07:07 -0800481 void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet) {
Winsoneca4ab62015-11-04 10:50:28 -0800482 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonb61e6542016-02-04 14:37:18 -0800483 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
Winson88f00ab2015-10-05 17:24:00 -0700484
Winson Chung012ef362014-07-31 18:36:25 -0700485 // Clear the progress map
Winson4993c2f2015-11-19 10:06:06 -0800486 mTaskIndexMap.clear();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700487
Winson Chung012ef362014-07-31 18:36:25 -0700488 // Return early if we have no tasks
Winson250608a2015-11-24 15:00:31 -0800489 ArrayList<Task> tasks = stack.getStackTasks();
Winson Chung012ef362014-07-31 18:36:25 -0700490 if (tasks.isEmpty()) {
Winson36a5a2c2015-10-29 18:04:39 -0700491 mFrontMostTaskP = 0;
Winson59924fe2016-03-17 14:13:18 -0700492 mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
Winson23afcae2015-10-28 11:14:54 -0700493 mNumStackTasks = mNumFreeformTasks = 0;
Winson Chung012ef362014-07-31 18:36:25 -0700494 return;
Winson Chungd7b2cb12014-06-26 15:08:50 -0700495 }
Winson Chung012ef362014-07-31 18:36:25 -0700496
Winson23afcae2015-10-28 11:14:54 -0700497 // Filter the set of freeform and stack tasks
498 ArrayList<Task> freeformTasks = new ArrayList<>();
499 ArrayList<Task> stackTasks = new ArrayList<>();
500 for (int i = 0; i < tasks.size(); i++) {
501 Task task = tasks.get(i);
Winson8aa99592016-01-19 15:07:07 -0800502 if (ignoreTasksSet.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800503 continue;
504 }
Winson23afcae2015-10-28 11:14:54 -0700505 if (task.isFreeformTask()) {
506 freeformTasks.add(task);
507 } else {
508 stackTasks.add(task);
509 }
510 }
511 mNumStackTasks = stackTasks.size();
512 mNumFreeformTasks = freeformTasks.size();
513
Winson4993c2f2015-11-19 10:06:06 -0800514 // Put each of the tasks in the progress map at a fixed index (does not need to actually
515 // map to a scroll position, just by index)
516 int taskCount = stackTasks.size();
517 for (int i = 0; i < taskCount; i++) {
518 Task task = stackTasks.get(i);
Winson66474132016-02-23 18:45:47 -0800519 mTaskIndexMap.put(task.key.id, i);
Winson4993c2f2015-11-19 10:06:06 -0800520 }
Winson88f00ab2015-10-05 17:24:00 -0700521
Winsone693aaf2016-03-01 12:05:59 -0800522 // Update the freeform tasks
Winson23afcae2015-10-28 11:14:54 -0700523 if (!freeformTasks.isEmpty()) {
Winson36a5a2c2015-10-29 18:04:39 -0700524 mFreeformLayoutAlgorithm.update(freeformTasks, this);
Winsone693aaf2016-03-01 12:05:59 -0800525 }
526
527 // Calculate the min/max/initial scroll
528 Task launchTask = stack.getLaunchTarget();
529 int launchTaskIndex = launchTask != null
530 ? stack.indexOfStackTask(launchTask)
531 : mNumStackTasks - 1;
532 if (getInitialFocusState() == STATE_FOCUSED) {
533 mMinScrollP = 0;
534 mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
535 if (launchState.launchedFromHome) {
536 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
Winson4993c2f2015-11-19 10:06:06 -0800537 } else {
Winsone693aaf2016-03-01 12:05:59 -0800538 mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
Winson4993c2f2015-11-19 10:06:06 -0800539 }
Winsone693aaf2016-03-01 12:05:59 -0800540 } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
541 // If there is one stack task, ignore the min/max/initial scroll positions
542 mMinScrollP = 0;
543 mMaxScrollP = 0;
544 mInitialScrollP = 0;
545 } else {
546 // Set the max scroll to be the point where the front most task is visible with the
547 // stack bottom offset
548 int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
Winson59924fe2016-03-17 14:13:18 -0700549 float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM);
Winsone693aaf2016-03-01 12:05:59 -0800550 mUnfocusedRange.offset(0f);
551 mMinScrollP = 0;
552 mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
553 Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
554 boolean scrollToFront = launchState.launchedFromHome ||
Winsonc69249f2016-03-28 13:38:39 -0700555 launchState.launchedViaDockGesture;
Winsone693aaf2016-03-01 12:05:59 -0800556 if (scrollToFront) {
557 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
Winson59924fe2016-03-17 14:13:18 -0700558 mInitialNormX = null;
Winsone693aaf2016-03-01 12:05:59 -0800559 } else {
Winson3b6ba1a2016-03-22 15:37:54 -0700560 // We are overriding the initial two task positions, so set the initial scroll
561 // position to match the second task (aka focused task) position
562 float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
563 mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
564 - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
Winsone693aaf2016-03-01 12:05:59 -0800565
Winson59924fe2016-03-17 14:13:18 -0700566 // Set the initial scroll to the predefined state (which differs from the stack)
567 mInitialNormX = new float[] {
568 getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
569 FROM_BOTTOM),
Winson3b6ba1a2016-03-22 15:37:54 -0700570 initialTopNormX
Winson59924fe2016-03-17 14:13:18 -0700571 };
572 }
Winsone693aaf2016-03-01 12:05:59 -0800573 }
574 }
575
576 public void updateToInitialState(List<Task> tasks) {
577 if (mInitialNormX == null) {
578 return;
579 }
580
Winsone693aaf2016-03-01 12:05:59 -0800581 mUnfocusedRange.offset(0f);
582 int taskCount = tasks.size();
583 for (int i = taskCount - 1; i >= 0; i--) {
584 int indexFromFront = taskCount - i - 1;
585 if (indexFromFront >= mInitialNormX.length) {
586 break;
587 }
588 float newTaskProgress = mInitialScrollP +
589 mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]);
590 mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
Winson23afcae2015-10-28 11:14:54 -0700591 }
Winson Chungd7b2cb12014-06-26 15:08:50 -0700592 }
593
Winson Chunga91c2932014-11-07 15:02:38 -0800594 /**
Winson66474132016-02-23 18:45:47 -0800595 * Adds and override task progress for the given task when transitioning from focused to
596 * unfocused state.
Winson8b1871d2015-11-20 09:56:20 -0800597 */
Winson66474132016-02-23 18:45:47 -0800598 public void addUnfocusedTaskOverride(Task task, float stackScroll) {
599 if (mFocusState != STATE_UNFOCUSED) {
600 mFocusedRange.offset(stackScroll);
601 mUnfocusedRange.offset(stackScroll);
602 float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id));
603 float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX);
604 float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY);
605 float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
606 if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
607 mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
608 }
Winson8b1871d2015-11-20 09:56:20 -0800609 }
610 }
611
Winsone693aaf2016-03-01 12:05:59 -0800612 public void clearUnfocusedTaskOverrides() {
613 mTaskIndexOverrideMap.clear();
614 }
615
Winson8b1871d2015-11-20 09:56:20 -0800616 /**
Winson66474132016-02-23 18:45:47 -0800617 * Updates this stack when a scroll happens.
Winson8b1871d2015-11-20 09:56:20 -0800618 */
Winson66474132016-02-23 18:45:47 -0800619 public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
Winson88737542016-02-17 13:27:33 -0800620 if (deltaScroll == 0f) {
621 return;
622 }
623
Winson66474132016-02-23 18:45:47 -0800624 for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
625 int taskId = mTaskIndexOverrideMap.keyAt(i);
626 float x = mTaskIndexMap.get(taskId);
627 float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
628 float newOverrideX = overrideX + deltaScroll;
629 mUnfocusedRange.offset(stackScroll);
630 boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
631 mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
632 if (outOfBounds || (overrideX >= x && x >= newOverrideX) ||
633 (overrideX <= x && x <= newOverrideX)) {
634 // Remove the override once we reach the original task index
635 mTaskIndexOverrideMap.removeAt(i);
636 } else if ((overrideX >= x && deltaScroll <= 0f) ||
637 (overrideX <= x && deltaScroll >= 0f)) {
638 // Scrolling from override x towards x, then lock the task in place
639 mTaskIndexOverrideMap.put(taskId, newOverrideX);
640 } else {
641 // Scrolling override x away from x, we should still move the scroll towards x
642 float deltaX = overrideX - x;
Winsone693aaf2016-03-01 12:05:59 -0800643 newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
Winson66474132016-02-23 18:45:47 -0800644 mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
645 }
Winson8b1871d2015-11-20 09:56:20 -0800646 }
647 }
648
649 /**
650 * Returns the default focus state.
651 */
Winsone693aaf2016-03-01 12:05:59 -0800652 public int getInitialFocusState() {
653 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
654 if (debugFlags.isPagingEnabled()) {
655 return STATE_FOCUSED;
656 } else {
657 return STATE_UNFOCUSED;
658 }
Winson8b1871d2015-11-20 09:56:20 -0800659 }
660
661 /**
Winsonf24f2162016-01-05 12:11:55 -0800662 * Returns the TaskViewTransform that would put the task just off the back of the stack.
Winsonbb410952015-12-04 14:34:11 -0800663 */
Winsonf24f2162016-01-05 12:11:55 -0800664 public TaskViewTransform getBackOfStackTransform() {
665 return mBackOfStackTransform;
Winsonbb410952015-12-04 14:34:11 -0800666 }
667
668 /**
Winsonf24f2162016-01-05 12:11:55 -0800669 * Returns the TaskViewTransform that would put the task just off the front of the stack.
Winson0e0d85c2015-12-03 11:35:25 -0800670 */
Winsonf24f2162016-01-05 12:11:55 -0800671 public TaskViewTransform getFrontOfStackTransform() {
672 return mFrontOfStackTransform;
Winson0e0d85c2015-12-03 11:35:25 -0800673 }
674
675 /**
Winsona78a8f32015-12-03 10:55:01 -0800676 *
677 * Returns the current stack state.
678 */
679 public StackState getStackState() {
680 return mState;
681 }
682
683 /**
Winson88737542016-02-17 13:27:33 -0800684 * Returns whether this stack layout has been initialized.
685 */
686 public boolean isInitialized() {
687 return !mStackRect.isEmpty();
688 }
689
690 /**
Winson4993c2f2015-11-19 10:06:06 -0800691 * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
692 * stack scroll. Requires that update() is called first.
Winson Chunga91c2932014-11-07 15:02:38 -0800693 */
694 public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
Winson23afcae2015-10-28 11:14:54 -0700695 // Ensure minimum visibility count
Winson Chunga91c2932014-11-07 15:02:38 -0800696 if (tasks.size() <= 1) {
697 return new VisibilityReport(1, 1);
698 }
699
Winsona5e6b362015-11-02 17:17:20 -0800700 // Quick return when there are no stack tasks
701 if (mNumStackTasks == 0) {
702 return new VisibilityReport(Math.max(mNumFreeformTasks, 1),
703 Math.max(mNumFreeformTasks, 1));
Winson23afcae2015-10-28 11:14:54 -0700704 }
705
706 // Otherwise, walk backwards in the stack and count the number of tasks and visible
Winsona5e6b362015-11-02 17:17:20 -0800707 // thumbnails and add that to the total freeform task count
Winson4993c2f2015-11-19 10:06:06 -0800708 TaskViewTransform tmpTransform = new TaskViewTransform();
Winsone693aaf2016-03-01 12:05:59 -0800709 Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
Winson4993c2f2015-11-19 10:06:06 -0800710 currentRange.offset(mInitialScrollP);
Winson35f30502015-09-28 11:24:36 -0700711 int taskBarHeight = mContext.getResources().getDimensionPixelSize(
Winson59924fe2016-03-17 14:13:18 -0700712 R.dimen.recents_task_view_header_height);
Winsona5e6b362015-11-02 17:17:20 -0800713 int numVisibleTasks = Math.max(mNumFreeformTasks, 1);
714 int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1);
Winson4993c2f2015-11-19 10:06:06 -0800715 float prevScreenY = Integer.MAX_VALUE;
716 for (int i = tasks.size() - 1; i >= 0; i--) {
Winson Chunga91c2932014-11-07 15:02:38 -0800717 Task task = tasks.get(i);
Winson4993c2f2015-11-19 10:06:06 -0800718
719 // Skip freeform
Winsona5e6b362015-11-02 17:17:20 -0800720 if (task.isFreeformTask()) {
721 continue;
722 }
723
Winson4993c2f2015-11-19 10:06:06 -0800724 // Skip invisible
725 float taskProgress = getStackScrollForTask(task);
726 if (!currentRange.isInRange(taskProgress)) {
727 continue;
Winson Chunga91c2932014-11-07 15:02:38 -0800728 }
Winson4993c2f2015-11-19 10:06:06 -0800729
Winsoneca4ab62015-11-04 10:50:28 -0800730 boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
Winson Chunga91c2932014-11-07 15:02:38 -0800731 if (isFrontMostTaskInGroup) {
Winson8f97c832016-03-04 11:32:03 -0800732 getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
733 tmpTransform, null, false /* ignoreSingleTaskCase */,
734 false /* forceUpdate */);
Winson4993c2f2015-11-19 10:06:06 -0800735 float screenY = tmpTransform.rect.top;
Winson35f30502015-09-28 11:24:36 -0700736 boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
Winson Chunga91c2932014-11-07 15:02:38 -0800737 if (hasVisibleThumbnail) {
738 numVisibleThumbnails++;
739 numVisibleTasks++;
740 prevScreenY = screenY;
741 } else {
742 // Once we hit the next front most task that does not have a visible thumbnail,
Winson4993c2f2015-11-19 10:06:06 -0800743 // walk through remaining visible set
Winson Chunga91c2932014-11-07 15:02:38 -0800744 for (int j = i; j >= 0; j--) {
745 numVisibleTasks++;
Winson4993c2f2015-11-19 10:06:06 -0800746 taskProgress = getStackScrollForTask(tasks.get(j));
747 if (!currentRange.isInRange(taskProgress)) {
748 continue;
Winson Chunga91c2932014-11-07 15:02:38 -0800749 }
750 }
751 break;
752 }
753 } else if (!isFrontMostTaskInGroup) {
754 // Affiliated task, no thumbnail
755 numVisibleTasks++;
756 }
757 }
758 return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
759 }
760
Winson88f00ab2015-10-05 17:24:00 -0700761 /**
762 * Returns the transform for the given task. This transform is relative to the mTaskRect, which
763 * is what the view is measured and laid out with.
764 */
Winson Chunga91c2932014-11-07 15:02:38 -0800765 public TaskViewTransform getStackTransform(Task task, float stackScroll,
Winson4993c2f2015-11-19 10:06:06 -0800766 TaskViewTransform transformOut, TaskViewTransform frontTransform) {
Winson14991502016-02-15 15:40:08 -0800767 return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
Winsone693aaf2016-03-01 12:05:59 -0800768 false /* forceUpdate */, false /* ignoreTaskOverrides */);
769 }
770
771 public TaskViewTransform getStackTransform(Task task, float stackScroll,
772 TaskViewTransform transformOut, TaskViewTransform frontTransform,
773 boolean ignoreTaskOverrides) {
774 return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
775 false /* forceUpdate */, ignoreTaskOverrides);
Winson9bbd28612016-02-03 14:52:12 -0800776 }
777
Winson66474132016-02-23 18:45:47 -0800778 public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
Winsone693aaf2016-03-01 12:05:59 -0800779 TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
780 boolean ignoreTaskOverrides) {
Winsona5e6b362015-11-02 17:17:20 -0800781 if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
782 mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
Winson Chungd7b2cb12014-06-26 15:08:50 -0700783 return transformOut;
Winson36a5a2c2015-10-29 18:04:39 -0700784 } else {
785 // Return early if we have an invalid index
Winson8f97c832016-03-04 11:32:03 -0800786 int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
787 if (task == null || nonOverrideTaskProgress == -1) {
Winson36a5a2c2015-10-29 18:04:39 -0700788 transformOut.reset();
789 return transformOut;
790 }
Winsone693aaf2016-03-01 12:05:59 -0800791 float taskProgress = ignoreTaskOverrides
Winson8f97c832016-03-04 11:32:03 -0800792 ? nonOverrideTaskProgress
Winsone693aaf2016-03-01 12:05:59 -0800793 : getStackScrollForTask(task);
Winson8f97c832016-03-04 11:32:03 -0800794 getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
795 transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
Winson9bbd28612016-02-03 14:52:12 -0800796 return transformOut;
Winson Chungd7b2cb12014-06-26 15:08:50 -0700797 }
Winson Chungd6c3db52014-07-22 17:02:16 -0700798 }
Winson Chungd7b2cb12014-06-26 15:08:50 -0700799
Winson8aa99592016-01-19 15:07:07 -0800800 /**
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800801 * Like {@link #getStackTransform}, but in screen coordinates
802 */
803 public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
804 TaskViewTransform transformOut, TaskViewTransform frontTransform) {
805 Rect windowRect = Recents.getSystemServices().getWindowRect();
806 TaskViewTransform transform = getStackTransform(task, stackScroll, transformOut,
807 frontTransform);
808 transform.rect.offset(windowRect.left, windowRect.top);
809 return transform;
810 }
811
812 /**
Winson8aa99592016-01-19 15:07:07 -0800813 * Update/get the transform.
814 *
815 * @param ignoreSingleTaskCase When set, will ensure that the transform computed does not take
816 * into account the special single-task case. This is only used
817 * internally to ensure that we can calculate the transform for any
818 * position in the stack.
819 */
Winson8f97c832016-03-04 11:32:03 -0800820 public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
821 float stackScroll, int focusState, TaskViewTransform transformOut,
822 TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
Winsoneca4ab62015-11-04 10:50:28 -0800823 SystemServicesProxy ssp = Recents.getSystemServices();
Winson88f00ab2015-10-05 17:24:00 -0700824
Winson4993c2f2015-11-19 10:06:06 -0800825 // Compute the focused and unfocused offset
Winsonbe8f3572016-02-15 16:52:27 -0800826 float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
827 mUnfocusedRange.offset(boundedStackScroll);
828 mFocusedRange.offset(boundedStackScroll);
829 float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
830 float boundedScrollFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
Winson8f97c832016-03-04 11:32:03 -0800831 float boundedScrollUnfocusedNonOverrideRangeX =
832 mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
Winson4993c2f2015-11-19 10:06:06 -0800833 mUnfocusedRange.offset(stackScroll);
Winson68088812016-02-12 16:06:04 -0800834 mFocusedRange.offset(stackScroll);
Winson8b1871d2015-11-20 09:56:20 -0800835 boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
Winson68088812016-02-12 16:06:04 -0800836 boolean focusedVisible = mFocusedRange.isInRange(taskProgress);
Winson23afcae2015-10-28 11:14:54 -0700837
Winson8b1871d2015-11-20 09:56:20 -0800838 // Skip if the task is not visible
Winson9bbd28612016-02-03 14:52:12 -0800839 if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
Winson8b1871d2015-11-20 09:56:20 -0800840 transformOut.reset();
Winson9bbd28612016-02-03 14:52:12 -0800841 return;
Winson Chung012ef362014-07-31 18:36:25 -0700842 }
Winson4993c2f2015-11-19 10:06:06 -0800843
Winson68088812016-02-12 16:06:04 -0800844 float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
845 float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
846
Winsonbb410952015-12-04 14:34:11 -0800847 int x = (mStackRect.width() - mTaskRect.width()) / 2;
Winson8b1871d2015-11-20 09:56:20 -0800848 int y;
849 float z;
Winson68088812016-02-12 16:06:04 -0800850 float dimAlpha;
Winson14991502016-02-15 15:40:08 -0800851 float viewOutlineAlpha;
Winson8aa99592016-01-19 15:07:07 -0800852 if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1 && !ignoreSingleTaskCase) {
Winson4993c2f2015-11-19 10:06:06 -0800853 // When there is exactly one task, then decouple the task from the stack and just move
854 // in screen space
Winson68088812016-02-12 16:06:04 -0800855 float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
Winsonf0d1c442015-12-01 11:04:45 -0800856 int centerYOffset = (mStackRect.top - mTaskRect.top) +
Winson619e40c2016-03-25 16:12:35 -0700857 (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
Winson68088812016-02-12 16:06:04 -0800858 y = centerYOffset + getYForDeltaP(tmpP, 0);
Winson8b1871d2015-11-20 09:56:20 -0800859 z = mMaxTranslationZ;
Winson68088812016-02-12 16:06:04 -0800860 dimAlpha = 0f;
Winson14991502016-02-15 15:40:08 -0800861 viewOutlineAlpha = (OUTLINE_ALPHA_MIN_VALUE + OUTLINE_ALPHA_MAX_VALUE) / 2f;
Winson4993c2f2015-11-19 10:06:06 -0800862
863 } else {
864 // Otherwise, update the task to the stack layout
Winson68088812016-02-12 16:06:04 -0800865 int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation(
866 unfocusedRangeX)) * mStackRect.height());
867 int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
868 focusedRangeX)) * mStackRect.height());
Winson12858a62016-02-24 11:06:01 -0800869 float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation(
Winsonbe8f3572016-02-15 16:52:27 -0800870 boundedScrollUnfocusedRangeX);
Winson12858a62016-02-24 11:06:01 -0800871 float focusedDim = mFocusedDimCurveInterpolator.getInterpolation(
Winsonbe8f3572016-02-15 16:52:27 -0800872 boundedScrollFocusedRangeX);
Winson68088812016-02-12 16:06:04 -0800873
874 y = (mStackRect.top - mTaskRect.top) +
Winson14991502016-02-15 15:40:08 -0800875 (int) Utilities.mapRange(focusState, unfocusedY, focusedY);
Winson8f97c832016-03-04 11:32:03 -0800876 z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
Winsonbe8f3572016-02-15 16:52:27 -0800877 mMinTranslationZ, mMaxTranslationZ);
Winson12858a62016-02-24 11:06:01 -0800878 dimAlpha = Utilities.mapRange(focusState, unfocusedDim, focusedDim);
Winsonbe8f3572016-02-15 16:52:27 -0800879 viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
Winson14991502016-02-15 15:40:08 -0800880 OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE);
Winson4993c2f2015-11-19 10:06:06 -0800881 }
882
Winson8b1871d2015-11-20 09:56:20 -0800883 // Fill out the transform
Winson4993c2f2015-11-19 10:06:06 -0800884 transformOut.scale = 1f;
Winsonbb410952015-12-04 14:34:11 -0800885 transformOut.alpha = 1f;
Winson8b1871d2015-11-20 09:56:20 -0800886 transformOut.translationZ = z;
Winson14991502016-02-15 15:40:08 -0800887 transformOut.dimAlpha = dimAlpha;
888 transformOut.viewOutlineAlpha = viewOutlineAlpha;
Winson8b1871d2015-11-20 09:56:20 -0800889 transformOut.rect.set(mTaskRect);
Winsonbb410952015-12-04 14:34:11 -0800890 transformOut.rect.offset(x, y);
Winson4993c2f2015-11-19 10:06:06 -0800891 Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
Winsona0731a12015-12-02 15:10:14 -0800892 transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
893 (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
Winson Chungd7b2cb12014-06-26 15:08:50 -0700894 }
895
Winson88f00ab2015-10-05 17:24:00 -0700896 /**
897 * Returns the untransformed task view bounds.
898 */
Winsonec417e42015-09-29 12:42:29 -0700899 public Rect getUntransformedTaskViewBounds() {
900 return new Rect(mTaskRect);
Winson Chunga4ccb862014-08-22 15:26:27 -0700901 }
902
Winson88f00ab2015-10-05 17:24:00 -0700903 /**
904 * Returns the scroll progress to scroll to such that the top of the task is at the top of the
905 * stack.
906 */
Winson Chung78cc96a2014-08-12 13:56:03 -0700907 float getStackScrollForTask(Task t) {
Winson66474132016-02-23 18:45:47 -0800908 return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0));
Winson Chungd7b2cb12014-06-26 15:08:50 -0700909 }
Winson23afcae2015-10-28 11:14:54 -0700910
911 /**
Winson59924fe2016-03-17 14:13:18 -0700912 * Returns the original scroll progress to scroll to such that the top of the task is at the top
913 * of the stack.
914 */
915 float getStackScrollForTaskIgnoreOverrides(Task t) {
916 return (float) mTaskIndexMap.get(t.key.id, 0);
917 }
918
919 /**
920 * Returns the scroll progress to scroll to such that the top of the task at the initial top
921 * offset (which is at the task's brightest point).
922 */
923 float getStackScrollForTaskAtInitialOffset(Task t) {
924 float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
925 mUnfocusedRange.offset(0f);
Winsonf8597b22016-03-23 18:44:26 -0700926 return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0,
927 mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP);
Winson59924fe2016-03-17 14:13:18 -0700928 }
929
930 /**
Winson23afcae2015-10-28 11:14:54 -0700931 * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
932 * length of the curve. We know the curve is mostly flat, so we just map the length of the
933 * screen along the arc-length proportionally (1/arclength).
934 */
935 public float getDeltaPForY(int downY, int y) {
Winsonf0d1c442015-12-01 11:04:45 -0800936 float deltaP = (float) (y - downY) / mStackRect.height() *
Winson4993c2f2015-11-19 10:06:06 -0800937 mUnfocusedCurveInterpolator.getArcLength();
Winson23afcae2015-10-28 11:14:54 -0700938 return -deltaP;
939 }
940
941 /**
942 * This is the inverse of {@link #getDeltaPForY}. Given a movement along the arc length
943 * of the curve, map back to the screen y.
944 */
945 public int getYForDeltaP(float downScrollP, float p) {
Winsonf0d1c442015-12-01 11:04:45 -0800946 int y = (int) ((p - downScrollP) * mStackRect.height() *
Winson4993c2f2015-11-19 10:06:06 -0800947 (1f / mUnfocusedCurveInterpolator.getArcLength()));
Winson23afcae2015-10-28 11:14:54 -0700948 return -y;
949 }
950
Winson4993c2f2015-11-19 10:06:06 -0800951 /**
Winson59924fe2016-03-17 14:13:18 -0700952 * Returns the task stack bounds in the current orientation. This rect takes into account the
953 * top and right system insets (but not the bottom inset) and left/right paddings, but _not_
954 * the top/bottom padding or insets.
955 */
956 public void getTaskStackBounds(Rect windowRect, int topInset, int rightInset,
Winson008ee15f2016-03-18 17:17:25 -0700957 Rect taskStackBounds) {
Winsona1ededd2016-03-25 12:23:12 -0700958 taskStackBounds.set(windowRect.left, windowRect.top + topInset,
959 windowRect.right - rightInset, windowRect.bottom);
Winson59924fe2016-03-17 14:13:18 -0700960
961 // Ensure that the new width is at most the smaller display edge size
Winson21700932016-03-24 17:26:23 -0700962 SystemServicesProxy ssp = Recents.getSystemServices();
963 Rect displayRect = ssp.getDisplayRect();
Winson59924fe2016-03-17 14:13:18 -0700964 int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
965 WIDTH);
Winson21700932016-03-24 17:26:23 -0700966 int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
967 if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
968 // If we are in landscape, calculate the width of the stack in portrait and ensure that
969 // we are not larger than that size
970 Rect portraitDisplayRect = new Rect(0, 0,
971 Math.min(displayRect.width(), displayRect.height()),
972 Math.max(displayRect.width(), displayRect.height()));
973 int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
974 mBaseSideMargin, mMinMargin, WIDTH);
975 targetStackWidth = Math.min(targetStackWidth,
976 portraitDisplayRect.width() - 2 * portraitSideMargin);
977 }
Winson59924fe2016-03-17 14:13:18 -0700978 taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
979 }
980
981 /**
Winson59924fe2016-03-17 14:13:18 -0700982 * Retrieves resources that are constant regardless of the current configuration of the device.
983 */
984 public static int getDimensionForDevice(Resources res, int phoneResId,
985 int tabletResId, int xlargeTabletResId) {
986 return getDimensionForDevice(res, phoneResId, phoneResId, tabletResId, tabletResId,
987 xlargeTabletResId, xlargeTabletResId);
988 }
989
990 /**
991 * Retrieves resources that are constant regardless of the current configuration of the device.
992 */
993 public static int getDimensionForDevice(Resources res, int phonePortResId, int phoneLandResId,
994 int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
995 int xlargeTabletLandResId) {
996 RecentsConfiguration config = Recents.getConfiguration();
997 boolean isLandscape = Recents.getSystemServices().getDisplayOrientation() ==
998 Configuration.ORIENTATION_LANDSCAPE;
999 if (config.isXLargeScreen) {
1000 return res.getDimensionPixelSize(isLandscape
1001 ? xlargeTabletLandResId
1002 : xlargeTabletPortResId);
1003 } else if (config.isLargeScreen) {
1004 return res.getDimensionPixelSize(isLandscape
1005 ? tabletLandResId
1006 : tabletPortResId);
1007 } else {
1008 return res.getDimensionPixelSize(isLandscape
1009 ? phoneLandResId
1010 : phonePortResId);
1011 }
1012 }
1013
1014 /**
1015 * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the
1016 * stack height).
1017 */
1018 private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) {
1019 float offset = (fromSide == FROM_TOP)
1020 ? mStackRect.height() - y
1021 : y;
1022 float offsetPct = offset / mStackRect.height();
1023 return mUnfocusedCurveInterpolator.getX(offsetPct);
1024 }
1025
1026 /**
Winson4993c2f2015-11-19 10:06:06 -08001027 * Creates a new path for the focused curve.
1028 */
1029 private Path constructFocusedCurve() {
Winson4993c2f2015-11-19 10:06:06 -08001030 // Initialize the focused curve. This curve is a piecewise curve composed of several
Winson68088812016-02-12 16:06:04 -08001031 // linear pieces that goes from (0,1) through (0.5, peek height offset),
1032 // (0.5, bottom task offsets), and (1,0).
1033 float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
Winson59924fe2016-03-17 14:13:18 -07001034 float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
1035 mStackRect.height();
Winson4993c2f2015-11-19 10:06:06 -08001036 Path p = new Path();
1037 p.moveTo(0f, 1f);
Winson68088812016-02-12 16:06:04 -08001038 p.lineTo(0.5f, 1f - topPeekHeightPct);
Winson59924fe2016-03-17 14:13:18 -07001039 p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
Winson4993c2f2015-11-19 10:06:06 -08001040 p.lineTo(1f, 0f);
Winson36a5a2c2015-10-29 18:04:39 -07001041 return p;
1042 }
1043
Winson4993c2f2015-11-19 10:06:06 -08001044 /**
1045 * Creates a new path for the unfocused curve.
1046 */
1047 private Path constructUnfocusedCurve() {
Winson4993c2f2015-11-19 10:06:06 -08001048 // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic
1049 // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0). This
1050 // ensures that we match the range, at which 0.5 represents the stack scroll at the current
1051 // task progress. Because the height offset can change depending on a resource, we compute
1052 // the control point of the second bezier such that between it and a first known point,
1053 // there is a tangent at (0.5, peek height offset).
1054 float cpoint1X = 0.4f;
Winson59924fe2016-03-17 14:13:18 -07001055 float cpoint1Y = 0.975f;
1056 float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
1057 float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
Winson4993c2f2015-11-19 10:06:06 -08001058 float b = 1f - slope * cpoint1X;
Winson12cb9202016-03-04 16:55:16 -08001059 float cpoint2X = 0.65f;
Winson4993c2f2015-11-19 10:06:06 -08001060 float cpoint2Y = slope * cpoint2X + b;
1061 Path p = new Path();
1062 p.moveTo(0f, 1f);
Winson59924fe2016-03-17 14:13:18 -07001063 p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct);
1064 p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
Winson4993c2f2015-11-19 10:06:06 -08001065 return p;
Winson23afcae2015-10-28 11:14:54 -07001066 }
Winsonf24f2162016-01-05 12:11:55 -08001067
1068 /**
Winson12858a62016-02-24 11:06:01 -08001069 * Creates a new path for the focused dim curve.
1070 */
1071 private Path constructFocusedDimCurve() {
1072 Path p = new Path();
1073 // The focused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
1074 // task), then goes back to max dim at the next task
1075 p.moveTo(0f, MAX_DIM);
1076 p.lineTo(0.5f, 0f);
1077 p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), MAX_DIM);
1078 p.lineTo(1f, MAX_DIM);
1079 return p;
1080 }
1081
1082 /**
1083 * Creates a new path for the unfocused dim curve.
1084 */
1085 private Path constructUnfocusedDimCurve() {
Winson59924fe2016-03-17 14:13:18 -07001086 float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
1087 float cpoint2X = focusX + (1f - focusX) / 2;
Winson12858a62016-02-24 11:06:01 -08001088 Path p = new Path();
1089 // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
1090 // task), then goes back to max dim towards the front of the stack
1091 p.moveTo(0f, MAX_DIM);
Winson59924fe2016-03-17 14:13:18 -07001092 p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f);
1093 p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM);
Winson12858a62016-02-24 11:06:01 -08001094 return p;
1095 }
1096
Winson59924fe2016-03-17 14:13:18 -07001097 /**
1098 * Scales the given {@param value} to the scale of the {@param instance} rect relative to the
1099 * {@param other} rect in the {@param extent} side.
1100 */
1101 private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
1102 @Extent int extent) {
1103 if (extent == WIDTH) {
Winsona1ededd2016-03-25 12:23:12 -07001104 float scale = Utilities.clamp01((float) instance.width() / other.width());
1105 return Math.max(minValue, (int) (scale * value));
Winson59924fe2016-03-17 14:13:18 -07001106 } else if (extent == HEIGHT) {
Winsona1ededd2016-03-25 12:23:12 -07001107 float scale = Utilities.clamp01((float) instance.height() / other.height());
1108 return Math.max(minValue, (int) (scale * value));
Winson59924fe2016-03-17 14:13:18 -07001109 }
1110 return value;
1111 }
Winson12858a62016-02-24 11:06:01 -08001112
1113 /**
Winsonf24f2162016-01-05 12:11:55 -08001114 * Updates the current transforms that would put a TaskView at the front and back of the stack.
1115 */
1116 private void updateFrontBackTransforms() {
Winson9317f862016-01-07 16:59:53 -08001117 // Return early if we have not yet initialized
1118 if (mStackRect.isEmpty()) {
1119 return;
1120 }
1121
Winson68088812016-02-12 16:06:04 -08001122 float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin,
1123 mFocusedRange.relativeMin);
1124 float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
1125 mFocusedRange.relativeMax);
Winson8f97c832016-03-04 11:32:03 -08001126 getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
Winson14991502016-02-15 15:40:08 -08001127 true /* ignoreSingleTaskCase */, true /* forceUpdate */);
Winson8f97c832016-03-04 11:32:03 -08001128 getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
Winson14991502016-02-15 15:40:08 -08001129 true /* ignoreSingleTaskCase */, true /* forceUpdate */);
Winsonf24f2162016-01-05 12:11:55 -08001130 mBackOfStackTransform.visible = true;
1131 mFrontOfStackTransform.visible = true;
1132 }
Winson Chungabedcab2014-08-15 12:27:37 -07001133}