Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.systemui.recents.views; |
| 18 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 19 | import android.annotation.IntDef; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 20 | import android.content.Context; |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 21 | import android.content.res.Configuration; |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 22 | import android.content.res.Resources; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 23 | import android.graphics.Path; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 24 | import android.graphics.Rect; |
Winson | 5500390 | 2016-01-12 12:00:37 -0800 | [diff] [blame] | 25 | import android.util.ArraySet; |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 26 | import android.util.SparseArray; |
| 27 | import android.util.SparseIntArray; |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 28 | import android.view.ViewDebug; |
Winson | c0d7058 | 2016-01-29 10:24:39 -0800 | [diff] [blame] | 29 | |
Winson | 2536c7e | 2015-10-01 15:49:31 -0700 | [diff] [blame] | 30 | import com.android.systemui.R; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 31 | import com.android.systemui.recents.Recents; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 32 | import com.android.systemui.recents.RecentsActivityLaunchState; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 33 | import com.android.systemui.recents.RecentsConfiguration; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 34 | import com.android.systemui.recents.RecentsDebugFlags; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 35 | import com.android.systemui.recents.misc.FreePathInterpolator; |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 36 | import com.android.systemui.recents.misc.SystemServicesProxy; |
Winson Chung | ffa2ec6 | 2014-07-03 15:54:42 -0700 | [diff] [blame] | 37 | import com.android.systemui.recents.misc.Utilities; |
Winson Chung | a433fa9 | 2014-07-08 21:50:31 -0700 | [diff] [blame] | 38 | import com.android.systemui.recents.model.Task; |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 39 | import com.android.systemui.recents.model.TaskStack; |
Winson Chung | a433fa9 | 2014-07-08 21:50:31 -0700 | [diff] [blame] | 40 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 41 | import java.lang.annotation.Retention; |
| 42 | import java.lang.annotation.RetentionPolicy; |
Winson Chung | a433fa9 | 2014-07-08 21:50:31 -0700 | [diff] [blame] | 43 | import java.util.ArrayList; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 44 | import java.util.List; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 45 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 46 | /** |
| 47 | * Used to describe a visible range that can be normalized to [0, 1]. |
| 48 | */ |
| 49 | class 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 | } |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 102 | |
| 103 | /** |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 104 | * 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 108 | */ |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 109 | public class TaskStackLayoutAlgorithm { |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 110 | |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 111 | // 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 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 116 | // The medium/maximum dim on the tasks |
| 117 | private static final float MED_DIM = 0.15f; |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 118 | private static final float MAX_DIM = 0.25f; |
Winson | 1bcf3c4 | 2016-02-10 13:29:39 -0800 | [diff] [blame] | 119 | |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 120 | // The various focus states |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 121 | public static final int STATE_FOCUSED = 1; |
| 122 | public static final int STATE_UNFOCUSED = 0; |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 123 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 124 | // 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 | |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 138 | public interface TaskStackLayoutAlgorithmCallbacks { |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 139 | void onFocusStateChanged(int prevFocusState, int curFocusState); |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 140 | } |
| 141 | |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 142 | /** |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 143 | * The various stack/freeform states. |
| 144 | */ |
| 145 | public static class StackState { |
| 146 | |
Winson Chung | aa4f800 | 2015-12-17 10:27:55 -0500 | [diff] [blame] | 147 | public static final StackState FREEFORM_ONLY = new StackState(1f, 255); |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 148 | public static final StackState STACK_ONLY = new StackState(0f, 0); |
| 149 | public static final StackState SPLIT = new StackState(0.5f, 255); |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 150 | |
| 151 | public final float freeformHeightPct; |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 152 | public final int freeformBackgroundAlpha; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 153 | |
| 154 | /** |
| 155 | * @param freeformHeightPct the percentage of the stack height (not including paddings) to |
| 156 | * allocate to the freeform workspace |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 157 | * @param freeformBackgroundAlpha the background alpha for the freeform workspace |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 158 | */ |
Winson | ef06413 | 2016-01-05 12:11:31 -0800 | [diff] [blame] | 159 | private StackState(float freeformHeightPct, int freeformBackgroundAlpha) { |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 160 | this.freeformHeightPct = freeformHeightPct; |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 161 | this.freeformBackgroundAlpha = freeformBackgroundAlpha; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 162 | } |
| 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(); |
Winson | 4b057c6 | 2016-01-12 15:01:52 -0800 | [diff] [blame] | 170 | int freeformCount = stack.getFreeformTaskCount(); |
| 171 | int stackCount = stack.getStackTaskCount(); |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 172 | 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, |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 189 | 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; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 193 | int ffPaddedHeight = (int) (availableHeight * freeformHeightPct); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 194 | 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, |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 200 | taskStackBounds.top, |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 201 | taskStackBounds.right, |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 202 | taskStackBounds.bottom); |
| 203 | if (ffPaddedHeight > 0) { |
| 204 | stackRectOut.top += ffPaddedHeight; |
| 205 | } else { |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 206 | stackRectOut.top += topMargin; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 207 | } |
| 208 | } |
| 209 | } |
| 210 | |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 211 | // 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 | |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 223 | Context mContext; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 224 | private StackState mState = StackState.SPLIT; |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 225 | private TaskStackLayoutAlgorithmCallbacks mCb; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 226 | |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 227 | // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot. |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 228 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 229 | public Rect mTaskRect = new Rect(); |
Winson | 008ee15f | 2016-03-18 17:17:25 -0700 | [diff] [blame] | 230 | // The freeform workspace bounds, inset by the top system insets and is a fixed height |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 231 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 232 | public Rect mFreeformRect = new Rect(); |
Winson | 008ee15f | 2016-03-18 17:17:25 -0700 | [diff] [blame] | 233 | // The stack bounds, inset from the top system insets, and runs to the bottom of the screen |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 234 | @ViewDebug.ExportedProperty(category="recents") |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 235 | public Rect mStackRect = new Rect(); |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 236 | // This is the current system insets |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 237 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 238 | public Rect mSystemInsets = new Rect(); |
Winson | 8f6ee48 | 2016-03-18 17:51:48 -0700 | [diff] [blame] | 239 | // This is the bounds of the stack action above the stack rect |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 240 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 8f6ee48 | 2016-03-18 17:51:48 -0700 | [diff] [blame] | 241 | public Rect mStackActionButtonRect = new Rect(); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 242 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 243 | // The visible ranges when the stack is focused and unfocused |
| 244 | private Range mUnfocusedRange; |
| 245 | private Range mFocusedRange; |
| 246 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 247 | // The base top margin for the stack from the system insets |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 248 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 249 | private int mBaseTopMargin; |
| 250 | // The base side margin for the stack from the system insets |
Winson | 73492c5 | 2016-03-09 17:44:54 -0800 | [diff] [blame] | 251 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 252 | 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; |
Winson | 008ee15f | 2016-03-18 17:17:25 -0700 | [diff] [blame] | 257 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 258 | // The gap between the freeform and stack layouts |
| 259 | @ViewDebug.ExportedProperty(category="recents") |
| 260 | private int mFreeformStackGap; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 261 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 262 | // 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 |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 272 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 273 | private int mFocusedTopPeekHeight; |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 274 | // The height between the bottom margin and the top of task in front of the focused task |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 275 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 276 | private int mFocusedBottomPeekHeight; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 277 | |
Winson | 250608a | 2015-11-24 15:00:31 -0800 | [diff] [blame] | 278 | // The offset from the bottom of the stack to the bottom of the bounds when the stack is |
| 279 | // scrolled to the front |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 280 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 281 | 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 | |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 289 | // 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 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 296 | // Indexed from the front of the stack, the normalized x in the unfocused range for each task |
| 297 | private float[] mInitialNormX; |
| 298 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 299 | // The state of the stack focus (0..1), which controls the transition of the stack from the |
| 300 | // focused to non-focused state |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 301 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 302 | private int mFocusState; |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 303 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 304 | // The smallest scroll progress, at this value, the back most task will be visible |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 305 | @ViewDebug.ExportedProperty(category="recents") |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 306 | float mMinScrollP; |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 307 | // The largest scroll progress, at this value, the front most task will be visible above the |
| 308 | // navigation bar |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 309 | @ViewDebug.ExportedProperty(category="recents") |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 310 | float mMaxScrollP; |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 311 | // The initial progress that the scroller is set when you first enter recents |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 312 | @ViewDebug.ExportedProperty(category="recents") |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 313 | float mInitialScrollP; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 314 | // The task progress for the front-most task in the stack |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 315 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 316 | float mFrontMostTaskP; |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 317 | |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 318 | // The last computed task counts |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 319 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 320 | int mNumStackTasks; |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 321 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 322 | int mNumFreeformTasks; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 323 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 324 | // The min/max z translations |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 325 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 326 | int mMinTranslationZ; |
Winson | 231bc9c | 2016-02-09 12:31:00 -0800 | [diff] [blame] | 327 | @ViewDebug.ExportedProperty(category="recents") |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 328 | int mMaxTranslationZ; |
| 329 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 330 | // Optimization, allows for quick lookup of task -> index |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 331 | private SparseIntArray mTaskIndexMap = new SparseIntArray(); |
| 332 | private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>(); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 333 | |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 334 | // The freeform workspace layout |
| 335 | FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm; |
| 336 | |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 337 | // 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 | |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 341 | public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) { |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 342 | Resources res = context.getResources(); |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 343 | mContext = context; |
| 344 | mCb = cb; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 345 | mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 346 | 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); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 356 | mFreeformStackGap = |
| 357 | res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin); |
| 358 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 359 | reloadOnConfigurationChange(context); |
| 360 | } |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 361 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 362 | /** |
| 363 | * Reloads the layout for the current configuration. |
| 364 | */ |
| 365 | public void reloadOnConfigurationChange(Context context) { |
| 366 | Resources res = context.getResources(); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 367 | 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)); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 371 | mFocusState = getInitialFocusState(); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 372 | 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); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 391 | mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context); |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 392 | } |
| 393 | |
| 394 | /** |
| 395 | * Resets this layout when the stack view is reset. |
| 396 | */ |
| 397 | public void reset() { |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 398 | mTaskIndexOverrideMap.clear(); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 399 | setFocusState(getInitialFocusState()); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 400 | } |
| 401 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 402 | /** |
| 403 | * Sets the system insets. |
| 404 | */ |
| 405 | public void setSystemInsets(Rect systemInsets) { |
| 406 | mSystemInsets.set(systemInsets); |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 407 | } |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 408 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 409 | /** |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 410 | * Sets the focused state. |
| 411 | */ |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 412 | public void setFocusState(int focusState) { |
| 413 | int prevFocusState = mFocusState; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 414 | mFocusState = focusState; |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 415 | updateFrontBackTransforms(); |
Winson | 1c84614 | 2016-01-22 11:34:38 -0800 | [diff] [blame] | 416 | if (mCb != null) { |
| 417 | mCb.onFocusStateChanged(prevFocusState, focusState); |
| 418 | } |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 419 | } |
| 420 | |
| 421 | /** |
| 422 | * Gets the focused state. |
| 423 | */ |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 424 | public int getFocusState() { |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 425 | return mFocusState; |
| 426 | } |
| 427 | |
| 428 | /** |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 429 | * Computes the stack and task rects. The given task stack bounds already has the top/right |
| 430 | * insets and left/right padding already applied. |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 431 | */ |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 432 | public void initialize(Rect windowRect, Rect taskStackBounds, StackState state) { |
| 433 | SystemServicesProxy ssp = Recents.getSystemServices(); |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 434 | Rect lastStackRect = new Rect(mStackRect); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 435 | Rect displayRect = ssp.getDisplayRect(); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 436 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 437 | 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 |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 445 | mState = state; |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 446 | mStackBottomOffset = mSystemInsets.bottom + bottomMargin; |
| 447 | state.computeRects(mFreeformRect, mStackRect, taskStackBounds, topMargin, |
| 448 | mFreeformStackGap, mStackBottomOffset); |
| 449 | |
Winson | 8f6ee48 | 2016-03-18 17:51:48 -0700 | [diff] [blame] | 450 | // The stack action button will take the full un-padded header space above the stack |
| 451 | mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin, |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 452 | mStackRect.right, mStackRect.top + mFocusedTopPeekHeight); |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 453 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 454 | // 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); |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 460 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 461 | // Short circuit here if the stack rects haven't changed so we don't do all the work below |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 462 | 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(); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 474 | } |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 475 | } |
| 476 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 477 | /** |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 478 | * Computes the minimum and maximum scroll progress values and the progress values for each task |
| 479 | * in the stack. |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 480 | */ |
Winson | 8aa9959 | 2016-01-19 15:07:07 -0800 | [diff] [blame] | 481 | void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet) { |
Winson | eca4ab6 | 2015-11-04 10:50:28 -0800 | [diff] [blame] | 482 | SystemServicesProxy ssp = Recents.getSystemServices(); |
Winson | b61e654 | 2016-02-04 14:37:18 -0800 | [diff] [blame] | 483 | RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 484 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 485 | // Clear the progress map |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 486 | mTaskIndexMap.clear(); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 487 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 488 | // Return early if we have no tasks |
Winson | 250608a | 2015-11-24 15:00:31 -0800 | [diff] [blame] | 489 | ArrayList<Task> tasks = stack.getStackTasks(); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 490 | if (tasks.isEmpty()) { |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 491 | mFrontMostTaskP = 0; |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 492 | mMinScrollP = mMaxScrollP = mInitialScrollP = 0; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 493 | mNumStackTasks = mNumFreeformTasks = 0; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 494 | return; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 495 | } |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 496 | |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 497 | // 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); |
Winson | 8aa9959 | 2016-01-19 15:07:07 -0800 | [diff] [blame] | 502 | if (ignoreTasksSet.contains(task.key)) { |
Winson | 3e87474 | 2016-01-07 10:08:17 -0800 | [diff] [blame] | 503 | continue; |
| 504 | } |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 505 | if (task.isFreeformTask()) { |
| 506 | freeformTasks.add(task); |
| 507 | } else { |
| 508 | stackTasks.add(task); |
| 509 | } |
| 510 | } |
| 511 | mNumStackTasks = stackTasks.size(); |
| 512 | mNumFreeformTasks = freeformTasks.size(); |
| 513 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 514 | // 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); |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 519 | mTaskIndexMap.put(task.key.id, i); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 520 | } |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 521 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 522 | // Update the freeform tasks |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 523 | if (!freeformTasks.isEmpty()) { |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 524 | mFreeformLayoutAlgorithm.update(freeformTasks, this); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 525 | } |
| 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); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 537 | } else { |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 538 | mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 539 | } |
Winson | 500ba75 | 2016-03-30 11:59:22 -0700 | [diff] [blame] | 540 | mInitialNormX = null; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 541 | } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { |
| 542 | // If there is one stack task, ignore the min/max/initial scroll positions |
| 543 | mMinScrollP = 0; |
| 544 | mMaxScrollP = 0; |
| 545 | mInitialScrollP = 0; |
Winson | 500ba75 | 2016-03-30 11:59:22 -0700 | [diff] [blame] | 546 | mInitialNormX = null; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 547 | } else { |
| 548 | // Set the max scroll to be the point where the front most task is visible with the |
| 549 | // stack bottom offset |
| 550 | int maxBottomOffset = mStackBottomOffset + mTaskRect.height(); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 551 | float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 552 | mUnfocusedRange.offset(0f); |
| 553 | mMinScrollP = 0; |
| 554 | mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) - |
| 555 | Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX))); |
| 556 | boolean scrollToFront = launchState.launchedFromHome || |
Winson | c69249f | 2016-03-28 13:38:39 -0700 | [diff] [blame] | 557 | launchState.launchedViaDockGesture; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 558 | if (scrollToFront) { |
| 559 | mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 560 | mInitialNormX = null; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 561 | } else { |
Winson | 3b6ba1a | 2016-03-22 15:37:54 -0700 | [diff] [blame] | 562 | // We are overriding the initial two task positions, so set the initial scroll |
| 563 | // position to match the second task (aka focused task) position |
| 564 | float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP); |
| 565 | mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2)) |
| 566 | - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX))); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 567 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 568 | // Set the initial scroll to the predefined state (which differs from the stack) |
| 569 | mInitialNormX = new float[] { |
| 570 | getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset, |
| 571 | FROM_BOTTOM), |
Winson | 3b6ba1a | 2016-03-22 15:37:54 -0700 | [diff] [blame] | 572 | initialTopNormX |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 573 | }; |
| 574 | } |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 575 | } |
| 576 | } |
| 577 | |
| 578 | public void updateToInitialState(List<Task> tasks) { |
| 579 | if (mInitialNormX == null) { |
| 580 | return; |
| 581 | } |
| 582 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 583 | mUnfocusedRange.offset(0f); |
| 584 | int taskCount = tasks.size(); |
| 585 | for (int i = taskCount - 1; i >= 0; i--) { |
| 586 | int indexFromFront = taskCount - i - 1; |
| 587 | if (indexFromFront >= mInitialNormX.length) { |
| 588 | break; |
| 589 | } |
| 590 | float newTaskProgress = mInitialScrollP + |
| 591 | mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]); |
| 592 | mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress); |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 593 | } |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 594 | } |
| 595 | |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 596 | /** |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 597 | * Adds and override task progress for the given task when transitioning from focused to |
| 598 | * unfocused state. |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 599 | */ |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 600 | public void addUnfocusedTaskOverride(Task task, float stackScroll) { |
| 601 | if (mFocusState != STATE_UNFOCUSED) { |
| 602 | mFocusedRange.offset(stackScroll); |
| 603 | mUnfocusedRange.offset(stackScroll); |
| 604 | float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id)); |
| 605 | float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX); |
| 606 | float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY); |
| 607 | float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX); |
| 608 | if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) { |
| 609 | mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress); |
| 610 | } |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 611 | } |
| 612 | } |
| 613 | |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 614 | public void clearUnfocusedTaskOverrides() { |
| 615 | mTaskIndexOverrideMap.clear(); |
| 616 | } |
| 617 | |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 618 | /** |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 619 | * Updates this stack when a scroll happens. |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 620 | */ |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 621 | public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) { |
Winson | 8873754 | 2016-02-17 13:27:33 -0800 | [diff] [blame] | 622 | if (deltaScroll == 0f) { |
| 623 | return; |
| 624 | } |
| 625 | |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 626 | for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) { |
| 627 | int taskId = mTaskIndexOverrideMap.keyAt(i); |
| 628 | float x = mTaskIndexMap.get(taskId); |
| 629 | float overrideX = mTaskIndexOverrideMap.get(taskId, 0f); |
| 630 | float newOverrideX = overrideX + deltaScroll; |
| 631 | mUnfocusedRange.offset(stackScroll); |
| 632 | boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f || |
| 633 | mUnfocusedRange.getNormalizedX(newOverrideX) > 1f; |
| 634 | if (outOfBounds || (overrideX >= x && x >= newOverrideX) || |
| 635 | (overrideX <= x && x <= newOverrideX)) { |
| 636 | // Remove the override once we reach the original task index |
| 637 | mTaskIndexOverrideMap.removeAt(i); |
| 638 | } else if ((overrideX >= x && deltaScroll <= 0f) || |
| 639 | (overrideX <= x && deltaScroll >= 0f)) { |
| 640 | // Scrolling from override x towards x, then lock the task in place |
| 641 | mTaskIndexOverrideMap.put(taskId, newOverrideX); |
| 642 | } else { |
| 643 | // Scrolling override x away from x, we should still move the scroll towards x |
| 644 | float deltaX = overrideX - x; |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 645 | newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll)); |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 646 | mTaskIndexOverrideMap.put(taskId, x + newOverrideX); |
| 647 | } |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 648 | } |
| 649 | } |
| 650 | |
| 651 | /** |
| 652 | * Returns the default focus state. |
| 653 | */ |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 654 | public int getInitialFocusState() { |
| 655 | RecentsDebugFlags debugFlags = Recents.getDebugFlags(); |
| 656 | if (debugFlags.isPagingEnabled()) { |
| 657 | return STATE_FOCUSED; |
| 658 | } else { |
| 659 | return STATE_UNFOCUSED; |
| 660 | } |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 661 | } |
| 662 | |
| 663 | /** |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 664 | * Returns the TaskViewTransform that would put the task just off the back of the stack. |
Winson | bb41095 | 2015-12-04 14:34:11 -0800 | [diff] [blame] | 665 | */ |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 666 | public TaskViewTransform getBackOfStackTransform() { |
| 667 | return mBackOfStackTransform; |
Winson | bb41095 | 2015-12-04 14:34:11 -0800 | [diff] [blame] | 668 | } |
| 669 | |
| 670 | /** |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 671 | * Returns the TaskViewTransform that would put the task just off the front of the stack. |
Winson | 0e0d85c | 2015-12-03 11:35:25 -0800 | [diff] [blame] | 672 | */ |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 673 | public TaskViewTransform getFrontOfStackTransform() { |
| 674 | return mFrontOfStackTransform; |
Winson | 0e0d85c | 2015-12-03 11:35:25 -0800 | [diff] [blame] | 675 | } |
| 676 | |
| 677 | /** |
Winson | a78a8f3 | 2015-12-03 10:55:01 -0800 | [diff] [blame] | 678 | * |
| 679 | * Returns the current stack state. |
| 680 | */ |
| 681 | public StackState getStackState() { |
| 682 | return mState; |
| 683 | } |
| 684 | |
| 685 | /** |
Winson | 8873754 | 2016-02-17 13:27:33 -0800 | [diff] [blame] | 686 | * Returns whether this stack layout has been initialized. |
| 687 | */ |
| 688 | public boolean isInitialized() { |
| 689 | return !mStackRect.isEmpty(); |
| 690 | } |
| 691 | |
| 692 | /** |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 693 | * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial |
| 694 | * stack scroll. Requires that update() is called first. |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 695 | */ |
| 696 | public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) { |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 697 | // Ensure minimum visibility count |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 698 | if (tasks.size() <= 1) { |
| 699 | return new VisibilityReport(1, 1); |
| 700 | } |
| 701 | |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 702 | // Quick return when there are no stack tasks |
| 703 | if (mNumStackTasks == 0) { |
| 704 | return new VisibilityReport(Math.max(mNumFreeformTasks, 1), |
| 705 | Math.max(mNumFreeformTasks, 1)); |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 706 | } |
| 707 | |
| 708 | // Otherwise, walk backwards in the stack and count the number of tasks and visible |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 709 | // thumbnails and add that to the total freeform task count |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 710 | TaskViewTransform tmpTransform = new TaskViewTransform(); |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 711 | Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 712 | currentRange.offset(mInitialScrollP); |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 713 | int taskBarHeight = mContext.getResources().getDimensionPixelSize( |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 714 | R.dimen.recents_task_view_header_height); |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 715 | int numVisibleTasks = Math.max(mNumFreeformTasks, 1); |
| 716 | int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 717 | float prevScreenY = Integer.MAX_VALUE; |
| 718 | for (int i = tasks.size() - 1; i >= 0; i--) { |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 719 | Task task = tasks.get(i); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 720 | |
| 721 | // Skip freeform |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 722 | if (task.isFreeformTask()) { |
| 723 | continue; |
| 724 | } |
| 725 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 726 | // Skip invisible |
| 727 | float taskProgress = getStackScrollForTask(task); |
| 728 | if (!currentRange.isInRange(taskProgress)) { |
| 729 | continue; |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 730 | } |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 731 | |
Winson | eca4ab6 | 2015-11-04 10:50:28 -0800 | [diff] [blame] | 732 | boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task); |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 733 | if (isFrontMostTaskInGroup) { |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 734 | getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState, |
| 735 | tmpTransform, null, false /* ignoreSingleTaskCase */, |
| 736 | false /* forceUpdate */); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 737 | float screenY = tmpTransform.rect.top; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 738 | boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight; |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 739 | if (hasVisibleThumbnail) { |
| 740 | numVisibleThumbnails++; |
| 741 | numVisibleTasks++; |
| 742 | prevScreenY = screenY; |
| 743 | } else { |
| 744 | // Once we hit the next front most task that does not have a visible thumbnail, |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 745 | // walk through remaining visible set |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 746 | for (int j = i; j >= 0; j--) { |
| 747 | numVisibleTasks++; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 748 | taskProgress = getStackScrollForTask(tasks.get(j)); |
| 749 | if (!currentRange.isInRange(taskProgress)) { |
| 750 | continue; |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 751 | } |
| 752 | } |
| 753 | break; |
| 754 | } |
| 755 | } else if (!isFrontMostTaskInGroup) { |
| 756 | // Affiliated task, no thumbnail |
| 757 | numVisibleTasks++; |
| 758 | } |
| 759 | } |
| 760 | return new VisibilityReport(numVisibleTasks, numVisibleThumbnails); |
| 761 | } |
| 762 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 763 | /** |
| 764 | * Returns the transform for the given task. This transform is relative to the mTaskRect, which |
| 765 | * is what the view is measured and laid out with. |
| 766 | */ |
Winson Chung | a91c293 | 2014-11-07 15:02:38 -0800 | [diff] [blame] | 767 | public TaskViewTransform getStackTransform(Task task, float stackScroll, |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 768 | TaskViewTransform transformOut, TaskViewTransform frontTransform) { |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 769 | return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 770 | false /* forceUpdate */, false /* ignoreTaskOverrides */); |
| 771 | } |
| 772 | |
| 773 | public TaskViewTransform getStackTransform(Task task, float stackScroll, |
| 774 | TaskViewTransform transformOut, TaskViewTransform frontTransform, |
| 775 | boolean ignoreTaskOverrides) { |
| 776 | return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, |
| 777 | false /* forceUpdate */, ignoreTaskOverrides); |
Winson | 9bbd2861 | 2016-02-03 14:52:12 -0800 | [diff] [blame] | 778 | } |
| 779 | |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 780 | public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState, |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 781 | TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate, |
| 782 | boolean ignoreTaskOverrides) { |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 783 | if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) { |
| 784 | mFreeformLayoutAlgorithm.getTransform(task, transformOut, this); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 785 | return transformOut; |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 786 | } else { |
| 787 | // Return early if we have an invalid index |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 788 | int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1); |
| 789 | if (task == null || nonOverrideTaskProgress == -1) { |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 790 | transformOut.reset(); |
| 791 | return transformOut; |
| 792 | } |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 793 | float taskProgress = ignoreTaskOverrides |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 794 | ? nonOverrideTaskProgress |
Winson | e693aaf | 2016-03-01 12:05:59 -0800 | [diff] [blame] | 795 | : getStackScrollForTask(task); |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 796 | getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState, |
| 797 | transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate); |
Winson | 9bbd2861 | 2016-02-03 14:52:12 -0800 | [diff] [blame] | 798 | return transformOut; |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 799 | } |
Winson Chung | d6c3db5 | 2014-07-22 17:02:16 -0700 | [diff] [blame] | 800 | } |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 801 | |
Winson | 8aa9959 | 2016-01-19 15:07:07 -0800 | [diff] [blame] | 802 | /** |
Jorim Jaggi | c6c89a8 | 2016-01-28 17:48:21 -0800 | [diff] [blame] | 803 | * Like {@link #getStackTransform}, but in screen coordinates |
| 804 | */ |
| 805 | public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll, |
| 806 | TaskViewTransform transformOut, TaskViewTransform frontTransform) { |
| 807 | Rect windowRect = Recents.getSystemServices().getWindowRect(); |
Winson | dec4430 | 2016-04-01 11:25:35 -0700 | [diff] [blame] | 808 | TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState, |
| 809 | transformOut, frontTransform, true /* forceUpdate */, |
| 810 | false /* ignoreTaskOverrides */); |
Jorim Jaggi | c6c89a8 | 2016-01-28 17:48:21 -0800 | [diff] [blame] | 811 | transform.rect.offset(windowRect.left, windowRect.top); |
| 812 | return transform; |
| 813 | } |
| 814 | |
| 815 | /** |
Winson | 8aa9959 | 2016-01-19 15:07:07 -0800 | [diff] [blame] | 816 | * Update/get the transform. |
| 817 | * |
| 818 | * @param ignoreSingleTaskCase When set, will ensure that the transform computed does not take |
| 819 | * into account the special single-task case. This is only used |
| 820 | * internally to ensure that we can calculate the transform for any |
| 821 | * position in the stack. |
| 822 | */ |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 823 | public void getStackTransform(float taskProgress, float nonOverrideTaskProgress, |
| 824 | float stackScroll, int focusState, TaskViewTransform transformOut, |
| 825 | TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) { |
Winson | eca4ab6 | 2015-11-04 10:50:28 -0800 | [diff] [blame] | 826 | SystemServicesProxy ssp = Recents.getSystemServices(); |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 827 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 828 | // Compute the focused and unfocused offset |
Winson | be8f357 | 2016-02-15 16:52:27 -0800 | [diff] [blame] | 829 | float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP); |
| 830 | mUnfocusedRange.offset(boundedStackScroll); |
| 831 | mFocusedRange.offset(boundedStackScroll); |
| 832 | float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress); |
| 833 | float boundedScrollFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress); |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 834 | float boundedScrollUnfocusedNonOverrideRangeX = |
| 835 | mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 836 | mUnfocusedRange.offset(stackScroll); |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 837 | mFocusedRange.offset(stackScroll); |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 838 | boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress); |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 839 | boolean focusedVisible = mFocusedRange.isInRange(taskProgress); |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 840 | |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 841 | // Skip if the task is not visible |
Winson | 9bbd2861 | 2016-02-03 14:52:12 -0800 | [diff] [blame] | 842 | if (!forceUpdate && !unfocusedVisible && !focusedVisible) { |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 843 | transformOut.reset(); |
Winson | 9bbd2861 | 2016-02-03 14:52:12 -0800 | [diff] [blame] | 844 | return; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 845 | } |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 846 | |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 847 | float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress); |
| 848 | float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress); |
| 849 | |
Winson | bb41095 | 2015-12-04 14:34:11 -0800 | [diff] [blame] | 850 | int x = (mStackRect.width() - mTaskRect.width()) / 2; |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 851 | int y; |
| 852 | float z; |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 853 | float dimAlpha; |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 854 | float viewOutlineAlpha; |
Winson | 8aa9959 | 2016-01-19 15:07:07 -0800 | [diff] [blame] | 855 | if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1 && !ignoreSingleTaskCase) { |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 856 | // When there is exactly one task, then decouple the task from the stack and just move |
| 857 | // in screen space |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 858 | float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks; |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 859 | int centerYOffset = (mStackRect.top - mTaskRect.top) + |
Winson | 619e40c | 2016-03-25 16:12:35 -0700 | [diff] [blame] | 860 | (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2; |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 861 | y = centerYOffset + getYForDeltaP(tmpP, 0); |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 862 | z = mMaxTranslationZ; |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 863 | dimAlpha = 0f; |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 864 | viewOutlineAlpha = (OUTLINE_ALPHA_MIN_VALUE + OUTLINE_ALPHA_MAX_VALUE) / 2f; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 865 | |
| 866 | } else { |
| 867 | // Otherwise, update the task to the stack layout |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 868 | int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation( |
| 869 | unfocusedRangeX)) * mStackRect.height()); |
| 870 | int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation( |
| 871 | focusedRangeX)) * mStackRect.height()); |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 872 | float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation( |
Winson | be8f357 | 2016-02-15 16:52:27 -0800 | [diff] [blame] | 873 | boundedScrollUnfocusedRangeX); |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 874 | float focusedDim = mFocusedDimCurveInterpolator.getInterpolation( |
Winson | be8f357 | 2016-02-15 16:52:27 -0800 | [diff] [blame] | 875 | boundedScrollFocusedRangeX); |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 876 | |
| 877 | y = (mStackRect.top - mTaskRect.top) + |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 878 | (int) Utilities.mapRange(focusState, unfocusedY, focusedY); |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 879 | z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX), |
Winson | be8f357 | 2016-02-15 16:52:27 -0800 | [diff] [blame] | 880 | mMinTranslationZ, mMaxTranslationZ); |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 881 | dimAlpha = Utilities.mapRange(focusState, unfocusedDim, focusedDim); |
Winson | be8f357 | 2016-02-15 16:52:27 -0800 | [diff] [blame] | 882 | viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX), |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 883 | OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 884 | } |
| 885 | |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 886 | // Fill out the transform |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 887 | transformOut.scale = 1f; |
Winson | bb41095 | 2015-12-04 14:34:11 -0800 | [diff] [blame] | 888 | transformOut.alpha = 1f; |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 889 | transformOut.translationZ = z; |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 890 | transformOut.dimAlpha = dimAlpha; |
| 891 | transformOut.viewOutlineAlpha = viewOutlineAlpha; |
Winson | 8b1871d | 2015-11-20 09:56:20 -0800 | [diff] [blame] | 892 | transformOut.rect.set(mTaskRect); |
Winson | bb41095 | 2015-12-04 14:34:11 -0800 | [diff] [blame] | 893 | transformOut.rect.offset(x, y); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 894 | Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale); |
Winson | a0731a1 | 2015-12-02 15:10:14 -0800 | [diff] [blame] | 895 | transformOut.visible = (transformOut.rect.top < mStackRect.bottom) && |
| 896 | (frontTransform == null || transformOut.rect.top != frontTransform.rect.top); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 897 | } |
| 898 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 899 | /** |
| 900 | * Returns the untransformed task view bounds. |
| 901 | */ |
Winson | ec417e4 | 2015-09-29 12:42:29 -0700 | [diff] [blame] | 902 | public Rect getUntransformedTaskViewBounds() { |
| 903 | return new Rect(mTaskRect); |
Winson Chung | a4ccb86 | 2014-08-22 15:26:27 -0700 | [diff] [blame] | 904 | } |
| 905 | |
Winson | 88f00ab | 2015-10-05 17:24:00 -0700 | [diff] [blame] | 906 | /** |
| 907 | * Returns the scroll progress to scroll to such that the top of the task is at the top of the |
| 908 | * stack. |
| 909 | */ |
Winson Chung | 78cc96a | 2014-08-12 13:56:03 -0700 | [diff] [blame] | 910 | float getStackScrollForTask(Task t) { |
Winson | 6647413 | 2016-02-23 18:45:47 -0800 | [diff] [blame] | 911 | return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0)); |
Winson Chung | d7b2cb1 | 2014-06-26 15:08:50 -0700 | [diff] [blame] | 912 | } |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 913 | |
| 914 | /** |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 915 | * Returns the original scroll progress to scroll to such that the top of the task is at the top |
| 916 | * of the stack. |
| 917 | */ |
| 918 | float getStackScrollForTaskIgnoreOverrides(Task t) { |
| 919 | return (float) mTaskIndexMap.get(t.key.id, 0); |
| 920 | } |
| 921 | |
| 922 | /** |
| 923 | * Returns the scroll progress to scroll to such that the top of the task at the initial top |
| 924 | * offset (which is at the task's brightest point). |
| 925 | */ |
| 926 | float getStackScrollForTaskAtInitialOffset(Task t) { |
| 927 | float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP); |
| 928 | mUnfocusedRange.offset(0f); |
Winson | f8597b2 | 2016-03-23 18:44:26 -0700 | [diff] [blame] | 929 | return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0, |
| 930 | mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 931 | } |
| 932 | |
| 933 | /** |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 934 | * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc |
| 935 | * length of the curve. We know the curve is mostly flat, so we just map the length of the |
| 936 | * screen along the arc-length proportionally (1/arclength). |
| 937 | */ |
| 938 | public float getDeltaPForY(int downY, int y) { |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 939 | float deltaP = (float) (y - downY) / mStackRect.height() * |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 940 | mUnfocusedCurveInterpolator.getArcLength(); |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 941 | return -deltaP; |
| 942 | } |
| 943 | |
| 944 | /** |
| 945 | * This is the inverse of {@link #getDeltaPForY}. Given a movement along the arc length |
| 946 | * of the curve, map back to the screen y. |
| 947 | */ |
| 948 | public int getYForDeltaP(float downScrollP, float p) { |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 949 | int y = (int) ((p - downScrollP) * mStackRect.height() * |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 950 | (1f / mUnfocusedCurveInterpolator.getArcLength())); |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 951 | return -y; |
| 952 | } |
| 953 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 954 | /** |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 955 | * Returns the task stack bounds in the current orientation. This rect takes into account the |
| 956 | * top and right system insets (but not the bottom inset) and left/right paddings, but _not_ |
| 957 | * the top/bottom padding or insets. |
| 958 | */ |
| 959 | public void getTaskStackBounds(Rect windowRect, int topInset, int rightInset, |
Winson | 008ee15f | 2016-03-18 17:17:25 -0700 | [diff] [blame] | 960 | Rect taskStackBounds) { |
Winson | a1ededd | 2016-03-25 12:23:12 -0700 | [diff] [blame] | 961 | taskStackBounds.set(windowRect.left, windowRect.top + topInset, |
| 962 | windowRect.right - rightInset, windowRect.bottom); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 963 | |
| 964 | // Ensure that the new width is at most the smaller display edge size |
Winson | 2170093 | 2016-03-24 17:26:23 -0700 | [diff] [blame] | 965 | SystemServicesProxy ssp = Recents.getSystemServices(); |
| 966 | Rect displayRect = ssp.getDisplayRect(); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 967 | int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin, |
| 968 | WIDTH); |
Winson | 2170093 | 2016-03-24 17:26:23 -0700 | [diff] [blame] | 969 | int targetStackWidth = taskStackBounds.width() - 2 * sideMargin; |
| 970 | if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) { |
| 971 | // If we are in landscape, calculate the width of the stack in portrait and ensure that |
| 972 | // we are not larger than that size |
| 973 | Rect portraitDisplayRect = new Rect(0, 0, |
| 974 | Math.min(displayRect.width(), displayRect.height()), |
| 975 | Math.max(displayRect.width(), displayRect.height())); |
| 976 | int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect, |
| 977 | mBaseSideMargin, mMinMargin, WIDTH); |
| 978 | targetStackWidth = Math.min(targetStackWidth, |
| 979 | portraitDisplayRect.width() - 2 * portraitSideMargin); |
| 980 | } |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 981 | taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0); |
| 982 | } |
| 983 | |
| 984 | /** |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 985 | * Retrieves resources that are constant regardless of the current configuration of the device. |
| 986 | */ |
| 987 | public static int getDimensionForDevice(Resources res, int phoneResId, |
| 988 | int tabletResId, int xlargeTabletResId) { |
| 989 | return getDimensionForDevice(res, phoneResId, phoneResId, tabletResId, tabletResId, |
| 990 | xlargeTabletResId, xlargeTabletResId); |
| 991 | } |
| 992 | |
| 993 | /** |
| 994 | * Retrieves resources that are constant regardless of the current configuration of the device. |
| 995 | */ |
| 996 | public static int getDimensionForDevice(Resources res, int phonePortResId, int phoneLandResId, |
| 997 | int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId, |
| 998 | int xlargeTabletLandResId) { |
| 999 | RecentsConfiguration config = Recents.getConfiguration(); |
| 1000 | boolean isLandscape = Recents.getSystemServices().getDisplayOrientation() == |
| 1001 | Configuration.ORIENTATION_LANDSCAPE; |
| 1002 | if (config.isXLargeScreen) { |
| 1003 | return res.getDimensionPixelSize(isLandscape |
| 1004 | ? xlargeTabletLandResId |
| 1005 | : xlargeTabletPortResId); |
| 1006 | } else if (config.isLargeScreen) { |
| 1007 | return res.getDimensionPixelSize(isLandscape |
| 1008 | ? tabletLandResId |
| 1009 | : tabletPortResId); |
| 1010 | } else { |
| 1011 | return res.getDimensionPixelSize(isLandscape |
| 1012 | ? phoneLandResId |
| 1013 | : phonePortResId); |
| 1014 | } |
| 1015 | } |
| 1016 | |
| 1017 | /** |
| 1018 | * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the |
| 1019 | * stack height). |
| 1020 | */ |
| 1021 | private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) { |
| 1022 | float offset = (fromSide == FROM_TOP) |
| 1023 | ? mStackRect.height() - y |
| 1024 | : y; |
| 1025 | float offsetPct = offset / mStackRect.height(); |
| 1026 | return mUnfocusedCurveInterpolator.getX(offsetPct); |
| 1027 | } |
| 1028 | |
| 1029 | /** |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1030 | * Creates a new path for the focused curve. |
| 1031 | */ |
| 1032 | private Path constructFocusedCurve() { |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1033 | // Initialize the focused curve. This curve is a piecewise curve composed of several |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 1034 | // linear pieces that goes from (0,1) through (0.5, peek height offset), |
| 1035 | // (0.5, bottom task offsets), and (1,0). |
| 1036 | float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height(); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1037 | float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) / |
| 1038 | mStackRect.height(); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1039 | Path p = new Path(); |
| 1040 | p.moveTo(0f, 1f); |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 1041 | p.lineTo(0.5f, 1f - topPeekHeightPct); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1042 | p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1043 | p.lineTo(1f, 0f); |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 1044 | return p; |
| 1045 | } |
| 1046 | |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1047 | /** |
| 1048 | * Creates a new path for the unfocused curve. |
| 1049 | */ |
| 1050 | private Path constructUnfocusedCurve() { |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1051 | // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic |
| 1052 | // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0). This |
| 1053 | // ensures that we match the range, at which 0.5 represents the stack scroll at the current |
| 1054 | // task progress. Because the height offset can change depending on a resource, we compute |
| 1055 | // the control point of the second bezier such that between it and a first known point, |
| 1056 | // there is a tangent at (0.5, peek height offset). |
| 1057 | float cpoint1X = 0.4f; |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1058 | float cpoint1Y = 0.975f; |
| 1059 | float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height(); |
| 1060 | float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1061 | float b = 1f - slope * cpoint1X; |
Winson | 12cb920 | 2016-03-04 16:55:16 -0800 | [diff] [blame] | 1062 | float cpoint2X = 0.65f; |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1063 | float cpoint2Y = slope * cpoint2X + b; |
| 1064 | Path p = new Path(); |
| 1065 | p.moveTo(0f, 1f); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1066 | p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct); |
| 1067 | p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f); |
Winson | 4993c2f | 2015-11-19 10:06:06 -0800 | [diff] [blame] | 1068 | return p; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 1069 | } |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 1070 | |
| 1071 | /** |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 1072 | * Creates a new path for the focused dim curve. |
| 1073 | */ |
| 1074 | private Path constructFocusedDimCurve() { |
| 1075 | Path p = new Path(); |
| 1076 | // The focused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused |
| 1077 | // task), then goes back to max dim at the next task |
| 1078 | p.moveTo(0f, MAX_DIM); |
| 1079 | p.lineTo(0.5f, 0f); |
| 1080 | p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), MAX_DIM); |
| 1081 | p.lineTo(1f, MAX_DIM); |
| 1082 | return p; |
| 1083 | } |
| 1084 | |
| 1085 | /** |
| 1086 | * Creates a new path for the unfocused dim curve. |
| 1087 | */ |
| 1088 | private Path constructUnfocusedDimCurve() { |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1089 | float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP); |
| 1090 | float cpoint2X = focusX + (1f - focusX) / 2; |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 1091 | Path p = new Path(); |
| 1092 | // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused |
| 1093 | // task), then goes back to max dim towards the front of the stack |
| 1094 | p.moveTo(0f, MAX_DIM); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1095 | p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f); |
| 1096 | p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM); |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 1097 | return p; |
| 1098 | } |
| 1099 | |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1100 | /** |
| 1101 | * Scales the given {@param value} to the scale of the {@param instance} rect relative to the |
| 1102 | * {@param other} rect in the {@param extent} side. |
| 1103 | */ |
| 1104 | private int getScaleForExtent(Rect instance, Rect other, int value, int minValue, |
| 1105 | @Extent int extent) { |
| 1106 | if (extent == WIDTH) { |
Winson | a1ededd | 2016-03-25 12:23:12 -0700 | [diff] [blame] | 1107 | float scale = Utilities.clamp01((float) instance.width() / other.width()); |
| 1108 | return Math.max(minValue, (int) (scale * value)); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1109 | } else if (extent == HEIGHT) { |
Winson | a1ededd | 2016-03-25 12:23:12 -0700 | [diff] [blame] | 1110 | float scale = Utilities.clamp01((float) instance.height() / other.height()); |
| 1111 | return Math.max(minValue, (int) (scale * value)); |
Winson | 59924fe | 2016-03-17 14:13:18 -0700 | [diff] [blame] | 1112 | } |
| 1113 | return value; |
| 1114 | } |
Winson | 12858a6 | 2016-02-24 11:06:01 -0800 | [diff] [blame] | 1115 | |
| 1116 | /** |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 1117 | * Updates the current transforms that would put a TaskView at the front and back of the stack. |
| 1118 | */ |
| 1119 | private void updateFrontBackTransforms() { |
Winson | 9317f86 | 2016-01-07 16:59:53 -0800 | [diff] [blame] | 1120 | // Return early if we have not yet initialized |
| 1121 | if (mStackRect.isEmpty()) { |
| 1122 | return; |
| 1123 | } |
| 1124 | |
Winson | 6808881 | 2016-02-12 16:06:04 -0800 | [diff] [blame] | 1125 | float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin, |
| 1126 | mFocusedRange.relativeMin); |
| 1127 | float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax, |
| 1128 | mFocusedRange.relativeMax); |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 1129 | getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null, |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 1130 | true /* ignoreSingleTaskCase */, true /* forceUpdate */); |
Winson | 8f97c83 | 2016-03-04 11:32:03 -0800 | [diff] [blame] | 1131 | getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null, |
Winson | 1499150 | 2016-02-15 15:40:08 -0800 | [diff] [blame] | 1132 | true /* ignoreSingleTaskCase */, true /* forceUpdate */); |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 1133 | mBackOfStackTransform.visible = true; |
| 1134 | mFrontOfStackTransform.visible = true; |
| 1135 | } |
Winson Chung | abedcab | 2014-08-15 12:27:37 -0700 | [diff] [blame] | 1136 | } |