blob: 2cd0c19a4352c26cc678515617402423ed73cf6b [file] [log] [blame]
Winsonf24f2162016-01-05 12:11:55 -08001/*
2 * Copyright (C) 2015 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
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.content.Context;
22import android.content.res.Resources;
Winson50448632016-02-01 18:04:59 -080023import android.graphics.Path;
Winsonf24f2162016-01-05 12:11:55 -080024import android.graphics.RectF;
25import android.view.View;
Winson05e46ca2016-02-05 15:40:29 -080026import android.view.animation.Interpolator;
Winson50448632016-02-01 18:04:59 -080027import android.view.animation.PathInterpolator;
Winsonc0d70582016-01-29 10:24:39 -080028
29import com.android.systemui.Interpolators;
Winsonf24f2162016-01-05 12:11:55 -080030import com.android.systemui.R;
31import com.android.systemui.recents.Recents;
32import com.android.systemui.recents.RecentsActivityLaunchState;
33import com.android.systemui.recents.RecentsConfiguration;
34import com.android.systemui.recents.misc.ReferenceCountedTrigger;
35import com.android.systemui.recents.model.Task;
36import com.android.systemui.recents.model.TaskStack;
Winsonf24f2162016-01-05 12:11:55 -080037
Winson05e46ca2016-02-05 15:40:29 -080038import java.util.ArrayList;
Winsonf24f2162016-01-05 12:11:55 -080039import java.util.List;
40
41/**
42 * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
43 * but not the contents of the {@link TaskView}s.
44 */
45public class TaskStackAnimationHelper {
46
47 /**
48 * Callbacks from the helper to coordinate view-content animations with view animations.
49 */
50 public interface Callbacks {
51 /**
52 * Callback to prepare for the start animation for the launch target {@link TaskView}.
53 */
54 void onPrepareLaunchTargetForEnterAnimation();
55
56 /**
57 * Callback to start the animation for the launch target {@link TaskView}.
58 */
59 void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
60 ReferenceCountedTrigger postAnimationTrigger);
61
62 /**
63 * Callback to start the animation for the launch target {@link TaskView} when it is
64 * launched from Recents.
65 */
66 void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
67 ReferenceCountedTrigger postAnimationTrigger);
68 }
69
Winson50448632016-02-01 18:04:59 -080070 private static final int FRAME_OFFSET_MS = 16;
71
72 public static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
73 public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 333;
74 private static final PathInterpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR =
75 new PathInterpolator(0, 0, 0, 1f);
76 private static final PathInterpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR =
77 new PathInterpolator(0, 0, 0.2f, 1f);
78
79 public static final int EXIT_TO_HOME_ALPHA_DURATION = 100;
80 public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 150;
81 private static final PathInterpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
82 new PathInterpolator(0.8f, 0, 0.6f, 1f);
83 private static final PathInterpolator EXIT_TO_HOME_ALPHA_INTERPOLATOR =
84 new PathInterpolator(0.4f, 0, 1f, 1f);
85
Winson05e46ca2016-02-05 15:40:29 -080086 private static final PathInterpolator FOCUS_NEXT_TASK_INTERPOLATOR =
87 new PathInterpolator(0.4f, 0, 0, 1f);
88 private static final PathInterpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
89 new PathInterpolator(0, 0, 0, 1f);
90 private static final PathInterpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
91 new PathInterpolator(0.4f, 0, 0.2f, 1f);
92
Winsonf24f2162016-01-05 12:11:55 -080093 private TaskStackView mStackView;
94
Winsonf24f2162016-01-05 12:11:55 -080095 private TaskViewTransform mTmpTransform = new TaskViewTransform();
Winson05e46ca2016-02-05 15:40:29 -080096 private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
97 private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
Winsonf24f2162016-01-05 12:11:55 -080098
99 public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
100 mStackView = stackView;
Winsonf24f2162016-01-05 12:11:55 -0800101 }
102
103 /**
104 * Prepares the stack views and puts them in their initial animation state while visible, before
105 * the in-app enter animations start (after the window-transition completes).
106 */
107 public void prepareForEnterAnimation() {
108 RecentsConfiguration config = Recents.getConfiguration();
109 RecentsActivityLaunchState launchState = config.getLaunchState();
110 Resources res = mStackView.getResources();
111
112 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
113 TaskStackViewScroller stackScroller = mStackView.getScroller();
114 TaskStack stack = mStackView.getStack();
115 Task launchTargetTask = stack.getLaunchTarget();
116
117 // Break early if there are no tasks
Winson4b057c62016-01-12 15:01:52 -0800118 if (stack.getTaskCount() == 0) {
Winsonf24f2162016-01-05 12:11:55 -0800119 return;
120 }
121
Winson40a22732016-02-02 18:07:00 -0800122 int offscreenYOffset = stackLayout.mStackRect.height();
Winsonf24f2162016-01-05 12:11:55 -0800123 int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
124 R.dimen.recents_task_view_affiliate_group_enter_offset);
125
126 // Prepare each of the task views for their enter animation from front to back
127 List<TaskView> taskViews = mStackView.getTaskViews();
128 for (int i = taskViews.size() - 1; i >= 0; i--) {
129 TaskView tv = taskViews.get(i);
130 Task task = tv.getTask();
131 boolean currentTaskOccludesLaunchTarget = (launchTargetTask != null &&
132 launchTargetTask.group.isTaskAboveTask(task, launchTargetTask));
133 boolean hideTask = (launchTargetTask != null &&
134 launchTargetTask.isFreeformTask() && task.isFreeformTask());
135
136 // Get the current transform for the task, which will be used to position it offscreen
137 stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
138 null);
139
140 if (hideTask) {
141 tv.setVisibility(View.INVISIBLE);
142 } else if (launchState.launchedHasConfigurationChanged) {
143 // Just load the views as-is
144 } else if (launchState.launchedFromAppWithThumbnail) {
145 if (task.isLaunchTarget) {
146 tv.onPrepareLaunchTargetForEnterAnimation();
147 } else if (currentTaskOccludesLaunchTarget) {
148 // Move the task view slightly lower so we can animate it in
149 RectF bounds = new RectF(mTmpTransform.rect);
150 bounds.offset(0, taskViewAffiliateGroupEnterOffset);
Winson65c851e2016-01-20 12:43:35 -0800151 tv.setClipViewInStack(false);
Winsonf24f2162016-01-05 12:11:55 -0800152 tv.setAlpha(0f);
153 tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top,
154 (int) bounds.right, (int) bounds.bottom);
155 }
156 } else if (launchState.launchedFromHome) {
157 // Move the task view off screen (below) so we can animate it in
158 RectF bounds = new RectF(mTmpTransform.rect);
Winson40a22732016-02-02 18:07:00 -0800159 bounds.offset(0, offscreenYOffset);
Winsonf24f2162016-01-05 12:11:55 -0800160 tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
161 (int) bounds.bottom);
162 }
163 }
164 }
165
166 /**
167 * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
168 * depending on how Recents was triggered.
169 */
170 public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
171 RecentsConfiguration config = Recents.getConfiguration();
172 RecentsActivityLaunchState launchState = config.getLaunchState();
173 Resources res = mStackView.getResources();
174
175 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
176 TaskStackViewScroller stackScroller = mStackView.getScroller();
177 TaskStack stack = mStackView.getStack();
178 Task launchTargetTask = stack.getLaunchTarget();
179
180 // Break early if there are no tasks
Winson4b057c62016-01-12 15:01:52 -0800181 if (stack.getTaskCount() == 0) {
Winsonf24f2162016-01-05 12:11:55 -0800182 return;
183 }
184
185 int taskViewEnterFromAppDuration = res.getInteger(
186 R.integer.recents_task_enter_from_app_duration);
Winson65c851e2016-01-20 12:43:35 -0800187 int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
188 R.integer.recents_task_enter_from_affiliated_app_duration);
Winsonf24f2162016-01-05 12:11:55 -0800189
190 // Create enter animations for each of the views from front to back
191 List<TaskView> taskViews = mStackView.getTaskViews();
192 int taskViewCount = taskViews.size();
193 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson50448632016-02-01 18:04:59 -0800194 int taskIndexFromFront = taskViewCount - i - 1;
Winson65c851e2016-01-20 12:43:35 -0800195 final TaskView tv = taskViews.get(i);
Winsonf24f2162016-01-05 12:11:55 -0800196 Task task = tv.getTask();
197 boolean currentTaskOccludesLaunchTarget = false;
198 if (launchTargetTask != null) {
199 currentTaskOccludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
200 launchTargetTask);
201 }
202
203 // Get the current transform for the task, which will be updated to the final transform
204 // to animate to depending on how recents was invoked
205 stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
206 null);
207
208 if (launchState.launchedFromAppWithThumbnail) {
209 if (task.isLaunchTarget) {
210 tv.onStartLaunchTargetEnterAnimation(taskViewEnterFromAppDuration,
211 mStackView.mScreenPinningEnabled, postAnimationTrigger);
212 } else {
213 // Animate the task up if it was occluding the launch target
214 if (currentTaskOccludesLaunchTarget) {
Winsonbe8e6962016-02-01 14:27:52 -0800215 AnimationProps taskAnimation = new AnimationProps(
Winsonc0d70582016-01-29 10:24:39 -0800216 taskViewEnterFromAffiliatedAppDuration, Interpolators.ALPHA_IN,
Winson65c851e2016-01-20 12:43:35 -0800217 new AnimatorListenerAdapter() {
218 @Override
219 public void onAnimationEnd(Animator animation) {
220 postAnimationTrigger.decrement();
221 tv.setClipViewInStack(false);
222 }
223 });
Winsonf24f2162016-01-05 12:11:55 -0800224 postAnimationTrigger.increment();
225 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
226 }
227 }
228
229 } else if (launchState.launchedFromHome) {
230 // Animate the tasks up
Winson50448632016-02-01 18:04:59 -0800231 AnimationProps taskAnimation = new AnimationProps()
232 .setStartDelay(AnimationProps.ALPHA, taskIndexFromFront * FRAME_OFFSET_MS)
233 .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION)
234 .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION -
235 (taskIndexFromFront * FRAME_OFFSET_MS))
236 .setInterpolator(AnimationProps.BOUNDS,
237 ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR)
238 .setInterpolator(AnimationProps.ALPHA,
239 ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
240 .setListener(postAnimationTrigger.decrementOnAnimationEnd());
Winsonf24f2162016-01-05 12:11:55 -0800241 postAnimationTrigger.increment();
242 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
243 }
244 }
245 }
246
247 /**
248 * Starts an in-app animation to hide all the task views so that we can transition back home.
249 */
250 public void startExitToHomeAnimation(boolean animated,
251 ReferenceCountedTrigger postAnimationTrigger) {
Winsonf24f2162016-01-05 12:11:55 -0800252 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
253 TaskStackViewScroller stackScroller = mStackView.getScroller();
254 TaskStack stack = mStackView.getStack();
255
256 // Break early if there are no tasks
Winson4b057c62016-01-12 15:01:52 -0800257 if (stack.getTaskCount() == 0) {
Winsonf24f2162016-01-05 12:11:55 -0800258 return;
259 }
260
Winson40a22732016-02-02 18:07:00 -0800261 int offscreenYOffset = stackLayout.mStackRect.height();
Winsonf24f2162016-01-05 12:11:55 -0800262
263 // Create the animations for each of the tasks
264 List<TaskView> taskViews = mStackView.getTaskViews();
265 int taskViewCount = taskViews.size();
266 for (int i = 0; i < taskViewCount; i++) {
Winson50448632016-02-01 18:04:59 -0800267 int taskIndexFromFront = taskViewCount - i - 1;
Winsonf24f2162016-01-05 12:11:55 -0800268 TaskView tv = taskViews.get(i);
269 Task task = tv.getTask();
Winson50448632016-02-01 18:04:59 -0800270
271 // Animate the tasks down
272 AnimationProps taskAnimation;
273 if (animated) {
274 taskAnimation = new AnimationProps()
275 .setStartDelay(AnimationProps.ALPHA, i * FRAME_OFFSET_MS)
276 .setDuration(AnimationProps.ALPHA, EXIT_TO_HOME_ALPHA_DURATION)
277 .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION +
278 (taskIndexFromFront * FRAME_OFFSET_MS))
279 .setInterpolator(AnimationProps.BOUNDS,
280 EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
281 .setInterpolator(AnimationProps.ALPHA,
282 EXIT_TO_HOME_ALPHA_INTERPOLATOR)
283 .setListener(postAnimationTrigger.decrementOnAnimationEnd());
284 postAnimationTrigger.increment();
285 } else {
286 taskAnimation = AnimationProps.IMMEDIATE;
287 }
Winsonf24f2162016-01-05 12:11:55 -0800288
289 stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
290 null);
Winson40a22732016-02-02 18:07:00 -0800291 mTmpTransform.rect.offset(0, offscreenYOffset);
Winsonf24f2162016-01-05 12:11:55 -0800292 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
293 }
294 }
295
296 /**
297 * Starts the animation for the launching task view, hiding any tasks that might occlude the
298 * window transition for the launching task.
299 */
300 public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
301 final ReferenceCountedTrigger postAnimationTrigger) {
302 Resources res = mStackView.getResources();
303 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
304 TaskStackViewScroller stackScroller = mStackView.getScroller();
305
306 int taskViewExitToAppDuration = res.getInteger(
307 R.integer.recents_task_exit_to_app_duration);
308 int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
309 R.dimen.recents_task_view_affiliate_group_enter_offset);
310
311 Task launchingTask = launchingTaskView.getTask();
312 List<TaskView> taskViews = mStackView.getTaskViews();
313 int taskViewCount = taskViews.size();
314 for (int i = 0; i < taskViewCount; i++) {
315 TaskView tv = taskViews.get(i);
316 Task task = tv.getTask();
317 boolean currentTaskOccludesLaunchTarget = (launchingTask != null &&
318 launchingTask.group.isTaskAboveTask(task, launchingTask));
319
320 if (tv == launchingTaskView) {
321 tv.setClipViewInStack(false);
322 tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
323 screenPinningRequested, postAnimationTrigger);
324 } else if (currentTaskOccludesLaunchTarget) {
325 // Animate this task out of view
Winsonbe8e6962016-02-01 14:27:52 -0800326 AnimationProps taskAnimation = new AnimationProps(
Winsonc0d70582016-01-29 10:24:39 -0800327 taskViewExitToAppDuration, Interpolators.ALPHA_OUT,
Winsonf24f2162016-01-05 12:11:55 -0800328 postAnimationTrigger.decrementOnAnimationEnd());
329 postAnimationTrigger.increment();
330
331 stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
332 null);
333 mTmpTransform.alpha = 0f;
334 mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
335 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
336 }
337 }
338 }
339
340 /**
341 * Starts the delete animation for the specified {@link TaskView}.
342 */
343 public void startDeleteTaskAnimation(Task deleteTask, final TaskView deleteTaskView,
344 final ReferenceCountedTrigger postAnimationTrigger) {
345 Resources res = mStackView.getResources();
346 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
347 TaskStackViewScroller stackScroller = mStackView.getScroller();
348
349 int taskViewRemoveAnimDuration = res.getInteger(
350 R.integer.recents_animate_task_view_remove_duration);
351 int taskViewRemoveAnimTranslationXPx = res.getDimensionPixelSize(
352 R.dimen.recents_task_view_remove_anim_translation_x);
353
354 // Disabling clipping with the stack while the view is animating away
355 deleteTaskView.setClipViewInStack(false);
356
357 // Compose the new animation and transform and star the animation
Winsonbe8e6962016-02-01 14:27:52 -0800358 AnimationProps taskAnimation = new AnimationProps(taskViewRemoveAnimDuration,
Winsonc0d70582016-01-29 10:24:39 -0800359 Interpolators.ALPHA_OUT, new AnimatorListenerAdapter() {
Winsonf24f2162016-01-05 12:11:55 -0800360 @Override
361 public void onAnimationEnd(Animator animation) {
362 postAnimationTrigger.decrement();
363
364 // Re-enable clipping with the stack (we will reuse this view)
365 deleteTaskView.setClipViewInStack(true);
366 }
367 });
368 postAnimationTrigger.increment();
369
370 stackLayout.getStackTransform(deleteTask, stackScroller.getStackScroll(), mTmpTransform,
371 null);
372 mTmpTransform.alpha = 0f;
373 mTmpTransform.rect.offset(taskViewRemoveAnimTranslationXPx, 0);
374 mStackView.updateTaskViewToTransform(deleteTaskView, mTmpTransform, taskAnimation);
375 }
376
377 /**
Winson49df4202016-01-25 17:33:34 -0800378 * Starts the animation to hide the {@link TaskView}s when the history is shown.
Winsonf24f2162016-01-05 12:11:55 -0800379 */
380 public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
381 Resources res = mStackView.getResources();
382 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
383 TaskStackViewScroller stackScroller = mStackView.getScroller();
384
Winson49df4202016-01-25 17:33:34 -0800385 int offscreenY = stackLayout.mStackRect.bottom;
Winsonf24f2162016-01-05 12:11:55 -0800386 int historyTransitionDuration = res.getInteger(
387 R.integer.recents_history_transition_duration);
Winson49df4202016-01-25 17:33:34 -0800388 int startDelayIncr = 16;
Winsonf24f2162016-01-05 12:11:55 -0800389
390 List<TaskView> taskViews = mStackView.getTaskViews();
391 int taskViewCount = taskViews.size();
392 for (int i = taskViewCount - 1; i >= 0; i--) {
393 TaskView tv = taskViews.get(i);
394 Task task = tv.getTask();
Winsonbe8e6962016-02-01 14:27:52 -0800395 AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
Winsonc0d70582016-01-29 10:24:39 -0800396 historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN,
Winsonf24f2162016-01-05 12:11:55 -0800397 postAnimationTrigger.decrementOnAnimationEnd());
398 postAnimationTrigger.increment();
399
400 stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
401 null);
402 mTmpTransform.alpha = 0f;
Winson49df4202016-01-25 17:33:34 -0800403 mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
Winsonf24f2162016-01-05 12:11:55 -0800404 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
405 }
406 }
407
408 /**
Winson49df4202016-01-25 17:33:34 -0800409 * Starts the animation to show the {@link TaskView}s when the history is hidden.
Winsonf24f2162016-01-05 12:11:55 -0800410 */
Winson49df4202016-01-25 17:33:34 -0800411 public void startHideHistoryAnimation() {
412 Resources res = mStackView.getResources();
413 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
414 TaskStackViewScroller stackScroller = mStackView.getScroller();
Winsonf24f2162016-01-05 12:11:55 -0800415
Winson49df4202016-01-25 17:33:34 -0800416 int historyTransitionDuration = res.getInteger(
Winsonf24f2162016-01-05 12:11:55 -0800417 R.integer.recents_history_transition_duration);
Winson49df4202016-01-25 17:33:34 -0800418 int startDelayIncr = 16;
Winsonf24f2162016-01-05 12:11:55 -0800419
420 List<TaskView> taskViews = mStackView.getTaskViews();
421 int taskViewCount = taskViews.size();
422 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson49df4202016-01-25 17:33:34 -0800423 TaskView tv = taskViews.get(i);
Winsonbe8e6962016-02-01 14:27:52 -0800424 AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
Winsonc0d70582016-01-29 10:24:39 -0800425 historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN);
Winson49df4202016-01-25 17:33:34 -0800426 stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
427 mTmpTransform, null);
428 mTmpTransform.alpha = 1f;
429 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
Winsonf24f2162016-01-05 12:11:55 -0800430 }
431 }
Winson05e46ca2016-02-05 15:40:29 -0800432
433 /**
434 * Starts the animation to focus the next {@link TaskView} when paging through recents.
435 *
436 * @return whether or not this will trigger a scroll in the stack
437 */
438 public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
439 boolean requestViewFocus) {
440 TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
441 TaskStackViewScroller stackScroller = mStackView.getScroller();
442 TaskStack stack = mStackView.getStack();
443
444 final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
445 boolean willScrollToFront = newScroll > stackScroller.getStackScroll();
446 boolean willScroll = Float.compare(newScroll, stackScroller.getStackScroll()) != 0;
447
448 // Get the current set of task transforms
449 ArrayList<Task> stackTasks = stack.getStackTasks();
450 mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
451
452 // Pick up the newly visible views after the scroll
453 mStackView.bindVisibleTaskViews(newScroll);
454
455 // Update the internal state
456 stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
457 stackScroller.setStackScroll(newScroll, null /* animation */);
458 mStackView.cancelDeferredTaskViewLayoutAnimation();
459
460 // Get the final set of task transforms
Winson14991502016-02-15 15:40:08 -0800461 mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
462 mTmpFinalTaskTransforms);
Winson05e46ca2016-02-05 15:40:29 -0800463
464 // Focus the task view
465 TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
466 newFocusedTaskView.setFocusedState(true, requestViewFocus);
467
468 // Setup the end listener to return all the hidden views to the view pool after the
469 // focus animation
Winson91b225d2016-02-16 15:18:07 -0800470 ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
471 postAnimTrigger.addLastDecrementRunnable(new Runnable() {
Winson05e46ca2016-02-05 15:40:29 -0800472 @Override
Winson91b225d2016-02-16 15:18:07 -0800473 public void run() {
Winson05e46ca2016-02-05 15:40:29 -0800474 mStackView.bindVisibleTaskViews(newScroll);
475 }
Winson91b225d2016-02-16 15:18:07 -0800476 });
Winson05e46ca2016-02-05 15:40:29 -0800477
478 List<TaskView> taskViews = mStackView.getTaskViews();
479 int taskViewCount = taskViews.size();
480 int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
481 for (int i = 0; i < taskViewCount; i++) {
482 TaskView tv = taskViews.get(i);
483 Task task = tv.getTask();
484
485 if (mStackView.isIgnoredTask(task)) {
486 continue;
487 }
488
489 int taskIndex = stackTasks.indexOf(task);
490 TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
491 TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
492
493 // Update the task to the initial state (for the newly picked up tasks)
494 mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
495
496 int duration;
497 Interpolator interpolator;
498 if (willScrollToFront) {
499 duration = Math.max(100, 100 + ((i - 1) * 50));
500 interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
501 } else {
502 if (i < newFocusTaskViewIndex) {
503 duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
504 interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
505 } else if (i > newFocusTaskViewIndex) {
506 duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
507 interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
508 } else {
509 duration = 200;
510 interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
511 }
512 }
513
514 AnimationProps anim = new AnimationProps()
515 .setDuration(AnimationProps.BOUNDS, duration)
516 .setInterpolator(AnimationProps.BOUNDS, interpolator)
Winson91b225d2016-02-16 15:18:07 -0800517 .setListener(postAnimTrigger.decrementOnAnimationEnd());
518 postAnimTrigger.increment();
Winson05e46ca2016-02-05 15:40:29 -0800519 mStackView.updateTaskViewToTransform(tv, toTransform, anim);
520 }
521 return willScroll;
522 }
Winsonf24f2162016-01-05 12:11:55 -0800523}