blob: 17e6b9e3c195ca7a080303a869449540c2fa1d61 [file] [log] [blame]
Matthew Ng43db6d22017-06-27 15:29:39 -07001/*
2 * Copyright (C) 2017 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.lowram;
18
19import android.content.Context;
20import android.graphics.Rect;
21import android.view.ViewConfiguration;
22
23import com.android.systemui.R;
24import com.android.systemui.recents.Recents;
25import com.android.systemui.recents.RecentsActivityLaunchState;
26import com.android.systemui.recents.misc.Utilities;
27import com.android.systemui.recents.model.Task;
28import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
29import com.android.systemui.recents.views.TaskViewTransform;
30
31import java.util.ArrayList;
32
33import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
34
35public class TaskStackLowRamLayoutAlgorithm {
36
37 private static final String TAG = "TaskStackLowRamLayoutAlgorithm";
38 private static final float MAX_OVERSCROLL = 0.2f / 0.3f;
39
40 public static final int MAX_LAYOUT_TASK_COUNT = 9;
41 public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME = 2;
42 public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_APP =
43 NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME + 1;
44 private Rect mWindowRect;
45
46 private int mFlingThreshold;
47 private int mPadding;
48 private int mPaddingLeftRight;
49 private int mTopOffset;
50 private int mPaddingEndTopBottom;
51 private Rect mTaskRect = new Rect();
52 private Rect mSystemInsets = new Rect();
53
54 public TaskStackLowRamLayoutAlgorithm(Context context) {
55 reloadOnConfigurationChange(context);
56 }
57
58 public void reloadOnConfigurationChange(Context context) {
59 mPadding = context.getResources()
60 .getDimensionPixelSize(R.dimen.recents_layout_side_margin_phone);
61 mFlingThreshold = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
62 }
63
64 public void initialize(Rect windowRect) {
65 mWindowRect = windowRect;
66 if (mWindowRect.height() > 0) {
67 int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
68 int windowWidth = mWindowRect.width() - mSystemInsets.right - mSystemInsets.left;
69 int width = Math.min(windowWidth, windowHeight) - mPadding * 2;
70 boolean isLandscape = windowWidth > windowHeight;
71 mTaskRect.set(0, 0, width, isLandscape ? width * 2 / 3 : width);
72 mPaddingLeftRight = (windowWidth - mTaskRect.width()) / 2;
73 mPaddingEndTopBottom = (windowHeight - mTaskRect.height()) / 2;
74
75 // Compute the top offset to center tasks in the middle of the screen
76 mTopOffset = (getTotalHeightOfTasks(MAX_LAYOUT_TASK_COUNT) - windowHeight) / 2;
77 }
78 }
79
80 public void setSystemInsets(Rect systemInsets) {
81 mSystemInsets = systemInsets;
82 }
83
84 public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
85 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
86 int maxVisible = launchState.launchedFromHome || launchState.launchedFromPipApp
87 || launchState.launchedWithNextPipApp
88 ? NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME
89 : NUM_TASK_VISIBLE_LAUNCHED_FROM_APP;
90 int visibleCount = Math.min(maxVisible, tasks.size());
91 return new VisibilityReport(visibleCount, visibleCount);
92 }
93
94 public void getFrontOfStackTransform(TaskViewTransform transformOut,
95 TaskStackLayoutAlgorithm stackLayout) {
96 if (mWindowRect == null) {
97 transformOut.reset();
98 return;
99 }
100
101 // Calculate the static task y position 2 tasks after/below the middle/current task
102 int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
103 int bottomOfCurrentTask = (windowHeight + mTaskRect.height()) / 2;
104 int y = bottomOfCurrentTask + mTaskRect.height() + mPadding * 2;
105 fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
106 }
107
108 public void getBackOfStackTransform(TaskViewTransform transformOut,
109 TaskStackLayoutAlgorithm stackLayout) {
110 if (mWindowRect == null) {
111 transformOut.reset();
112 return;
113 }
114
115 // Calculate the static task y position 2 tasks before/above the middle/current task
116 int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
117 int topOfCurrentTask = (windowHeight - mTaskRect.height()) / 2;
118 int y = topOfCurrentTask - (mTaskRect.height() + mPadding) * 2;
119 fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
120 }
121
122 public TaskViewTransform getTransform(int taskIndex, float stackScroll,
123 TaskViewTransform transformOut, int taskCount, TaskStackLayoutAlgorithm stackLayout) {
124 if (taskCount == 0) {
125 transformOut.reset();
126 return transformOut;
127 }
128 boolean visible = true;
Matthew Ng43db6d22017-06-27 15:29:39 -0700129 int y;
130 if (taskCount > 1) {
131 y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll);
132
133 // Check visibility from the bottom of the task
134 visible = y + mPadding + getTaskRect().height() > 0;
135 } else {
136 int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
137 y = (windowHeight - mTaskRect.height()) / 2 - percentageToScroll(stackScroll);
138 }
139 fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, visible);
140 return transformOut;
141 }
142
143 /**
144 * Finds the closest task to the scroll percentage in the y axis and returns the percentage of
145 * the task to scroll to.
146 * @param scrollP percentage to find nearest to
147 * @param numTasks number of tasks in recents stack
148 * @param velocity speed of fling
149 */
150 public float getClosestTaskP(float scrollP, int numTasks, int velocity) {
151 int y = percentageToScroll(scrollP);
152
153 int lastY = getTaskTopFromIndex(0) - mPaddingEndTopBottom;
154 for (int i = 1; i < numTasks; i++) {
155 int taskY = getTaskTopFromIndex(i) - mPaddingEndTopBottom;
156 int diff = taskY - y;
157 if (diff > 0) {
158 int diffPrev = Math.abs(y - lastY);
159 boolean useNext = diff > diffPrev;
160 if (Math.abs(velocity) > mFlingThreshold) {
161 useNext = velocity > 0;
162 }
163 return useNext
164 ? scrollToPercentage(lastY) : scrollToPercentage(taskY);
165 }
166 lastY = taskY;
167 }
168 return scrollToPercentage(lastY);
169 }
170
171 /**
172 * Convert a scroll value to a percentage
173 * @param scroll a scroll value
174 * @return a percentage that represents the scroll from the total height of tasks
175 */
176 public float scrollToPercentage(int scroll) {
177 return (float) scroll / (mTaskRect.height() + mPadding);
178 }
179
180 /**
181 * Converts a percentage to the scroll value from the total height of tasks
182 * @param p a percentage that represents the scroll value
183 * @return a scroll value in pixels
184 */
185 public int percentageToScroll(float p) {
186 return (int) (p * (mTaskRect.height() + mPadding));
187 }
188
189 /**
190 * Get the min scroll progress for low ram layout. This computes the top position of the
191 * first task and reduce by the end padding to center the first task
192 * @return position of max scroll
193 */
194 public float getMinScrollP() {
195 return getScrollPForTask(0);
196 }
197
198 /**
199 * Get the max scroll progress for low ram layout. This computes the top position of the last
200 * task and reduce by the end padding to center the last task
201 * @param taskCount the amount of tasks in the recents stack
202 * @return position of max scroll
203 */
204 public float getMaxScrollP(int taskCount) {
205 return getScrollPForTask(taskCount - 1);
206 }
207
208 /**
209 * Get the initial scroll value whether launched from home or from an app.
210 * @param taskCount the amount of tasks currently in recents
211 * @param fromHome if launching recents from home or not
212 * @return from home it will return max value and from app it will return 2nd last task
213 */
214 public float getInitialScrollP(int taskCount, boolean fromHome) {
215 if (fromHome) {
216 return getMaxScrollP(taskCount);
217 }
218 if (taskCount < 2) {
219 return 0;
220 }
221 return getScrollPForTask(taskCount - 2);
222 }
223
224 /**
225 * Get the scroll progress for any task
226 * @param taskIndex task index to get the scroll progress of
227 * @return scroll progress of task
228 */
229 public float getScrollPForTask(int taskIndex) {
230 return scrollToPercentage(getTaskTopFromIndex(taskIndex) - mPaddingEndTopBottom);
231 }
232
233 public Rect getTaskRect() {
234 return mTaskRect;
235 }
236
237 public float getMaxOverscroll() {
238 return MAX_OVERSCROLL;
239 }
240
241 private int getTaskTopFromIndex(int index) {
242 return getTotalHeightOfTasks(index) - mTopOffset;
243 }
244
245 private int getTotalHeightOfTasks(int taskCount) {
246 return taskCount * mTaskRect.height() + (taskCount + 1) * mPadding;
247 }
248
249 private void fillStackTransform(TaskViewTransform transformOut, int y, int translationZ,
250 boolean visible) {
251 transformOut.scale = 1f;
252 transformOut.alpha = 1f;
253 transformOut.translationZ = translationZ;
254 transformOut.dimAlpha = 0f;
255 transformOut.viewOutlineAlpha = 1f;
256 transformOut.rect.set(getTaskRect());
Matthew Ng3a5ec522017-09-06 17:10:06 -0700257 transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y);
Matthew Ng43db6d22017-06-27 15:29:39 -0700258 Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
259 transformOut.visible = visible;
260 }
261}