blob: 5b9dfabe838aca6fbf473ce9b3fd0487dc6e5258 [file] [log] [blame]
Winson Chung303e1ff2014-03-07 15:06:19 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
Winson70f0bf72016-02-01 14:05:29 -080019import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
20import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
21import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
22
Winsona78a8f32015-12-03 10:55:01 -080023import android.animation.ObjectAnimator;
Winson Chungbf5dbf12014-09-16 00:58:25 +020024import android.animation.ValueAnimator;
Winson Chung9f49df92014-05-07 18:08:34 -070025import android.content.ComponentName;
Winson Chung303e1ff2014-03-07 15:06:19 -080026import android.content.Context;
Winsonbb410952015-12-04 14:34:11 -080027import android.content.res.Resources;
Jorim Jaggi900fb482015-06-02 15:07:33 -070028import android.graphics.Canvas;
Winson Chung303e1ff2014-03-07 15:06:19 -080029import android.graphics.Rect;
Winsona78a8f32015-12-03 10:55:01 -080030import android.graphics.drawable.Drawable;
Winsonde0591a2015-12-04 17:24:35 -080031import android.graphics.drawable.GradientDrawable;
Winson Chung83ea6f72015-06-17 13:00:23 -070032import android.os.Bundle;
Winson Chung4ab4d832015-12-11 10:25:46 -050033import android.os.Parcelable;
Winson Chung931c51f2015-12-17 17:08:55 -050034import android.provider.Settings;
Winson55003902016-01-12 12:00:37 -080035import android.util.ArrayMap;
36import android.util.ArraySet;
Winson8aa99592016-01-19 15:07:07 -080037import android.util.MutableBoolean;
Winson Chung37c8d8e2014-03-24 14:53:07 -070038import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080039import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080040import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080041import android.view.ViewDebug;
Winson70f0bf72016-02-01 14:05:29 -080042import android.view.ViewGroup;
Winson Chungee445952014-09-09 16:12:59 +020043import android.view.accessibility.AccessibilityEvent;
Winson Chung83ea6f72015-06-17 13:00:23 -070044import android.view.accessibility.AccessibilityNodeInfo;
Winson Chung303e1ff2014-03-07 15:06:19 -080045import android.widget.FrameLayout;
Winsonc0d70582016-01-29 10:24:39 -080046
Winson42329522016-02-05 10:39:46 -080047import com.android.internal.logging.MetricsLogger;
48import com.android.internal.logging.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080049import com.android.systemui.Interpolators;
Winson Chungc6a16232014-04-01 14:04:48 -070050import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070051import com.android.systemui.recents.Recents;
Winsone6c90732015-09-24 16:06:29 -070052import com.android.systemui.recents.RecentsActivity;
Winson2536c7e2015-10-01 15:49:31 -070053import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chung303e1ff2014-03-07 15:06:19 -080054import com.android.systemui.recents.RecentsConfiguration;
Winson4b9cded2016-01-26 16:26:47 -080055import com.android.systemui.recents.RecentsDebugFlags;
Winsone6c90732015-09-24 16:06:29 -070056import com.android.systemui.recents.events.EventBus;
Winsone5f1faa2015-11-20 12:26:23 -080057import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
Winsone693aaf2016-03-01 12:05:59 -080058import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
Winsonef064132016-01-05 12:11:31 -080059import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
Winson4b9cded2016-01-26 16:26:47 -080060import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
Winsone5f1faa2015-11-20 12:26:23 -080061import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
Winson8f6ee482016-03-18 17:51:48 -070062import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
Winsonbc0f8cd2016-03-15 15:44:48 -070063import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winson8b1871d2015-11-20 09:56:20 -080064import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Winsonb61e6542016-02-04 14:37:18 -080065import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Winson Chung48f2cda2015-12-11 13:20:12 -050066import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winsonef064132016-01-05 12:11:31 -080067import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
Winson88737542016-02-17 13:27:33 -080068import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
Winsone6c90732015-09-24 16:06:29 -070069import com.android.systemui.recents.events.activity.PackagesChangedEvent;
Winson8f6ee482016-03-18 17:51:48 -070070import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
Winson397ae742015-11-20 11:27:33 -080071import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
Winsonef064132016-01-05 12:11:31 -080072import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
Winson0d14d4d2015-10-26 17:05:04 -070073import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Winsonef064132016-01-05 12:11:31 -080074import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winsonb1e71d02015-11-23 12:40:23 -080075import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
Winsone7f138c2015-10-22 16:15:21 -070076import com.android.systemui.recents.events.ui.UserInteractionEvent;
Winsoneca4ab62015-11-04 10:50:28 -080077import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
78import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
79import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
80import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070081import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
82import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
83import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
Winson Chungffa2ec62014-07-03 15:54:42 -070084import com.android.systemui.recents.misc.DozeTrigger;
Winson Chungee445952014-09-09 16:12:59 +020085import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungbf5dbf12014-09-16 00:58:25 +020086import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080087import com.android.systemui.recents.model.Task;
88import com.android.systemui.recents.model.TaskStack;
Winson Chung303e1ff2014-03-07 15:06:19 -080089
90import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -080091import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -080092
Winson Chung303e1ff2014-03-07 15:06:19 -080093
94/* The visual representation of a task stack view */
Winson Chung04dfe0d2014-03-14 14:06:29 -070095public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
Winson Chung012ef362014-07-31 18:36:25 -070096 TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
Winson1c846142016-01-22 11:34:38 -080097 TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
Winsone6c90732015-09-24 16:06:29 -070098 ViewPool.ViewPoolConsumer<TaskView, Task> {
Winson Chung04dfe0d2014-03-14 14:06:29 -070099
Winson Chung4ab4d832015-12-11 10:25:46 -0500100 private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
101 private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
102 "saved_instance_state_layout_focused_state";
103 private final static String KEY_SAVED_STATE_LAYOUT_STACK_SCROLL =
104 "saved_instance_state_layout_stack_scroll";
105
Winson8f6ee482016-03-18 17:51:48 -0700106 // The thresholds at which to show/hide the stack action button.
107 private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
108 private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
Winsonc29ff002015-11-20 16:00:45 -0800109
Winson8aa99592016-01-19 15:07:07 -0800110 public static final int DEFAULT_SYNC_STACK_DURATION = 200;
Winsonf24f2162016-01-05 12:11:55 -0800111 private static final int DRAG_SCALE_DURATION = 175;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100112 static final float DRAG_SCALE_FACTOR = 1.05f;
Winson Chung06266772015-12-11 10:24:21 -0500113
Winsonf8597b22016-03-23 18:44:26 -0700114 private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
Winson96e61342016-03-15 16:47:19 -0700115 private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
116
Winson8aa99592016-01-19 15:07:07 -0800117 private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
118
Winson05e46ca2016-02-05 15:40:29 -0800119 LayoutInflater mInflater;
Winson88737542016-02-17 13:27:33 -0800120 TaskStack mStack = new TaskStack();
Winson231bc9c2016-02-09 12:31:00 -0800121 @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
Winson36a5a2c2015-10-29 18:04:39 -0700122 TaskStackLayoutAlgorithm mLayoutAlgorithm;
Winsonf9357d92016-03-25 15:14:37 -0700123 // The stable layout algorithm is only used to calculate the task rect with the stable bounds
124 TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
Winson231bc9c2016-02-09 12:31:00 -0800125 @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
Winson Chung012ef362014-07-31 18:36:25 -0700126 TaskStackViewScroller mStackScroller;
Winson231bc9c2016-02-09 12:31:00 -0800127 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
Winson Chung303e1ff2014-03-07 15:06:19 -0800128 TaskStackViewTouchHandler mTouchHandler;
Winsonf24f2162016-01-05 12:11:55 -0800129 TaskStackAnimationHelper mAnimationHelper;
Winsonde0591a2015-12-04 17:24:35 -0800130 GradientDrawable mFreeformWorkspaceBackground;
Winsona78a8f32015-12-03 10:55:01 -0800131 ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -0800132 ViewPool<TaskView, Task> mViewPool;
Winsonf24f2162016-01-05 12:11:55 -0800133
134 ArrayList<TaskView> mTaskViews = new ArrayList<>();
Winsone6c90732015-09-24 16:06:29 -0700135 ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
Winson8aa99592016-01-19 15:07:07 -0800136 ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
Winsonbe8e6962016-02-01 14:27:52 -0800137 AnimationProps mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800138
Winson231bc9c2016-02-09 12:31:00 -0800139 @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
Winson Chungd543c1b2014-06-23 15:06:45 -0700140 DozeTrigger mUIDozeTrigger;
Winson231bc9c2016-02-09 12:31:00 -0800141 @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Winsonaaf33bc2015-12-03 12:02:38 -0800142 Task mFocusedTask;
Winsonf24f2162016-01-05 12:11:55 -0800143
Winsonbb410952015-12-04 14:34:11 -0800144 int mTaskCornerRadiusPx;
Winson3e874742016-01-07 10:08:17 -0800145 private int mDividerSize;
Winson4b9cded2016-01-26 16:26:47 -0800146 private int mStartTimerIndicatorDuration;
Winsonf24f2162016-01-05 12:11:55 -0800147
Winson231bc9c2016-02-09 12:31:00 -0800148 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800149 boolean mTaskViewsClipDirty = true;
Winson231bc9c2016-02-09 12:31:00 -0800150 @ViewDebug.ExportedProperty(category="recents")
Winson Chung303e1ff2014-03-07 15:06:19 -0800151 boolean mAwaitingFirstLayout = true;
Winson231bc9c2016-02-09 12:31:00 -0800152 @ViewDebug.ExportedProperty(category="recents")
Winson70f0bf72016-02-01 14:05:29 -0800153 boolean mInMeasureLayout = false;
Winson231bc9c2016-02-09 12:31:00 -0800154 @ViewDebug.ExportedProperty(category="recents")
Winsone5f1faa2015-11-20 12:26:23 -0800155 boolean mEnterAnimationComplete = false;
Winson231bc9c2016-02-09 12:31:00 -0800156 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800157 boolean mTouchExplorationEnabled;
Winson231bc9c2016-02-09 12:31:00 -0800158 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800159 boolean mScreenPinningEnabled;
Winson3150e572015-10-23 15:07:24 -0700160
Winson3e874742016-01-07 10:08:17 -0800161 // The stable stack bounds are the full bounds that we were measured with from RecentsView
Winson231bc9c2016-02-09 12:31:00 -0800162 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800163 private Rect mStableStackBounds = new Rect();
Winson3e874742016-01-07 10:08:17 -0800164 // The current stack bounds are dynamic and may change as the user drags and drops
Winson231bc9c2016-02-09 12:31:00 -0800165 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800166 private Rect mStackBounds = new Rect();
Winson59924fe2016-03-17 14:13:18 -0700167 // The current window bounds at the point we were measured
168 @ViewDebug.ExportedProperty(category="recents")
169 private Rect mStableWindowRect = new Rect();
170 // The current window bounds are dynamic and may change as the user drags and drops
171 @ViewDebug.ExportedProperty(category="recents")
172 private Rect mWindowRect = new Rect();
Winsond9529612016-01-28 13:29:49 -0800173
Winson05e46ca2016-02-05 15:40:29 -0800174 private Rect mTmpRect = new Rect();
175 private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
176 private List<TaskView> mTmpTaskViews = new ArrayList<>();
177 private TaskViewTransform mTmpTransform = new TaskViewTransform();
Winson59924fe2016-03-17 14:13:18 -0700178 private ArrayList<TaskViewTransform> mTmpTaskTransforms = new ArrayList<>();
Winsonc4387022016-02-24 12:05:26 -0800179 private int[] mTmpIntPair = new int[2];
Winson Chung931c51f2015-12-17 17:08:55 -0500180
Winson Chungbf5dbf12014-09-16 00:58:25 +0200181 // A convenience update listener to request updating clipping of tasks
Winsoneca4ab62015-11-04 10:50:28 -0800182 private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
Winson Chungbf5dbf12014-09-16 00:58:25 +0200183 new ValueAnimator.AnimatorUpdateListener() {
Winson5b7dd532015-12-01 16:02:12 -0800184 @Override
185 public void onAnimationUpdate(ValueAnimator animation) {
Winson55003902016-01-12 12:00:37 -0800186 if (!mTaskViewsClipDirty) {
187 mTaskViewsClipDirty = true;
188 invalidate();
189 }
Winson5b7dd532015-12-01 16:02:12 -0800190 }
191 };
Winson Chungbf5dbf12014-09-16 00:58:25 +0200192
Winsoneca4ab62015-11-04 10:50:28 -0800193 // The drop targets for a task drag
194 private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
195 @Override
Winson3e874742016-01-07 10:08:17 -0800196 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
197 // This drop target has a fixed bounds and should be checked last, so just fall through
198 // if it is the current target
199 if (!isCurrentTarget) {
200 return mLayoutAlgorithm.mFreeformRect.contains(x, y);
201 }
202 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800203 }
204 };
205
206 private DropTarget mStackDropTarget = new DropTarget() {
207 @Override
Winson3e874742016-01-07 10:08:17 -0800208 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
209 // This drop target has a fixed bounds and should be checked last, so just fall through
210 // if it is the current target
211 if (!isCurrentTarget) {
212 return mLayoutAlgorithm.mStackRect.contains(x, y);
213 }
214 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800215 }
216 };
217
Winson88737542016-02-17 13:27:33 -0800218 public TaskStackView(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800219 super(context);
Winsonde0591a2015-12-04 17:24:35 -0800220 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonbb410952015-12-04 14:34:11 -0800221 Resources res = context.getResources();
222
Winson Chungb0a28ea2014-10-28 15:21:35 -0700223 // Set the stack first
Winson88737542016-02-17 13:27:33 -0800224 mStack.setCallbacks(this);
Winson35f30502015-09-28 11:24:36 -0700225 mViewPool = new ViewPool<>(context, this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700226 mInflater = LayoutInflater.from(context);
Winson1c846142016-01-22 11:34:38 -0800227 mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
Winsonf9357d92016-03-25 15:14:37 -0700228 mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
Winson1c846142016-01-22 11:34:38 -0800229 mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
Winson35f30502015-09-28 11:24:36 -0700230 mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
Winsonf24f2162016-01-05 12:11:55 -0800231 mAnimationHelper = new TaskStackAnimationHelper(context, this);
Winsonbb410952015-12-04 14:34:11 -0800232 mTaskCornerRadiusPx = res.getDimensionPixelSize(
233 R.dimen.recents_task_view_rounded_corners_radius);
Winson3e874742016-01-07 10:08:17 -0800234 mDividerSize = ssp.getDockedDividerSize(context);
Winson35f30502015-09-28 11:24:36 -0700235
236 int taskBarDismissDozeDelaySeconds = getResources().getInteger(
237 R.integer.recents_task_bar_dismiss_delay_seconds);
238 mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
Winson Chunga26fb782014-06-12 17:52:39 -0700239 @Override
240 public void run() {
241 // Show the task bar dismiss buttons
Winson Chung6ac8bd62015-01-07 16:38:35 -0800242 List<TaskView> taskViews = getTaskViews();
243 int taskViewCount = taskViews.size();
244 for (int i = 0; i < taskViewCount; i++) {
245 TaskView tv = taskViews.get(i);
Winson Chung969f5862014-06-16 17:08:24 -0700246 tv.startNoUserInteractionAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700247 }
248 }
249 });
Winson Chung83ea6f72015-06-17 13:00:23 -0700250 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Winson36a5a2c2015-10-29 18:04:39 -0700251
Winsonde0591a2015-12-04 17:24:35 -0800252 mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
253 R.drawable.recents_freeform_workspace_bg);
Winsona78a8f32015-12-03 10:55:01 -0800254 mFreeformWorkspaceBackground.setCallback(this);
Winsonde0591a2015-12-04 17:24:35 -0800255 if (ssp.hasFreeformWorkspaceSupport()) {
Winson Chungaa4f8002015-12-17 10:27:55 -0500256 mFreeformWorkspaceBackground.setColor(
257 getContext().getColor(R.color.recents_freeform_workspace_bg_color));
Winsonde0591a2015-12-04 17:24:35 -0800258 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800259 }
260
Winsona1ededd2016-03-25 12:23:12 -0700261 @Override
262 protected void onAttachedToWindow() {
263 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
264 super.onAttachedToWindow();
265 readSystemFlags();
266 }
267
268 @Override
269 protected void onDetachedFromWindow() {
270 super.onDetachedFromWindow();
271 EventBus.getDefault().unregister(this);
272 }
273
Winson88737542016-02-17 13:27:33 -0800274 /**
Winsona1ededd2016-03-25 12:23:12 -0700275 * Called from RecentsActivity when it is relaunched.
Winson88737542016-02-17 13:27:33 -0800276 */
Winsona1ededd2016-03-25 12:23:12 -0700277 void onReload(boolean isResumingFromVisible) {
Winson88737542016-02-17 13:27:33 -0800278 if (!isResumingFromVisible) {
279 // Reset the focused task
280 resetFocusedTask(getFocusedTask());
281 }
282
283 // Reset the state of each of the task views
284 List<TaskView> taskViews = new ArrayList<>();
285 taskViews.addAll(getTaskViews());
286 taskViews.addAll(mViewPool.getViews());
287 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsona1ededd2016-03-25 12:23:12 -0700288 taskViews.get(i).onReload(isResumingFromVisible);
Winson88737542016-02-17 13:27:33 -0800289 }
290
291 // Reset the stack state
292 readSystemFlags();
293 mTaskViewsClipDirty = true;
294 mEnterAnimationComplete = false;
295 mUIDozeTrigger.stopDozing();
296 if (isResumingFromVisible) {
297 // Animate in the freeform workspace
298 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
299 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
300 Interpolators.FAST_OUT_SLOW_IN));
301 } else {
302 mStackScroller.reset();
Winsonf9357d92016-03-25 15:14:37 -0700303 mStableLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800304 mLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800305 }
Winson88737542016-02-17 13:27:33 -0800306
Winsona1ededd2016-03-25 12:23:12 -0700307 // Since we always animate to the same place in (the initial state), always reset the stack
308 // to the initial state when resuming
309 mAwaitingFirstLayout = true;
310 requestLayout();
Winsone6c90732015-09-24 16:06:29 -0700311 }
312
Winson88737542016-02-17 13:27:33 -0800313 /**
314 * Sets the stack tasks of this TaskStackView from the given TaskStack.
315 */
Winsona1ededd2016-03-25 12:23:12 -0700316 public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
Winson88737542016-02-17 13:27:33 -0800317 boolean isInitialized = mLayoutAlgorithm.isInitialized();
Winsona1ededd2016-03-25 12:23:12 -0700318 // Only notify if we are already initialized, otherwise, everything will pick up all the
319 // new and old tasks when we next layout
Winson88737542016-02-17 13:27:33 -0800320 mStack.setTasks(getContext(), stack.computeAllTasksList(),
Winsona1ededd2016-03-25 12:23:12 -0700321 allowNotifyStackChanges && isInitialized);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700322 }
323
Winson Chungd16c5652015-01-26 16:11:07 -0800324 /** Returns the task stack. */
Winsone693aaf2016-03-01 12:05:59 -0800325 public TaskStack getStack() {
Winson Chungd16c5652015-01-26 16:11:07 -0800326 return mStack;
327 }
328
Winsone693aaf2016-03-01 12:05:59 -0800329 /**
330 * Updates this TaskStackView to the initial state.
331 */
332 public void updateToInitialState() {
333 mStackScroller.setStackScrollToInitialState();
334 mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
335 }
336
Winson Chung6ac8bd62015-01-07 16:38:35 -0800337 /** Updates the list of task views */
338 void updateTaskViewsList() {
339 mTaskViews.clear();
340 int childCount = getChildCount();
341 for (int i = 0; i < childCount; i++) {
342 View v = getChildAt(i);
343 if (v instanceof TaskView) {
344 mTaskViews.add((TaskView) v);
345 }
346 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800347 }
348
349 /** Gets the list of task views */
350 List<TaskView> getTaskViews() {
Winsonf24f2162016-01-05 12:11:55 -0800351 return mTaskViews;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800352 }
353
Winson1b585612015-11-06 09:16:26 -0800354 /**
355 * Returns the front most task view.
356 *
357 * @param stackTasksOnly if set, will return the front most task view in the stack (by default
358 * the front most task view will be freeform since they are placed above
359 * stack tasks)
360 */
361 private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
362 List<TaskView> taskViews = getTaskViews();
363 int taskViewCount = taskViews.size();
364 for (int i = taskViewCount - 1; i >= 0; i--) {
365 TaskView tv = taskViews.get(i);
366 Task task = tv.getTask();
367 if (stackTasksOnly && task.isFreeformTask()) {
368 continue;
369 }
370 return tv;
371 }
372 return null;
373 }
374
375 /**
376 * Finds the child view given a specific {@param task}.
377 */
378 public TaskView getChildViewForTask(Task t) {
379 List<TaskView> taskViews = getTaskViews();
380 int taskViewCount = taskViews.size();
381 for (int i = 0; i < taskViewCount; i++) {
382 TaskView tv = taskViews.get(i);
383 if (tv.getTask() == t) {
384 return tv;
385 }
386 }
387 return null;
388 }
389
Winson Chungd7b2cb12014-06-26 15:08:50 -0700390 /** Returns the stack algorithm for this task stack. */
Winson36a5a2c2015-10-29 18:04:39 -0700391 public TaskStackLayoutAlgorithm getStackAlgorithm() {
Winson Chung012ef362014-07-31 18:36:25 -0700392 return mLayoutAlgorithm;
Winson Chung303e1ff2014-03-07 15:06:19 -0800393 }
394
Winson Chungc6a16232014-04-01 14:04:48 -0700395 /**
Winson8aa99592016-01-19 15:07:07 -0800396 * Adds a task to the ignored set.
Winson Chungc6a16232014-04-01 14:04:48 -0700397 */
Winson8aa99592016-01-19 15:07:07 -0800398 void addIgnoreTask(Task task) {
399 mIgnoreTasks.add(task.key);
400 }
401
402 /**
403 * Removes a task from the ignored set.
404 */
405 void removeIgnoreTask(Task task) {
406 mIgnoreTasks.remove(task.key);
407 }
408
409 /**
410 * Returns whether the specified {@param task} is ignored.
411 */
412 boolean isIgnoredTask(Task task) {
413 return mIgnoreTasks.contains(task.key);
414 }
415
416 /**
417 * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
418 * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
419 * visible range includes all tasks at the target stack scroll. This is useful for ensure that
420 * all views necessary for a transition or animation will be visible at the start.
421 *
422 * This call ignores freeform tasks.
423 *
424 * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
425 * match the size of {@param tasks}
426 * @param tasks The set of tasks for which to generate transforms
427 * @param curStackScroll The current stack scroll
428 * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
429 * The range of the union of the visible views at the current and
430 * target stack scrolls will be returned.
431 * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
432 * Transforms will still be calculated for the ignore tasks.
Winsonc4387022016-02-24 12:05:26 -0800433 * @return the front and back most visible task indices (there may be non visible tasks in
434 * between this range)
Winson8aa99592016-01-19 15:07:07 -0800435 */
Winsonc4387022016-02-24 12:05:26 -0800436 int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
Winson8aa99592016-01-19 15:07:07 -0800437 ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800438 ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
Winson Chungc6a16232014-04-01 14:04:48 -0700439 int taskCount = tasks.size();
Winsonc4387022016-02-24 12:05:26 -0800440 int[] visibleTaskRange = mTmpIntPair;
441 visibleTaskRange[0] = -1;
442 visibleTaskRange[1] = -1;
Winson8aa99592016-01-19 15:07:07 -0800443 boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
Winson Chungc9567c02014-06-16 20:25:51 -0700444
445 // We can reuse the task transforms where possible to reduce object allocation
Winsonc5fd3502016-01-18 15:18:37 -0800446 Utilities.matchTaskListSize(tasks, taskTransforms);
Winson Chungc9567c02014-06-16 20:25:51 -0700447
448 // Update the stack transforms
Winson4993c2f2015-11-19 10:06:06 -0800449 TaskViewTransform frontTransform = null;
Winson8aa99592016-01-19 15:07:07 -0800450 TaskViewTransform frontTransformAtTarget = null;
451 TaskViewTransform transform = null;
452 TaskViewTransform transformAtTarget = null;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700453 for (int i = taskCount - 1; i >= 0; i--) {
Winsona5e6b362015-11-02 17:17:20 -0800454 Task task = tasks.get(i);
Winson8aa99592016-01-19 15:07:07 -0800455
456 // Calculate the current and (if necessary) the target transform for the task
457 transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800458 taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800459 if (useTargetStackScroll && !transform.visible) {
460 // If we have a target stack scroll and the task is not currently visible, then we
461 // just update the transform at the new scroll
462 // TODO: Optimize this
463 transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
464 targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
465 if (transformAtTarget.visible) {
466 transform.copyFrom(transformAtTarget);
467 }
Winson3e874742016-01-07 10:08:17 -0800468 }
469
Winson8aa99592016-01-19 15:07:07 -0800470 // For ignore tasks, only calculate the stack transform and skip the calculation of the
471 // visible stack indices
472 if (ignoreTasksSet.contains(task.key)) {
473 continue;
474 }
Winsonf24f2162016-01-05 12:11:55 -0800475
476 // For freeform tasks, only calculate the stack transform and skip the calculation of
477 // the visible stack indices
Winsona5e6b362015-11-02 17:17:20 -0800478 if (task.isFreeformTask()) {
479 continue;
480 }
481
Winson4993c2f2015-11-19 10:06:06 -0800482 frontTransform = transform;
Winson8aa99592016-01-19 15:07:07 -0800483 frontTransformAtTarget = transformAtTarget;
Winsonc4387022016-02-24 12:05:26 -0800484 if (transform.visible) {
485 if (visibleTaskRange[0] < 0) {
486 visibleTaskRange[0] = i;
487 }
488 visibleTaskRange[1] = i;
489 }
Winson Chungc6a16232014-04-01 14:04:48 -0700490 }
Winsonc4387022016-02-24 12:05:26 -0800491 return visibleTaskRange;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700492 }
493
Winsonf24f2162016-01-05 12:11:55 -0800494 /**
Winson8aa99592016-01-19 15:07:07 -0800495 * Binds the visible {@link TaskView}s at the given target scroll.
Winsonf24f2162016-01-05 12:11:55 -0800496 */
Winson8aa99592016-01-19 15:07:07 -0800497 void bindVisibleTaskViews(float targetStackScroll) {
Winsone693aaf2016-03-01 12:05:59 -0800498 bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
499 }
500
501 void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
502 bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800503 }
504
505 /**
506 * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
507 * current {@link TaskStack}. This call does not continue on to update their position to the
508 * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
509 * be added/removed from the view hierarchy and placed in the correct Z order and initial
510 * position (if not currently on screen).
511 *
512 * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
513 * includes those visible at the current stack scroll, and all at the
514 * target stack scroll.
515 * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
516 * {@link TaskView}s
Winsone693aaf2016-03-01 12:05:59 -0800517 * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
518 * tasks at their non-overridden task progress
Winson8aa99592016-01-19 15:07:07 -0800519 */
Winsone693aaf2016-03-01 12:05:59 -0800520 void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
521 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800522 // Get all the task transforms
Winsonc4387022016-02-24 12:05:26 -0800523 ArrayList<Task> tasks = mStack.getStackTasks();
524 int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
Winsone693aaf2016-03-01 12:05:59 -0800525 mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
526 ignoreTaskOverrides);
Winsonf24f2162016-01-05 12:11:55 -0800527
528 // Return all the invisible children to the pool
Winson55003902016-01-12 12:00:37 -0800529 mTmpTaskViewMap.clear();
Winson8aa99592016-01-19 15:07:07 -0800530 List<TaskView> taskViews = getTaskViews();
531 int lastFocusedTaskIndex = -1;
532 int taskViewCount = taskViews.size();
Winsonf24f2162016-01-05 12:11:55 -0800533 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800534 TaskView tv = taskViews.get(i);
535 Task task = tv.getTask();
Winsonf24f2162016-01-05 12:11:55 -0800536
Winson3e874742016-01-07 10:08:17 -0800537 // Skip ignored tasks
Winson8aa99592016-01-19 15:07:07 -0800538 if (ignoreTasksSet.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800539 continue;
540 }
541
Winson1d5ff7e2016-03-04 11:21:09 -0800542 // It is possible for the set of lingering TaskViews to differ from the stack if the
543 // stack was updated before the relayout. If the task view is no longer in the stack,
544 // then just return it back to the view pool.
545 int taskIndex = mStack.indexOfStackTask(task);
546 TaskViewTransform transform = null;
547 if (taskIndex != -1) {
548 transform = mCurrentTaskTransforms.get(taskIndex);
549 }
550
551 if (task.isFreeformTask() || (transform != null && transform.visible)) {
Winson55003902016-01-12 12:00:37 -0800552 mTmpTaskViewMap.put(task.key, tv);
Winsonf24f2162016-01-05 12:11:55 -0800553 } else {
554 if (mTouchExplorationEnabled) {
555 lastFocusedTaskIndex = taskIndex;
556 resetFocusedTask(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800557 }
Winsonf24f2162016-01-05 12:11:55 -0800558 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -0800559 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800560 }
Winsonf24f2162016-01-05 12:11:55 -0800561
562 // Pick up all the newly visible children
Winsonc4387022016-02-24 12:05:26 -0800563 for (int i = tasks.size() - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800564 Task task = tasks.get(i);
565 TaskViewTransform transform = mCurrentTaskTransforms.get(i);
Winsonf24f2162016-01-05 12:11:55 -0800566
Winson3e874742016-01-07 10:08:17 -0800567 // Skip ignored tasks
Winson8aa99592016-01-19 15:07:07 -0800568 if (ignoreTasksSet.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800569 continue;
570 }
571
Winsonf24f2162016-01-05 12:11:55 -0800572 // Skip the invisible non-freeform stack tasks
Winson05e46ca2016-02-05 15:40:29 -0800573 if (!task.isFreeformTask() && !transform.visible) {
Winsonf24f2162016-01-05 12:11:55 -0800574 continue;
575 }
576
Winson55003902016-01-12 12:00:37 -0800577 TaskView tv = mTmpTaskViewMap.get(task.key);
Winsonf24f2162016-01-05 12:11:55 -0800578 if (tv == null) {
579 tv = mViewPool.pickUpViewFromPool(task, task);
580 if (task.isFreeformTask()) {
Winsonbe8e6962016-02-01 14:27:52 -0800581 tv.updateViewPropertiesToTaskTransform(transform, AnimationProps.IMMEDIATE,
Winsonf24f2162016-01-05 12:11:55 -0800582 mRequestUpdateClippingListener);
583 } else {
Winson68088812016-02-12 16:06:04 -0800584 if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
Winsonf24f2162016-01-05 12:11:55 -0800585 tv.updateViewPropertiesToTaskTransform(
586 mLayoutAlgorithm.getBackOfStackTransform(),
Winsonbe8e6962016-02-01 14:27:52 -0800587 AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
Winsonf24f2162016-01-05 12:11:55 -0800588 } else {
589 tv.updateViewPropertiesToTaskTransform(
590 mLayoutAlgorithm.getFrontOfStackTransform(),
Winsonbe8e6962016-02-01 14:27:52 -0800591 AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
Winsonf24f2162016-01-05 12:11:55 -0800592 }
593 }
594 } else {
595 // Reattach it in the right z order
596 final int taskIndex = mStack.indexOfStackTask(task);
597 final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
598 if (insertIndex != getTaskViews().indexOf(tv)){
599 detachViewFromParent(tv);
600 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
601 updateTaskViewsList();
602 }
603 }
604 }
605
606 // Update the focus if the previous focused task was returned to the view pool
607 if (lastFocusedTaskIndex != -1) {
Winsonc4387022016-02-24 12:05:26 -0800608 if (lastFocusedTaskIndex < visibleTaskRange[1]) {
609 setFocusedTask(visibleTaskRange[1], false /* scrollToTask */,
Winsonf24f2162016-01-05 12:11:55 -0800610 true /* requestViewFocus */);
611 } else {
Winsonc4387022016-02-24 12:05:26 -0800612 setFocusedTask(visibleTaskRange[0], false /* scrollToTask */,
Winsonf24f2162016-01-05 12:11:55 -0800613 true /* requestViewFocus */);
614 }
615 }
616 }
617
618 /**
Winson59924fe2016-03-17 14:13:18 -0700619 * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
Winsonf24f2162016-01-05 12:11:55 -0800620 */
Winsonbe8e6962016-02-01 14:27:52 -0800621 void relayoutTaskViews(AnimationProps animation) {
Winson59924fe2016-03-17 14:13:18 -0700622 relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
623 }
624
625 /**
626 * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
627 */
628 void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet) {
629 relayoutTaskViews(animation, ignoreTasksSet, false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800630 }
Winson3e874742016-01-07 10:08:17 -0800631
Winson8aa99592016-01-19 15:07:07 -0800632 /**
633 * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
634 * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
635 * animations that are current running on those task views, and will ensure that the children
636 * {@link TaskView}s will match the set of visible tasks in the stack.
637 *
638 * @param ignoreTasksSet the set of tasks to ignore in the relayout
639 */
Winson59924fe2016-03-17 14:13:18 -0700640 void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
641 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800642 // If we had a deferred animation, cancel that
Winson8aa99592016-01-19 15:07:07 -0800643 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800644
Winson8aa99592016-01-19 15:07:07 -0800645 // Synchronize the current set of TaskViews
Winsone693aaf2016-03-01 12:05:59 -0800646 bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
Winson59924fe2016-03-17 14:13:18 -0700647 ignoreTaskOverrides /* ignoreTaskOverrides */);
Winsonf24f2162016-01-05 12:11:55 -0800648
649 // Animate them to their final transforms with the given animation
650 List<TaskView> taskViews = getTaskViews();
651 int taskViewCount = taskViews.size();
652 for (int i = 0; i < taskViewCount; i++) {
Winson43336942016-03-07 14:52:59 -0800653 TaskView tv = taskViews.get(i);
654 int taskIndex = mStack.indexOfStackTask(tv.getTask());
655 TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
Winsonf24f2162016-01-05 12:11:55 -0800656
Winson8aa99592016-01-19 15:07:07 -0800657 if (ignoreTasksSet.contains(tv.getTask().key)) {
Winson3e874742016-01-07 10:08:17 -0800658 continue;
659 }
660
Winsonf24f2162016-01-05 12:11:55 -0800661 updateTaskViewToTransform(tv, transform, animation);
662 }
663 }
664
665 /**
666 * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
667 */
Winsonbe8e6962016-02-01 14:27:52 -0800668 void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
Winson8aa99592016-01-19 15:07:07 -0800669 mDeferredTaskViewLayoutAnimation = animation;
Winson1c846142016-01-22 11:34:38 -0800670 invalidate();
Winsonf24f2162016-01-05 12:11:55 -0800671 }
672
673 /**
674 * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
Winsonbe8e6962016-02-01 14:27:52 -0800675 * given set of {@link AnimationProps} properties.
Winsonf24f2162016-01-05 12:11:55 -0800676 */
677 public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
Winsonbe8e6962016-02-01 14:27:52 -0800678 AnimationProps animation) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500679 if (taskView.isAnimatingTo(transform)) {
680 return;
681 }
682 taskView.cancelTransformAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800683 taskView.updateViewPropertiesToTaskTransform(transform, animation,
684 mRequestUpdateClippingListener);
685 }
686
687 /**
Winson8aa99592016-01-19 15:07:07 -0800688 * Returns the current task transforms of all tasks, falling back to the stack layout if there
689 * is no {@link TaskView} for the task.
Winsonf24f2162016-01-05 12:11:55 -0800690 */
Winson8aa99592016-01-19 15:07:07 -0800691 public void getCurrentTaskTransforms(ArrayList<Task> tasks,
692 ArrayList<TaskViewTransform> transformsOut) {
693 Utilities.matchTaskListSize(tasks, transformsOut);
Winson66474132016-02-23 18:45:47 -0800694 int focusState = mLayoutAlgorithm.getFocusState();
Winson8aa99592016-01-19 15:07:07 -0800695 for (int i = tasks.size() - 1; i >= 0; i--) {
696 Task task = tasks.get(i);
697 TaskViewTransform transform = transformsOut.get(i);
698 TaskView tv = getChildViewForTask(task);
699 if (tv != null) {
700 transform.fillIn(tv);
701 } else {
702 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
Winsone693aaf2016-03-01 12:05:59 -0800703 focusState, transform, null, true /* forceUpdate */,
704 false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800705 }
706 transform.visible = true;
707 }
708 }
709
710 /**
711 * Returns the task transforms for all the tasks in the stack if the stack was at the given
Winson14991502016-02-15 15:40:08 -0800712 * {@param stackScroll} and {@param focusState}.
Winson8aa99592016-01-19 15:07:07 -0800713 */
Winson66474132016-02-23 18:45:47 -0800714 public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
Winson8aa99592016-01-19 15:07:07 -0800715 ArrayList<TaskViewTransform> transformsOut) {
716 Utilities.matchTaskListSize(tasks, transformsOut);
717 for (int i = tasks.size() - 1; i >= 0; i--) {
718 Task task = tasks.get(i);
719 TaskViewTransform transform = transformsOut.get(i);
Winson14991502016-02-15 15:40:08 -0800720 mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
Winsone693aaf2016-03-01 12:05:59 -0800721 true /* forceUpdate */, true /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800722 transform.visible = true;
723 }
724 }
725
726 /**
Winson05e46ca2016-02-05 15:40:29 -0800727 * Cancels the next deferred task view layout.
728 */
729 void cancelDeferredTaskViewLayoutAnimation() {
730 mDeferredTaskViewLayoutAnimation = null;
731 }
732
733 /**
Winson8aa99592016-01-19 15:07:07 -0800734 * Cancels all {@link TaskView} animations.
735 *
736 * @see #cancelAllTaskViewAnimations(ArraySet<Task.TaskKey>)
737 */
738 void cancelAllTaskViewAnimations() {
739 cancelAllTaskViewAnimations(mIgnoreTasks);
740 }
741
742 /**
743 * Cancels all {@link TaskView} animations.
744 *
745 * @param ignoreTasksSet The set of tasks to continue running their animations.
746 */
747 void cancelAllTaskViewAnimations(ArraySet<Task.TaskKey> ignoreTasksSet) {
Winsonf24f2162016-01-05 12:11:55 -0800748 List<TaskView> taskViews = getTaskViews();
Winson3e874742016-01-07 10:08:17 -0800749 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsonf24f2162016-01-05 12:11:55 -0800750 final TaskView tv = taskViews.get(i);
Winson8aa99592016-01-19 15:07:07 -0800751 if (!ignoreTasksSet.contains(tv.getTask().key)) {
752 tv.cancelTransformAnimation();
753 }
Winsonf24f2162016-01-05 12:11:55 -0800754 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800755 }
756
Winson3150e572015-10-23 15:07:24 -0700757 /**
758 * Updates the clip for each of the task views from back to front.
759 */
Winsonf24f2162016-01-05 12:11:55 -0800760 private void clipTaskViews() {
Winson Chung93748a12014-07-13 17:43:31 -0700761 // Update the clip on each task child
Winson Chung6ac8bd62015-01-07 16:38:35 -0800762 List<TaskView> taskViews = getTaskViews();
Winson3150e572015-10-23 15:07:24 -0700763 TaskView tmpTv = null;
Winson8aa99592016-01-19 15:07:07 -0800764 TaskView prevVisibleTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800765 int taskViewCount = taskViews.size();
Winson3150e572015-10-23 15:07:24 -0700766 for (int i = 0; i < taskViewCount; i++) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800767 TaskView tv = taskViews.get(i);
Winson3150e572015-10-23 15:07:24 -0700768 TaskView frontTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800769 int clipBottom = 0;
Winson8aa99592016-01-19 15:07:07 -0800770
Winson05e46ca2016-02-05 15:40:29 -0800771 if (isIgnoredTask(tv.getTask())) {
Winson8aa99592016-01-19 15:07:07 -0800772 // For each of the ignore tasks, update the translationZ of its TaskView to be
773 // between the translationZ of the tasks immediately underneath it
774 if (prevVisibleTv != null) {
775 tv.setTranslationZ(Math.max(tv.getTranslationZ(),
776 prevVisibleTv.getTranslationZ() + 0.1f));
777 }
778 }
779
Winson3150e572015-10-23 15:07:24 -0700780 if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800781 // Find the next view to clip against
Winson3150e572015-10-23 15:07:24 -0700782 for (int j = i + 1; j < taskViewCount; j++) {
783 tmpTv = taskViews.get(j);
Winsonef064132016-01-05 12:11:31 -0800784
Winson3150e572015-10-23 15:07:24 -0700785 if (tmpTv.shouldClipViewInStack()) {
786 frontTv = tmpTv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800787 break;
Winson Chung93748a12014-07-13 17:43:31 -0700788 }
789 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800790
791 // Clip against the next view, this is just an approximation since we are
792 // stacked and we can make assumptions about the visibility of the this
793 // task relative to the ones in front of it.
Winson3150e572015-10-23 15:07:24 -0700794 if (frontTv != null) {
Winsonbb410952015-12-04 14:34:11 -0800795 float taskBottom = tv.getBottom();
796 float frontTaskTop = frontTv.getTop();
Winson3150e572015-10-23 15:07:24 -0700797 if (frontTaskTop < taskBottom) {
798 // Map the stack view space coordinate (the rects) to view space
Winsonbb410952015-12-04 14:34:11 -0800799 clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
Winson3150e572015-10-23 15:07:24 -0700800 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800801 }
Winson Chung93748a12014-07-13 17:43:31 -0700802 }
Winsonf24f2162016-01-05 12:11:55 -0800803 tv.getViewBounds().setClipBottom(clipBottom);
Winsone693aaf2016-03-01 12:05:59 -0800804 tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
Winson8aa99592016-01-19 15:07:07 -0800805 prevVisibleTv = tv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800806 }
Winsonf24f2162016-01-05 12:11:55 -0800807 mTaskViewsClipDirty = false;
Winson Chung93748a12014-07-13 17:43:31 -0700808 }
809
Winson3e874742016-01-07 10:08:17 -0800810 /**
Winson8aa99592016-01-19 15:07:07 -0800811 * Updates the layout algorithm min and max virtual scroll bounds.
812 *
813 * @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
814 */
Winson003eda62016-03-11 14:56:00 -0800815 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
Winson8aa99592016-01-19 15:07:07 -0800816 updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
817 }
818
819 /**
Winson3e874742016-01-07 10:08:17 -0800820 * Updates the min and max virtual scroll bounds.
821 *
Winson8aa99592016-01-19 15:07:07 -0800822 * @param ignoreTasksSet the set of tasks to ignore in the relayout
Winson3e874742016-01-07 10:08:17 -0800823 */
Winson003eda62016-03-11 14:56:00 -0800824 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
Winson8aa99592016-01-19 15:07:07 -0800825 ArraySet<Task.TaskKey> ignoreTasksSet) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800826 // Compute the min and max scroll values
Winson3e874742016-01-07 10:08:17 -0800827 mLayoutAlgorithm.update(mStack, ignoreTasksSet);
Winson Chung303e1ff2014-03-07 15:06:19 -0800828
Winson8aa99592016-01-19 15:07:07 -0800829 // Update the freeform workspace background
Winsona5e6b362015-11-02 17:17:20 -0800830 SystemServicesProxy ssp = Recents.getSystemServices();
831 if (ssp.hasFreeformWorkspaceSupport()) {
832 mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
Winsona5e6b362015-11-02 17:17:20 -0800833 mFreeformWorkspaceBackground.setBounds(mTmpRect);
834 }
835
Winson Chung303e1ff2014-03-07 15:06:19 -0800836 if (boundScrollToNewMinMax) {
Winson Chung012ef362014-07-31 18:36:25 -0700837 mStackScroller.boundScroll();
Winson Chung303e1ff2014-03-07 15:06:19 -0800838 }
839 }
840
Winson Chung012ef362014-07-31 18:36:25 -0700841 /** Returns the scroller. */
842 public TaskStackViewScroller getScroller() {
843 return mStackScroller;
844 }
845
Winson0d14d4d2015-10-26 17:05:04 -0700846 /**
847 * Sets the focused task to the provided (bounded taskIndex).
Winsone5f1faa2015-11-20 12:26:23 -0800848 *
849 * @return whether or not the stack will scroll as a part of this focus change
Winson0d14d4d2015-10-26 17:05:04 -0700850 */
Winsonf24f2162016-01-05 12:11:55 -0800851 private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
Winsonaaf33bc2015-12-03 12:02:38 -0800852 final boolean requestViewFocus) {
Winson4b9cded2016-01-26 16:26:47 -0800853 return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800854 }
855
856 /**
Winson05e46ca2016-02-05 15:40:29 -0800857 * Sets the focused task to the provided (bounded focusTaskIndex).
Peter Schillerb124d562015-12-11 21:31:17 -0800858 *
859 * @return whether or not the stack will scroll as a part of this focus change
860 */
Winson05e46ca2016-02-05 15:40:29 -0800861 private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
862 boolean requestViewFocus, int timerIndicatorDuration) {
Winson0d14d4d2015-10-26 17:05:04 -0700863 // Find the next task to focus
Winson4b057c62016-01-12 15:01:52 -0800864 int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
Winson68088812016-02-12 16:06:04 -0800865 Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
Winson0d14d4d2015-10-26 17:05:04 -0700866 final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
Winson250608a2015-11-24 15:00:31 -0800867 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700868
Winson0d14d4d2015-10-26 17:05:04 -0700869 // Reset the last focused task state if changed
Winsonaaf33bc2015-12-03 12:02:38 -0800870 if (mFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800871 // Cancel the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800872 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800873 final TaskView tv = getChildViewForTask(mFocusedTask);
874 if (tv != null) {
875 tv.getHeaderView().cancelFocusTimerIndicator();
876 }
877 }
Winsonb433c5b2016-01-20 17:11:29 -0800878
879 resetFocusedTask(mFocusedTask);
Winson0d14d4d2015-10-26 17:05:04 -0700880 }
881
Winsone5f1faa2015-11-20 12:26:23 -0800882 boolean willScroll = false;
Winsonaaf33bc2015-12-03 12:02:38 -0800883 mFocusedTask = newFocusedTask;
Peter Schillerb124d562015-12-11 21:31:17 -0800884
Winsonaaf33bc2015-12-03 12:02:38 -0800885 if (newFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800886 // Start the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800887 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800888 final TaskView tv = getChildViewForTask(mFocusedTask);
889 if (tv != null) {
Winson4b9cded2016-01-26 16:26:47 -0800890 tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
Peter Schillerb124d562015-12-11 21:31:17 -0800891 } else {
892 // The view is null; set a flag for later
Winson4b9cded2016-01-26 16:26:47 -0800893 mStartTimerIndicatorDuration = timerIndicatorDuration;
Peter Schillerb124d562015-12-11 21:31:17 -0800894 }
895 }
896
Winson0d14d4d2015-10-26 17:05:04 -0700897 if (scrollToTask) {
Winson1c846142016-01-22 11:34:38 -0800898 // Cancel any running enter animations at this point when we scroll or change focus
899 if (!mEnterAnimationComplete) {
900 cancelAllTaskViewAnimations();
901 }
902
Winsone693aaf2016-03-01 12:05:59 -0800903 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winson05e46ca2016-02-05 15:40:29 -0800904 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
905 requestViewFocus);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700906 } else {
Winson05e46ca2016-02-05 15:40:29 -0800907 // Focus the task view
908 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
909 if (newFocusedTaskView != null) {
910 newFocusedTaskView.setFocusedState(true, requestViewFocus);
911 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700912 }
913 }
Winsone5f1faa2015-11-20 12:26:23 -0800914 return willScroll;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700915 }
916
Winson Chungd213a1e2014-10-02 11:18:30 -0700917 /**
Winson0d14d4d2015-10-26 17:05:04 -0700918 * Sets the focused task relative to the currently focused task.
919 *
Winsone5f1faa2015-11-20 12:26:23 -0800920 * @param forward whether to go to the next task in the stack (along the curve) or the previous
Winson1b585612015-11-06 09:16:26 -0800921 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
922 * if the currently focused task is not a stack task, will set the focus
923 * to the first visible stack task
Winson0d14d4d2015-10-26 17:05:04 -0700924 * @param animated determines whether to actually draw the highlight along with the change in
925 * focus.
Winson Chungd213a1e2014-10-02 11:18:30 -0700926 */
Winson1b585612015-11-06 09:16:26 -0800927 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
Winsone5f1faa2015-11-20 12:26:23 -0800928 setRelativeFocusedTask(forward, stackTasksOnly, animated, false);
929 }
930
931 /**
932 * Sets the focused task relative to the currently focused task.
933 *
934 * @param forward whether to go to the next task in the stack (along the curve) or the previous
935 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
936 * if the currently focused task is not a stack task, will set the focus
937 * to the first visible stack task
938 * @param animated determines whether to actually draw the highlight along with the change in
939 * focus.
940 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
Peter Schillerb124d562015-12-11 21:31:17 -0800941 * happens.
Winsone5f1faa2015-11-20 12:26:23 -0800942 */
943 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
944 boolean cancelWindowAnimations) {
Winson4b9cded2016-01-26 16:26:47 -0800945 setRelativeFocusedTask(forward, stackTasksOnly, animated, cancelWindowAnimations, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800946 }
947
948 /**
949 * Sets the focused task relative to the currently focused task.
950 *
951 * @param forward whether to go to the next task in the stack (along the curve) or the previous
952 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
953 * if the currently focused task is not a stack task, will set the focus
954 * to the first visible stack task
955 * @param animated determines whether to actually draw the highlight along with the change in
956 * focus.
957 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
958 * happens.
Winson4b9cded2016-01-26 16:26:47 -0800959 * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
Peter Schillerb124d562015-12-11 21:31:17 -0800960 */
961 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
Winson4b9cded2016-01-26 16:26:47 -0800962 boolean cancelWindowAnimations,
963 int timerIndicatorDuration) {
Winsonaaf33bc2015-12-03 12:02:38 -0800964 int newIndex = mStack.indexOfStackTask(mFocusedTask);
965 if (mFocusedTask != null) {
Winson1b585612015-11-06 09:16:26 -0800966 if (stackTasksOnly) {
Winson250608a2015-11-24 15:00:31 -0800967 List<Task> tasks = mStack.getStackTasks();
Winsonaaf33bc2015-12-03 12:02:38 -0800968 if (mFocusedTask.isFreeformTask()) {
Winson1b585612015-11-06 09:16:26 -0800969 // Try and focus the front most stack task
970 TaskView tv = getFrontMostTaskView(stackTasksOnly);
971 if (tv != null) {
Winson250608a2015-11-24 15:00:31 -0800972 newIndex = mStack.indexOfStackTask(tv.getTask());
Winson1b585612015-11-06 09:16:26 -0800973 }
974 } else {
975 // Try the next task if it is a stack task
Winsonaaf33bc2015-12-03 12:02:38 -0800976 int tmpNewIndex = newIndex + (forward ? -1 : 1);
Winson1b585612015-11-06 09:16:26 -0800977 if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
978 Task t = tasks.get(tmpNewIndex);
979 if (!t.isFreeformTask()) {
980 newIndex = tmpNewIndex;
981 }
982 }
983 }
984 } else {
Winson8b1871d2015-11-20 09:56:20 -0800985 // No restrictions, lets just move to the new task (looping forward/backwards if
986 // necessary)
Winson4b057c62016-01-12 15:01:52 -0800987 int taskCount = mStack.getTaskCount();
Winsonaaf33bc2015-12-03 12:02:38 -0800988 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
Winson1b585612015-11-06 09:16:26 -0800989 }
990 } else {
Winson23b0d3f2016-02-15 17:43:01 -0800991 // We don't have a focused task
992 float stackScroll = mStackScroller.getStackScroll();
993 ArrayList<Task> tasks = mStack.getStackTasks();
994 int taskCount = tasks.size();
995 if (forward) {
996 // Walk backwards and focus the next task smaller than the current stack scroll
997 for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
998 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
999 if (Float.compare(taskP, stackScroll) <= 0) {
1000 break;
1001 }
1002 }
1003 } else {
1004 // Walk forwards and focus the next task larger than the current stack scroll
1005 for (newIndex = 0; newIndex < taskCount; newIndex++) {
1006 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1007 if (Float.compare(taskP, stackScroll) >= 0) {
1008 break;
1009 }
1010 }
Winson1b585612015-11-06 09:16:26 -08001011 }
1012 }
1013 if (newIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001014 boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
Winson4b9cded2016-01-26 16:26:47 -08001015 true /* requestViewFocus */, timerIndicatorDuration);
Winsone5f1faa2015-11-20 12:26:23 -08001016 if (willScroll && cancelWindowAnimations) {
1017 // As we iterate to the next/previous task, cancel any current/lagging window
1018 // transition animations
1019 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1020 }
Winson1b585612015-11-06 09:16:26 -08001021 }
Winson Chunga0e88b52014-08-11 19:25:42 -07001022 }
1023
Winson0d14d4d2015-10-26 17:05:04 -07001024 /**
1025 * Resets the focused task.
1026 */
Winsona0731a12015-12-02 15:10:14 -08001027 void resetFocusedTask(Task task) {
1028 if (task != null) {
1029 TaskView tv = getChildViewForTask(task);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001030 if (tv != null) {
Winsonf24f2162016-01-05 12:11:55 -08001031 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001032 }
Winson Chungb0a28ea2014-10-28 15:21:35 -07001033 }
Winsonaaf33bc2015-12-03 12:02:38 -08001034 mFocusedTask = null;
Winson Chungb0a28ea2014-10-28 15:21:35 -07001035 }
1036
Winson142af422015-11-09 10:39:57 -08001037 /**
1038 * Returns the focused task.
1039 */
1040 Task getFocusedTask() {
Winsonaaf33bc2015-12-03 12:02:38 -08001041 return mFocusedTask;
Winson142af422015-11-09 10:39:57 -08001042 }
1043
Winson Chung303e1ff2014-03-07 15:06:19 -08001044 @Override
Winson Chungee445952014-09-09 16:12:59 +02001045 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1046 super.onInitializeAccessibilityEvent(event);
Winson Chung6ac8bd62015-01-07 16:38:35 -08001047 List<TaskView> taskViews = getTaskViews();
1048 int taskViewCount = taskViews.size();
1049 if (taskViewCount > 0) {
1050 TaskView backMostTask = taskViews.get(0);
1051 TaskView frontMostTask = taskViews.get(taskViewCount - 1);
Winson250608a2015-11-24 15:00:31 -08001052 event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
1053 event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
Winson Chung296278a2015-12-17 12:09:02 -05001054 event.setContentDescription(frontMostTask.getTask().title);
Winson Chungee445952014-09-09 16:12:59 +02001055 }
Winson4b057c62016-01-12 15:01:52 -08001056 event.setItemCount(mStack.getTaskCount());
Winson59924fe2016-03-17 14:13:18 -07001057
1058 int stackHeight = mLayoutAlgorithm.mStackRect.height();
1059 event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
1060 event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
Winson Chungee445952014-09-09 16:12:59 +02001061 }
1062
1063 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001064 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1065 super.onInitializeAccessibilityNodeInfo(info);
1066 List<TaskView> taskViews = getTaskViews();
1067 int taskViewCount = taskViews.size();
Winsonaaf33bc2015-12-03 12:02:38 -08001068 if (taskViewCount > 1 && mFocusedTask != null) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001069 info.setScrollable(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001070 int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask);
1071 if (focusedTaskIndex > 0) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001072 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1073 }
Winson4b057c62016-01-12 15:01:52 -08001074 if (focusedTaskIndex < mStack.getTaskCount() - 1) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001075 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1076 }
1077 }
1078 }
1079
1080 @Override
Winson Chung4ab4d832015-12-11 10:25:46 -05001081 protected Parcelable onSaveInstanceState() {
1082 Bundle savedState = new Bundle();
1083 savedState.putParcelable(KEY_SAVED_STATE_SUPER, super.onSaveInstanceState());
Winson66474132016-02-23 18:45:47 -08001084 savedState.putInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState());
Winson Chung4ab4d832015-12-11 10:25:46 -05001085 savedState.putFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL, mStackScroller.getStackScroll());
1086 return super.onSaveInstanceState();
1087 }
1088
1089 @Override
1090 protected void onRestoreInstanceState(Parcelable state) {
1091 Bundle savedState = (Bundle) state;
1092 super.onRestoreInstanceState(savedState.getParcelable(KEY_SAVED_STATE_SUPER));
1093
Winson66474132016-02-23 18:45:47 -08001094 mLayoutAlgorithm.setFocusState(savedState.getInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE));
Winson Chung4ab4d832015-12-11 10:25:46 -05001095 mStackScroller.setStackScroll(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL));
1096 }
1097
1098 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001099 public CharSequence getAccessibilityClassName() {
1100 return TaskStackView.class.getName();
1101 }
1102
1103 @Override
1104 public boolean performAccessibilityAction(int action, Bundle arguments) {
1105 if (super.performAccessibilityAction(action, arguments)) {
1106 return true;
1107 }
Winson0d14d4d2015-10-26 17:05:04 -07001108 switch (action) {
1109 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
Winson1b585612015-11-06 09:16:26 -08001110 setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */);
Winson0d14d4d2015-10-26 17:05:04 -07001111 return true;
1112 }
1113 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
Winson1b585612015-11-06 09:16:26 -08001114 setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */);
Winson0d14d4d2015-10-26 17:05:04 -07001115 return true;
Winson Chung83ea6f72015-06-17 13:00:23 -07001116 }
1117 }
1118 return false;
1119 }
1120
1121 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -08001122 public boolean onInterceptTouchEvent(MotionEvent ev) {
1123 return mTouchHandler.onInterceptTouchEvent(ev);
1124 }
1125
1126 @Override
1127 public boolean onTouchEvent(MotionEvent ev) {
1128 return mTouchHandler.onTouchEvent(ev);
1129 }
1130
1131 @Override
Winson Chungd213a1e2014-10-02 11:18:30 -07001132 public boolean onGenericMotionEvent(MotionEvent ev) {
1133 return mTouchHandler.onGenericMotionEvent(ev);
1134 }
1135
1136 @Override
Winson Chungd7b2cb12014-06-26 15:08:50 -07001137 public void computeScroll() {
Winsonf24f2162016-01-05 12:11:55 -08001138 if (mStackScroller.computeScroll()) {
1139 // Notify accessibility
1140 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
1141 }
Winson8aa99592016-01-19 15:07:07 -08001142 if (mDeferredTaskViewLayoutAnimation != null) {
1143 relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
Winsonf24f2162016-01-05 12:11:55 -08001144 mTaskViewsClipDirty = true;
Winson8aa99592016-01-19 15:07:07 -08001145 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -08001146 }
1147 if (mTaskViewsClipDirty) {
1148 clipTaskViews();
1149 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001150 }
1151
Winson3e874742016-01-07 10:08:17 -08001152 /**
Winson8aa99592016-01-19 15:07:07 -08001153 * Computes the maximum number of visible tasks and thumbnails. Requires that
Winsoneca4ab62015-11-04 10:50:28 -08001154 * updateLayoutForStack() is called first.
Winson Chunga91c2932014-11-07 15:02:38 -08001155 */
Winson36a5a2c2015-10-29 18:04:39 -07001156 public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
Winson250608a2015-11-24 15:00:31 -08001157 return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
Winson Chunga91c2932014-11-07 15:02:38 -08001158 }
1159
Winson3e874742016-01-07 10:08:17 -08001160 /**
Winson59924fe2016-03-17 14:13:18 -07001161 * Updates the system insets.
Winson3e874742016-01-07 10:08:17 -08001162 */
Winson59924fe2016-03-17 14:13:18 -07001163 public void setSystemInsets(Rect systemInsets) {
Winson49df4202016-01-25 17:33:34 -08001164 if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
Winsonf9357d92016-03-25 15:14:37 -07001165 mStableLayoutAlgorithm.setSystemInsets(systemInsets);
Winson49df4202016-01-25 17:33:34 -08001166 mLayoutAlgorithm.setSystemInsets(systemInsets);
Winson49df4202016-01-25 17:33:34 -08001167 requestLayout();
1168 }
Winson147ecaf2015-09-16 16:49:55 -07001169 }
1170
Winson Chunga91c2932014-11-07 15:02:38 -08001171 /**
Winson Chunga4ccb862014-08-22 15:26:27 -07001172 * This is called with the full window width and height to allow stack view children to
Winson Chungdcfa7972014-07-22 12:27:13 -07001173 * perform the full screen transition down.
Winson Chungf7bca432014-04-30 17:11:13 -07001174 */
Winson Chung303e1ff2014-03-07 15:06:19 -08001175 @Override
1176 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson70f0bf72016-02-01 14:05:29 -08001177 mInMeasureLayout = true;
Winson Chung303e1ff2014-03-07 15:06:19 -08001178 int width = MeasureSpec.getSize(widthMeasureSpec);
1179 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -08001180
Winson59924fe2016-03-17 14:13:18 -07001181 // Update the stable stack bounds, but only update the current stack bounds if the stable
1182 // bounds have changed. This is because we may get spurious measures while dragging where
1183 // our current stack bounds reflect the target drop region.
1184 mLayoutAlgorithm.getTaskStackBounds(new Rect(0, 0, width, height),
Winson008ee15f2016-03-18 17:17:25 -07001185 mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
Winson59924fe2016-03-17 14:13:18 -07001186 if (!mTmpRect.equals(mStableStackBounds)) {
1187 mStableStackBounds.set(mTmpRect);
1188 mStackBounds.set(mTmpRect);
1189 mStableWindowRect.set(0, 0, width, height);
1190 mWindowRect.set(0, 0, width, height);
1191 }
1192
Winson8aa99592016-01-19 15:07:07 -08001193 // Compute the rects in the stack algorithm
Winsonf9357d92016-03-25 15:14:37 -07001194 mStableLayoutAlgorithm.initialize(mStableWindowRect, mStableStackBounds,
1195 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winson59924fe2016-03-17 14:13:18 -07001196 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001197 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1198 updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
Winson Chung303e1ff2014-03-07 15:06:19 -08001199
Winsonf24f2162016-01-05 12:11:55 -08001200 // If this is the first layout, then scroll to the front of the stack, then update the
1201 // TaskViews with the stack so that we can lay them out
Winsone693aaf2016-03-01 12:05:59 -08001202 // TODO: The second check is a workaround for wacky layouts that we get while docking via
1203 // long pressing the recents button
1204 if (mAwaitingFirstLayout ||
1205 (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) {
1206 updateToInitialState();
Winson Chung303e1ff2014-03-07 15:06:19 -08001207 }
Winsone693aaf2016-03-01 12:05:59 -08001208
Winson8aa99592016-01-19 15:07:07 -08001209 // Rebind all the views, including the ignore ones
Winsone693aaf2016-03-01 12:05:59 -08001210 bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
1211 false /* ignoreTaskOverrides */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001212
Winson Chungdcfa7972014-07-22 12:27:13 -07001213 // Measure each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001214 mTmpTaskViews.clear();
1215 mTmpTaskViews.addAll(getTaskViews());
1216 mTmpTaskViews.addAll(mViewPool.getViews());
1217 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001218 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001219 measureTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001220 }
1221
1222 setMeasuredDimension(width, height);
Winson70f0bf72016-02-01 14:05:29 -08001223 mInMeasureLayout = false;
1224 }
1225
1226 /**
1227 * Measures a TaskView.
1228 */
1229 private void measureTaskView(TaskView tv) {
1230 if (tv.getBackground() != null) {
1231 tv.getBackground().getPadding(mTmpRect);
1232 } else {
1233 mTmpRect.setEmpty();
1234 }
Winsonf9357d92016-03-25 15:14:37 -07001235 Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
Winson70f0bf72016-02-01 14:05:29 -08001236 tv.measure(
Winsonf9357d92016-03-25 15:14:37 -07001237 MeasureSpec.makeMeasureSpec(taskRect.width() + mTmpRect.left + mTmpRect.right,
Winson70f0bf72016-02-01 14:05:29 -08001238 MeasureSpec.EXACTLY),
Winsonf9357d92016-03-25 15:14:37 -07001239 MeasureSpec.makeMeasureSpec(taskRect.height() + mTmpRect.top + mTmpRect.bottom,
Winson70f0bf72016-02-01 14:05:29 -08001240 MeasureSpec.EXACTLY));
Winson Chung303e1ff2014-03-07 15:06:19 -08001241 }
1242
1243 @Override
1244 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson36a5a2c2015-10-29 18:04:39 -07001245 // Layout each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001246 mTmpTaskViews.clear();
1247 mTmpTaskViews.addAll(getTaskViews());
1248 mTmpTaskViews.addAll(mViewPool.getViews());
1249 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001250 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001251 layoutTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001252 }
1253
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001254 if (changed) {
Winsona2236f12015-11-13 16:10:01 -08001255 if (mStackScroller.isScrollOutOfBounds()) {
1256 mStackScroller.boundScroll();
1257 }
Winsonf24f2162016-01-05 12:11:55 -08001258 }
Winson8aa99592016-01-19 15:07:07 -08001259 // Relayout all of the task views including the ignored ones
Winsonbe8e6962016-02-01 14:27:52 -08001260 relayoutTaskViews(AnimationProps.IMMEDIATE, EMPTY_TASK_SET);
Winsonf24f2162016-01-05 12:11:55 -08001261 clipTaskViews();
1262
1263 if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
1264 mAwaitingFirstLayout = false;
1265 onFirstLayout();
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001266 }
Winson Chungdcfa7972014-07-22 12:27:13 -07001267 }
Winson Chung24cf1522014-05-29 12:03:33 -07001268
Winson70f0bf72016-02-01 14:05:29 -08001269 /**
1270 * Lays out a TaskView.
1271 */
1272 private void layoutTaskView(TaskView tv) {
1273 if (tv.getBackground() != null) {
1274 tv.getBackground().getPadding(mTmpRect);
1275 } else {
1276 mTmpRect.setEmpty();
1277 }
Winsonf9357d92016-03-25 15:14:37 -07001278 Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
Winson59924fe2016-03-17 14:13:18 -07001279 tv.cancelTransformAnimation();
Winson70f0bf72016-02-01 14:05:29 -08001280 tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
1281 taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
1282 }
1283
Winson Chungdcfa7972014-07-22 12:27:13 -07001284 /** Handler for the first layout. */
1285 void onFirstLayout() {
Winsonf24f2162016-01-05 12:11:55 -08001286 // Setup the view for the enter animation
1287 mAnimationHelper.prepareForEnterAnimation();
Winson Chung083baf92014-07-11 10:32:42 -07001288
Winsona78a8f32015-12-03 10:55:01 -08001289 // Animate in the freeform workspace
Winsonbe8e6962016-02-01 14:27:52 -08001290 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
1291 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
1292 Interpolators.FAST_OUT_SLOW_IN));
Winsona78a8f32015-12-03 10:55:01 -08001293
Winson0d14d4d2015-10-26 17:05:04 -07001294 // Set the task focused state without requesting view focus, and leave the focus animations
1295 // until after the enter-animation
Winson5da43472015-11-04 17:39:55 -08001296 RecentsConfiguration config = Recents.getConfiguration();
1297 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson4b9cded2016-01-26 16:26:47 -08001298 int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
Winson5da43472015-11-04 17:39:55 -08001299 if (focusedTaskIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001300 setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
Winson5da43472015-11-04 17:39:55 -08001301 false /* requestViewFocus */);
Winson0983e022015-10-13 17:21:42 -07001302 }
Winson Chung860e2d82014-12-04 11:43:02 -08001303
Winson8f6ee482016-03-18 17:51:48 -07001304 // Update the stack action button visibility
1305 if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1306 EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
Winson Chungde750de2015-12-11 10:26:06 -05001307 } else {
Winson8f6ee482016-03-18 17:51:48 -07001308 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winsonc29ff002015-11-20 16:00:45 -08001309 }
Winson Chung24cf1522014-05-29 12:03:33 -07001310 }
1311
Winson671e8f92016-01-12 13:16:56 -08001312 public boolean isTouchPointInView(float x, float y, TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001313 mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
1314 mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
1315 return mTmpRect.contains((int) x, (int) y);
1316 }
1317
1318 /**
1319 * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
1320 * calculating the scroll position before and after a layout change.
1321 */
1322 public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
1323 for (int i = tasks.size() - 1; i >= 0; i--) {
1324 Task task = tasks.get(i);
1325
1326 // Ignore deleting tasks
Winson05e46ca2016-02-05 15:40:29 -08001327 if (isIgnoredTask(task)) {
Winson8aa99592016-01-19 15:07:07 -08001328 if (i == tasks.size() - 1) {
1329 isFrontMostTask.value = true;
1330 }
1331 continue;
1332 }
1333 return task;
1334 }
1335 return null;
Winson Chung303e1ff2014-03-07 15:06:19 -08001336 }
1337
Jorim Jaggi900fb482015-06-02 15:07:33 -07001338 @Override
Winsonbe8e6962016-02-01 14:27:52 -08001339 protected void onDraw(Canvas canvas) {
1340 super.onDraw(canvas);
1341
Winson36a5a2c2015-10-29 18:04:39 -07001342 // Draw the freeform workspace background
Winson805578d2015-11-23 14:47:37 -08001343 SystemServicesProxy ssp = Recents.getSystemServices();
1344 if (ssp.hasFreeformWorkspaceSupport()) {
1345 if (mFreeformWorkspaceBackground.getAlpha() > 0) {
1346 mFreeformWorkspaceBackground.draw(canvas);
1347 }
Winson36a5a2c2015-10-29 18:04:39 -07001348 }
Jorim Jaggi900fb482015-06-02 15:07:33 -07001349 }
1350
Winsona78a8f32015-12-03 10:55:01 -08001351 @Override
1352 protected boolean verifyDrawable(Drawable who) {
1353 if (who == mFreeformWorkspaceBackground) {
1354 return true;
1355 }
1356 return super.verifyDrawable(who);
1357 }
1358
Winsona5e6b362015-11-02 17:17:20 -08001359 /**
1360 * Launches the freeform tasks.
1361 */
1362 public boolean launchFreeformTasks() {
Winsonf24f2162016-01-05 12:11:55 -08001363 ArrayList<Task> tasks = mStack.getFreeformTasks();
1364 if (!tasks.isEmpty()) {
1365 Task frontTask = tasks.get(tasks.size() - 1);
1366 if (frontTask != null && frontTask.isFreeformTask()) {
1367 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
1368 frontTask, null, INVALID_STACK_ID, false));
1369 return true;
1370 }
Winsona5e6b362015-11-02 17:17:20 -08001371 }
1372 return false;
1373 }
1374
Winson Chung303e1ff2014-03-07 15:06:19 -08001375 /**** TaskStackCallbacks Implementation ****/
1376
1377 @Override
Winson Chung06266772015-12-11 10:24:21 -05001378 public void onStackTaskAdded(TaskStack stack, Task newTask) {
1379 // Update the min/max scroll and animate other task views into their new positions
Winson8aa99592016-01-19 15:07:07 -08001380 updateLayoutAlgorithm(true /* boundScroll */);
Winson Chung06266772015-12-11 10:24:21 -05001381
1382 // Animate all the tasks into place
Winsonbe8e6962016-02-01 14:27:52 -08001383 relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winsonc0d70582016-01-29 10:24:39 -08001384 Interpolators.FAST_OUT_SLOW_IN));
Winson Chung06266772015-12-11 10:24:21 -05001385 }
1386
Winson8aa99592016-01-19 15:07:07 -08001387 /**
1388 * We expect that the {@link TaskView} associated with the removed task is already hidden.
1389 */
Winson Chung06266772015-12-11 10:24:21 -05001390 @Override
Winsonaaf33bc2015-12-03 12:02:38 -08001391 public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Winson20684082016-03-16 17:13:34 -07001392 Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
Winsonaaf33bc2015-12-03 12:02:38 -08001393 if (mFocusedTask == removedTask) {
Winsona0731a12015-12-02 15:10:14 -08001394 resetFocusedTask(removedTask);
1395 }
1396
Winson8aa99592016-01-19 15:07:07 -08001397 // Remove the view associated with this task, we can't rely on updateTransforms
1398 // to work here because the task is no longer in the list
1399 TaskView tv = getChildViewForTask(removedTask);
1400 if (tv != null) {
1401 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -08001402 }
1403
Winson8aa99592016-01-19 15:07:07 -08001404 // Remove the task from the ignored set
1405 removeIgnoreTask(removedTask);
1406
1407 // If requested, relayout with the given animation
1408 if (animation != null) {
1409 updateLayoutAlgorithm(true /* boundScroll */);
1410 relayoutTaskViews(animation);
1411 }
Winsonf24f2162016-01-05 12:11:55 -08001412
Winson Chung931c51f2015-12-17 17:08:55 -05001413 // Update the new front most task's action button
1414 if (mScreenPinningEnabled && newFrontMostTask != null) {
Winson Chung1f24c7e2014-07-11 17:06:48 -07001415 TaskView frontTv = getChildViewForTask(newFrontMostTask);
1416 if (frontTv != null) {
Winson Chung931c51f2015-12-17 17:08:55 -05001417 frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
Winson Chung1f24c7e2014-07-11 17:06:48 -07001418 }
1419 }
1420
Winson397ae742015-11-20 11:27:33 -08001421 // If there are no remaining tasks, then just close recents
Winson4b057c62016-01-12 15:01:52 -08001422 if (mStack.getTaskCount() == 0) {
Winson20684082016-03-16 17:13:34 -07001423 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
1424 ? R.string.recents_empty_message
1425 : R.string.recents_empty_message_dismissed_all));
Winson Chung9f49df92014-05-07 18:08:34 -07001426 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001427 }
1428
Winsona1ededd2016-03-25 12:23:12 -07001429 @Override
1430 public void onStackTasksUpdated(TaskStack stack) {
1431 // Update the layout and immediately layout
1432 updateLayoutAlgorithm(false /* boundScroll */);
1433 relayoutTaskViews(AnimationProps.IMMEDIATE);
1434
1435 // Rebind all the task views. This will not trigger new resources to be loaded
1436 // unless they have actually changed
1437 List<TaskView> taskViews = getTaskViews();
1438 int taskViewCount = taskViews.size();
1439 for (int i = 0; i < taskViewCount; i++) {
1440 TaskView tv = taskViews.get(i);
1441 bindTaskView(tv, tv.getTask());
1442 }
1443 }
1444
Winson Chung303e1ff2014-03-07 15:06:19 -08001445 /**** ViewPoolConsumer Implementation ****/
1446
1447 @Override
1448 public TaskView createView(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -07001449 return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
Winson Chung303e1ff2014-03-07 15:06:19 -08001450 }
1451
1452 @Override
Winson05e46ca2016-02-05 15:40:29 -08001453 public void onReturnViewToPool(TaskView tv) {
Winson Chung931c51f2015-12-17 17:08:55 -05001454 final Task task = tv.getTask();
Winson Chung303e1ff2014-03-07 15:06:19 -08001455
Winson43336942016-03-07 14:52:59 -08001456 // Unbind the task from the task view
1457 unbindTaskView(tv, task);
Winson Chung303e1ff2014-03-07 15:06:19 -08001458
Winson Chung931c51f2015-12-17 17:08:55 -05001459 // Reset the view properties and view state
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001460 tv.resetViewProperties();
Winsonf24f2162016-01-05 12:11:55 -08001461 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungb0a28ea2014-10-28 15:21:35 -07001462 tv.setClipViewInStack(false);
Winson Chung931c51f2015-12-17 17:08:55 -05001463 if (mScreenPinningEnabled) {
Winsonf24f2162016-01-05 12:11:55 -08001464 tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
Winson Chung931c51f2015-12-17 17:08:55 -05001465 }
Winson3c107162016-01-22 15:53:00 -08001466
1467 // Detach the view from the hierarchy
1468 detachViewFromParent(tv);
1469 // Update the task views list after removing the task view
1470 updateTaskViewsList();
Winson Chung303e1ff2014-03-07 15:06:19 -08001471 }
1472
1473 @Override
Winson05e46ca2016-02-05 15:40:29 -08001474 public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001475 // Find the index where this task should be placed in the stack
Winson250608a2015-11-24 15:00:31 -08001476 int taskIndex = mStack.indexOfStackTask(task);
Winsona78a8f32015-12-03 10:55:01 -08001477 int insertIndex = findTaskViewInsertIndex(task, taskIndex);
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001478
Winson Chung303e1ff2014-03-07 15:06:19 -08001479 // Add/attach the view to the hierarchy
Winson Chung303e1ff2014-03-07 15:06:19 -08001480 if (isNewView) {
Winson70f0bf72016-02-01 14:05:29 -08001481 if (mInMeasureLayout) {
1482 // If we are measuring the layout, then just add the view normally as it will be
1483 // laid out during the layout pass
1484 addView(tv, insertIndex);
1485 } else {
1486 // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
1487 // pass, and we should layout the new child ourselves
1488 ViewGroup.LayoutParams params = tv.getLayoutParams();
1489 if (params == null) {
1490 params = generateDefaultLayoutParams();
1491 }
1492 addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
1493 measureTaskView(tv);
1494 layoutTaskView(tv);
1495 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001496 } else {
1497 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
1498 }
Winson Chung6ac8bd62015-01-07 16:38:35 -08001499 // Update the task views list after adding the new task view
1500 updateTaskViewsList();
Winson Chungb0a28ea2014-10-28 15:21:35 -07001501
Winson43336942016-03-07 14:52:59 -08001502 // Bind the task view to the new task
1503 bindTaskView(tv, task);
Winson3c107162016-01-22 15:53:00 -08001504
1505 // If the doze trigger has already fired, then update the state for this task view
Winsone693aaf2016-03-01 12:05:59 -08001506 if (mUIDozeTrigger.isAsleep()) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +00001507 tv.setNoUserInteractionState();
1508 }
Winson3c107162016-01-22 15:53:00 -08001509
Winson Chungb0a28ea2014-10-28 15:21:35 -07001510 // Set the new state for this view, including the callbacks and view clipping
1511 tv.setCallbacks(this);
1512 tv.setTouchEnabled(true);
1513 tv.setClipViewInStack(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001514 if (mFocusedTask == task) {
Winsonf24f2162016-01-05 12:11:55 -08001515 tv.setFocusedState(true, false /* requestViewFocus */);
Winson4b9cded2016-01-26 16:26:47 -08001516 if (mStartTimerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -08001517 // The timer indicator couldn't be started before, so start it now
Winson4b9cded2016-01-26 16:26:47 -08001518 tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
1519 mStartTimerIndicatorDuration = 0;
Peter Schillerb124d562015-12-11 21:31:17 -08001520 }
Winsona0731a12015-12-02 15:10:14 -08001521 }
Winson Chung931c51f2015-12-17 17:08:55 -05001522
1523 // Restore the action button visibility if it is the front most task view
Winson35a8b042016-01-22 09:41:09 -08001524 if (mScreenPinningEnabled && tv.getTask() ==
1525 mStack.getStackFrontMostTask(false /* includeFreeform */)) {
Winson Chung931c51f2015-12-17 17:08:55 -05001526 tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
1527 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001528 }
1529
1530 @Override
1531 public boolean hasPreferredData(TaskView tv, Task preferredData) {
1532 return (tv.getTask() == preferredData);
1533 }
1534
Winson43336942016-03-07 14:52:59 -08001535 private void bindTaskView(TaskView tv, Task task) {
1536 // Rebind the task and request that this task's data be filled into the TaskView
1537 tv.onTaskBound(task);
1538
1539 // Load the task data
1540 Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
1541 }
1542
1543 private void unbindTaskView(TaskView tv, Task task) {
1544 // Report that this task's data is no longer being used
1545 Recents.getTaskLoader().unloadTaskData(task);
1546 }
1547
Winson Chung303e1ff2014-03-07 15:06:19 -08001548 /**** TaskViewCallbacks Implementation ****/
1549
1550 @Override
Winson Chung93748a12014-07-13 17:43:31 -07001551 public void onTaskViewClipStateChanged(TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001552 if (!mTaskViewsClipDirty) {
1553 mTaskViewsClipDirty = true;
1554 invalidate();
1555 }
Winson Chung93748a12014-07-13 17:43:31 -07001556 }
1557
Winson1c846142016-01-22 11:34:38 -08001558 /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
1559
1560 @Override
Winson66474132016-02-23 18:45:47 -08001561 public void onFocusStateChanged(int prevFocusState, int curFocusState) {
Winson1c846142016-01-22 11:34:38 -08001562 if (mDeferredTaskViewLayoutAnimation == null) {
1563 mUIDozeTrigger.poke();
Winsonbe8e6962016-02-01 14:27:52 -08001564 relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
Winson1c846142016-01-22 11:34:38 -08001565 }
1566 }
1567
Winson Chung012ef362014-07-31 18:36:25 -07001568 /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
1569
1570 @Override
Winson14991502016-02-15 15:40:08 -08001571 public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
Winson Chung012ef362014-07-31 18:36:25 -07001572 mUIDozeTrigger.poke();
Winson8aa99592016-01-19 15:07:07 -08001573 if (animation != null) {
1574 relayoutTaskViewsOnNextFrame(animation);
1575 }
Winson66474132016-02-23 18:45:47 -08001576 mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
Winsonc29ff002015-11-20 16:00:45 -08001577
Winson49df4202016-01-25 17:33:34 -08001578 if (mEnterAnimationComplete) {
Winson8f6ee482016-03-18 17:51:48 -07001579 if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1580 curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1581 EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
1582 } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1583 curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1584 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winson49df4202016-01-25 17:33:34 -08001585 }
Winsonc29ff002015-11-20 16:00:45 -08001586 }
Winson Chung012ef362014-07-31 18:36:25 -07001587 }
1588
Winsone6c90732015-09-24 16:06:29 -07001589 /**** EventBus Events ****/
Winson Chung9f49df92014-05-07 18:08:34 -07001590
Winsone6c90732015-09-24 16:06:29 -07001591 public final void onBusEvent(PackagesChangedEvent event) {
Winson Chung04400672014-10-17 14:53:30 -07001592 // Compute which components need to be removed
Winson55003902016-01-12 12:00:37 -08001593 ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
Winsone7f138c2015-10-22 16:15:21 -07001594 event.packageName, event.userId);
Winson Chung04400672014-10-17 14:53:30 -07001595
Winson Chung9f49df92014-05-07 18:08:34 -07001596 // For other tasks, just remove them directly if they no longer exist
Winson250608a2015-11-24 15:00:31 -08001597 ArrayList<Task> tasks = mStack.getStackTasks();
Winson Chung9f49df92014-05-07 18:08:34 -07001598 for (int i = tasks.size() - 1; i >= 0; i--) {
1599 final Task t = tasks.get(i);
Winsone7f138c2015-10-22 16:15:21 -07001600 if (removedComponents.contains(t.key.getComponent())) {
Winson0d14d4d2015-10-26 17:05:04 -07001601 final TaskView tv = getChildViewForTask(t);
Winson Chung9f49df92014-05-07 18:08:34 -07001602 if (tv != null) {
1603 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001604 tv.dismissTask();
Winson Chung9f49df92014-05-07 18:08:34 -07001605 } else {
1606 // Otherwise, remove the task from the stack immediately
Winson20684082016-03-16 17:13:34 -07001607 mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson Chung9f49df92014-05-07 18:08:34 -07001608 }
1609 }
1610 }
1611 }
Winson2536c7e2015-10-01 15:49:31 -07001612
Winson Chung48f2cda2015-12-11 13:20:12 -05001613 public final void onBusEvent(LaunchTaskEvent event) {
1614 // Cancel any doze triggers once a task is launched
1615 mUIDozeTrigger.stopDozing();
1616 }
1617
Winsonb61e6542016-02-04 14:37:18 -08001618 public final void onBusEvent(LaunchNextTaskRequestEvent event) {
1619 int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
1620 if (launchTaskIndex != -1) {
1621 launchTaskIndex = Math.max(0, launchTaskIndex - 1);
1622 } else {
1623 launchTaskIndex = mStack.getTaskCount() - 1;
1624 }
1625 if (launchTaskIndex != -1) {
1626 // Stop all animations
1627 mUIDozeTrigger.stopDozing();
1628 cancelAllTaskViewAnimations();
1629
Winson96e61342016-03-15 16:47:19 -07001630 final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
Winsonf8597b22016-03-23 18:44:26 -07001631 float curScroll = mStackScroller.getStackScroll();
1632 float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
1633 float absScrollDiff = Math.abs(targetScroll - curScroll);
1634 if (getChildViewForTask(launchTask) == null || absScrollDiff > 0.35f) {
1635 int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
1636 absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
1637 mStackScroller.animateScroll(targetScroll,
Winson96e61342016-03-15 16:47:19 -07001638 duration, new Runnable() {
1639 @Override
1640 public void run() {
1641 EventBus.getDefault().send(new LaunchTaskEvent(
1642 getChildViewForTask(launchTask), launchTask, null,
1643 INVALID_STACK_ID, false /* screenPinningRequested */));
1644 }
1645 });
1646 } else {
1647 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
1648 launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
1649 }
Winsond9342902016-02-25 10:18:33 -08001650
1651 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
1652 launchTask.key.getComponent().toString());
Winsonbc0f8cd2016-03-15 15:44:48 -07001653 } else if (mStack.getTaskCount() == 0) {
1654 // If there are no tasks, then just hide recents back to home.
1655 EventBus.getDefault().send(new HideRecentsEvent(false, true));
Winsonb61e6542016-02-04 14:37:18 -08001656 }
1657 }
1658
Winsonef064132016-01-05 12:11:31 -08001659 public final void onBusEvent(LaunchTaskStartedEvent event) {
Winsonf24f2162016-01-05 12:11:55 -08001660 mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
1661 event.getAnimationTrigger());
Winson0d14d4d2015-10-26 17:05:04 -07001662 }
Winson2536c7e2015-10-01 15:49:31 -07001663
Winsonef064132016-01-05 12:11:31 -08001664 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
1665 // Stop any scrolling
1666 mStackScroller.stopScroller();
1667 mStackScroller.stopBoundScrollAnimation();
Winson2536c7e2015-10-01 15:49:31 -07001668
Winsonef064132016-01-05 12:11:31 -08001669 // Start the task animations
Winsonf24f2162016-01-05 12:11:55 -08001670 mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001671
1672 // Dismiss the freeform workspace background
Winson50448632016-02-01 18:04:59 -08001673 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
Winsonbe8e6962016-02-01 14:27:52 -08001674 animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
1675 Interpolators.FAST_OUT_SLOW_IN));
Winson0d14d4d2015-10-26 17:05:04 -07001676 }
Winson42be4312015-10-10 11:51:08 -07001677
Winson0d14d4d2015-10-26 17:05:04 -07001678 public final void onBusEvent(DismissFocusedTaskViewEvent event) {
Winsonaaf33bc2015-12-03 12:02:38 -08001679 if (mFocusedTask != null) {
1680 TaskView tv = getChildViewForTask(mFocusedTask);
1681 if (tv != null) {
1682 tv.dismissTask();
1683 }
1684 resetFocusedTask(mFocusedTask);
Winson2536c7e2015-10-01 15:49:31 -07001685 }
1686 }
Winsone7f138c2015-10-22 16:15:21 -07001687
Winsonef064132016-01-05 12:11:31 -08001688 public final void onBusEvent(final DismissTaskViewEvent event) {
1689 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001690 mAnimationHelper.startDeleteTaskAnimation(event.task, event.taskView,
1691 event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001692 }
1693
1694 public final void onBusEvent(TaskViewDismissedEvent event) {
Winson8aa99592016-01-19 15:07:07 -08001695 removeTaskViewFromStack(event.taskView, event.task);
Winsonef064132016-01-05 12:11:31 -08001696 EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
Winson42329522016-02-05 10:39:46 -08001697
1698 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
1699 event.task.key.getComponent().toString());
Winsonef064132016-01-05 12:11:31 -08001700 }
1701
1702 public final void onBusEvent(FocusNextTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001703 // Stop any scrolling
1704 mStackScroller.stopScroller();
1705 mStackScroller.stopBoundScrollAnimation();
1706
Peter Schillerb124d562015-12-11 21:31:17 -08001707 setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
Winson4b9cded2016-01-26 16:26:47 -08001708 event.timerIndicatorDuration);
Winsonef064132016-01-05 12:11:31 -08001709 }
1710
1711 public final void onBusEvent(FocusPreviousTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001712 // Stop any scrolling
1713 mStackScroller.stopScroller();
1714 mStackScroller.stopBoundScrollAnimation();
1715
Winsonef064132016-01-05 12:11:31 -08001716 setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
1717 }
1718
Winsone7f138c2015-10-22 16:15:21 -07001719 public final void onBusEvent(UserInteractionEvent event) {
1720 // Poke the doze trigger on user interaction
1721 mUIDozeTrigger.poke();
Winson4b9cded2016-01-26 16:26:47 -08001722
1723 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
1724 if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
Winsonc5ef63f2016-01-21 14:39:23 -08001725 TaskView tv = getChildViewForTask(mFocusedTask);
1726 if (tv != null) {
1727 tv.getHeaderView().cancelFocusTimerIndicator();
1728 }
Peter Schillerb124d562015-12-11 21:31:17 -08001729 }
Winsone7f138c2015-10-22 16:15:21 -07001730 }
1731
Winsoneca4ab62015-11-04 10:50:28 -08001732 public final void onBusEvent(DragStartEvent event) {
Winson70f0bf72016-02-01 14:05:29 -08001733 // Ensure that the drag task is not animated
1734 addIgnoreTask(event.task);
1735
Winsoneca4ab62015-11-04 10:50:28 -08001736 if (event.task.isFreeformTask()) {
1737 // Animate to the front of the stack
Winsond9529612016-01-28 13:29:49 -08001738 mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
Winsoneca4ab62015-11-04 10:50:28 -08001739 }
Winsonf24f2162016-01-05 12:11:55 -08001740
1741 // Enlarge the dragged view slightly
1742 float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
1743 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
1744 mTmpTransform, null);
1745 mTmpTransform.scale = finalScale;
Winson3e874742016-01-07 10:08:17 -08001746 mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
Winsonf24f2162016-01-05 12:11:55 -08001747 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08001748 new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winsoneca4ab62015-11-04 10:50:28 -08001749 }
1750
1751 public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
1752 SystemServicesProxy ssp = Recents.getSystemServices();
1753 if (ssp.hasFreeformWorkspaceSupport()) {
1754 event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
1755 event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
1756 }
1757 }
1758
1759 public final void onBusEvent(DragDropTargetChangedEvent event) {
Winsonbe8e6962016-02-01 14:27:52 -08001760 AnimationProps animation = new AnimationProps(250, Interpolators.FAST_OUT_SLOW_IN);
Winson59924fe2016-03-17 14:13:18 -07001761 boolean ignoreTaskOverrides = false;
Winson3e874742016-01-07 10:08:17 -08001762 if (event.dropTarget instanceof TaskStack.DockState) {
1763 // Calculate the new task stack bounds that matches the window size that Recents will
1764 // have after the drop
1765 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
1766 mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
1767 getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
Winson59924fe2016-03-17 14:13:18 -07001768 mLayoutAlgorithm, getResources(), mWindowRect));
1769 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001770 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1771 updateLayoutAlgorithm(true /* boundScroll */);
Winson59924fe2016-03-17 14:13:18 -07001772 ignoreTaskOverrides = true;
Winson3e874742016-01-07 10:08:17 -08001773 } else {
Winson8aa99592016-01-19 15:07:07 -08001774 // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
1775 // task view, so add it back to the ignore set after updating the layout
Winson59924fe2016-03-17 14:13:18 -07001776 mWindowRect.set(mStableWindowRect);
Winson3e874742016-01-07 10:08:17 -08001777 mStackBounds.set(mStableStackBounds);
Winson8aa99592016-01-19 15:07:07 -08001778 removeIgnoreTask(event.task);
Winson59924fe2016-03-17 14:13:18 -07001779 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001780 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1781 updateLayoutAlgorithm(true /* boundScroll */);
1782 addIgnoreTask(event.task);
Winson3e874742016-01-07 10:08:17 -08001783 }
Winson59924fe2016-03-17 14:13:18 -07001784 relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides);
Winsoneca4ab62015-11-04 10:50:28 -08001785 }
1786
1787 public final void onBusEvent(final DragEndEvent event) {
Winson479f7442015-11-25 15:16:27 -08001788 // We don't handle drops on the dock regions
1789 if (event.dropTarget instanceof TaskStack.DockState) {
Winson59924fe2016-03-17 14:13:18 -07001790 // However, we do need to reset the overrides, since the last state of this task stack
1791 // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
1792 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winsoneca4ab62015-11-04 10:50:28 -08001793 return;
1794 }
1795
Winson479f7442015-11-25 15:16:27 -08001796 boolean isFreeformTask = event.task.isFreeformTask();
1797 boolean hasChangedStacks =
1798 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
1799 (isFreeformTask && event.dropTarget == mStackDropTarget);
Winson479f7442015-11-25 15:16:27 -08001800
Winson5b7dd532015-12-01 16:02:12 -08001801 if (hasChangedStacks) {
Winson479f7442015-11-25 15:16:27 -08001802 // Move the task to the right position in the stack (ie. the front of the stack if
Winson8aa99592016-01-19 15:07:07 -08001803 // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
Winson479f7442015-11-25 15:16:27 -08001804 // before we update their stack ids, otherwise, the keys will have changed.
1805 if (event.dropTarget == mFreeformWorkspaceDropTarget) {
1806 mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001807 } else if (event.dropTarget == mStackDropTarget) {
1808 mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001809 }
Winson8aa99592016-01-19 15:07:07 -08001810 updateLayoutAlgorithm(true /* boundScroll */);
Winson479f7442015-11-25 15:16:27 -08001811
1812 // Move the task to the new stack in the system after the animation completes
Winson Chungaaeaac12015-12-16 16:49:36 -05001813 event.addPostAnimationCallback(new Runnable() {
Winson479f7442015-11-25 15:16:27 -08001814 @Override
1815 public void run() {
1816 SystemServicesProxy ssp = Recents.getSystemServices();
1817 ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
1818 }
1819 });
Winsoneca4ab62015-11-04 10:50:28 -08001820 }
Winsoneca4ab62015-11-04 10:50:28 -08001821
Winsonbb410952015-12-04 14:34:11 -08001822 // We translated the view but we need to animate it back from the current layout-space rect
1823 // to its final layout-space rect
1824 int x = (int) event.taskView.getTranslationX();
1825 int y = (int) event.taskView.getTranslationY();
1826 Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(),
1827 event.taskView.getRight(), event.taskView.getBottom());
1828 taskViewRect.offset(x, y);
1829 event.taskView.setTranslationX(0);
1830 event.taskView.setTranslationY(0);
1831 event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
1832 taskViewRect.right, taskViewRect.bottom);
1833
Winson88737542016-02-17 13:27:33 -08001834 // Animate the non-drag TaskViews back into position
Winsonf24f2162016-01-05 12:11:55 -08001835 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
1836 mTmpTransform, null);
1837 event.getAnimationTrigger().increment();
Winsonbe8e6962016-02-01 14:27:52 -08001838 relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winsonc0d70582016-01-29 10:24:39 -08001839 Interpolators.FAST_OUT_SLOW_IN));
Winson88737542016-02-17 13:27:33 -08001840
1841 // Animate the drag TaskView back into position
Winsonf24f2162016-01-05 12:11:55 -08001842 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08001843 new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
Winsonf24f2162016-01-05 12:11:55 -08001844 event.getAnimationTrigger().decrementOnAnimationEnd()));
Winson8aa99592016-01-19 15:07:07 -08001845 removeIgnoreTask(event.task);
Winsoneca4ab62015-11-04 10:50:28 -08001846 }
1847
Winson8b1871d2015-11-20 09:56:20 -08001848 public final void onBusEvent(IterateRecentsEvent event) {
Winson Chungd6b78a32015-12-15 10:22:45 -05001849 if (!mEnterAnimationComplete) {
1850 // Cancel the previous task's window transition before animating the focused state
1851 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1852 }
Winson8b1871d2015-11-20 09:56:20 -08001853 }
1854
Winsone5f1faa2015-11-20 12:26:23 -08001855 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
1856 mEnterAnimationComplete = true;
Winsonef064132016-01-05 12:11:31 -08001857
Winson4b057c62016-01-12 15:01:52 -08001858 if (mStack.getTaskCount() > 0) {
Winsonef064132016-01-05 12:11:31 -08001859 // Start the task enter animations
Winsonf24f2162016-01-05 12:11:55 -08001860 mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001861
1862 // Add a runnable to the post animation ref counter to clear all the views
1863 event.addPostAnimationCallback(new Runnable() {
1864 @Override
1865 public void run() {
1866 // Start the dozer to trigger to trigger any UI that shows after a timeout
1867 mUIDozeTrigger.startDozing();
1868
1869 // Update the focused state here -- since we only set the focused task without
1870 // requesting view focus in onFirstLayout(), actually request view focus and
1871 // animate the focused state if we are alt-tabbing now, after the window enter
1872 // animation is completed
1873 if (mFocusedTask != null) {
1874 RecentsConfiguration config = Recents.getConfiguration();
1875 RecentsActivityLaunchState launchState = config.getLaunchState();
1876 setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
1877 false /* scrollToTask */, launchState.launchedWithAltTab);
1878 }
Winson4b9cded2016-01-26 16:26:47 -08001879
1880 EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent());
Winsonef064132016-01-05 12:11:31 -08001881 }
1882 });
1883 }
Winsone5f1faa2015-11-20 12:26:23 -08001884 }
1885
Winsonb1e71d02015-11-23 12:40:23 -08001886 public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
1887 List<TaskView> taskViews = getTaskViews();
1888 int taskViewCount = taskViews.size();
1889 for (int i = 0; i < taskViewCount; i++) {
1890 TaskView tv = taskViews.get(i);
1891 Task task = tv.getTask();
Winson Chung296278a2015-12-17 12:09:02 -05001892 if (task.isFreeformTask()) {
Winsonb1e71d02015-11-23 12:40:23 -08001893 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
1894 }
1895 }
1896 }
1897
Winson88737542016-02-17 13:27:33 -08001898 public final void onBusEvent(MultiWindowStateChangedEvent event) {
Winson931845f2016-02-24 19:38:41 -08001899 if (!event.inMultiWindow) {
Winson88737542016-02-17 13:27:33 -08001900 // Scroll the stack to the front to see the undocked task
1901 mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
Winson931845f2016-02-24 19:38:41 -08001902 @Override
1903 public void run() {
Winson88737542016-02-17 13:27:33 -08001904 List<TaskView> taskViews = getTaskViews();
1905 int taskViewCount = taskViews.size();
1906 for (int i = 0; i < taskViewCount; i++) {
1907 TaskView tv = taskViews.get(i);
1908 tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
1909 tv.mIsDisabledInSafeMode);
1910 }
Winson931845f2016-02-24 19:38:41 -08001911 }
1912 });
1913 }
Winsond9529612016-01-28 13:29:49 -08001914 }
1915
Winsone693aaf2016-03-01 12:05:59 -08001916 public final void onBusEvent(ConfigurationChangedEvent event) {
Winsonf9357d92016-03-25 15:14:37 -07001917 mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winsone693aaf2016-03-01 12:05:59 -08001918 mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winson59924fe2016-03-17 14:13:18 -07001919 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winsone693aaf2016-03-01 12:05:59 -08001920 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1921 }
1922
Winson0d14d4d2015-10-26 17:05:04 -07001923 /**
1924 * Removes the task from the stack, and updates the focus to the next task in the stack if the
1925 * removed TaskView was focused.
1926 */
Winson8aa99592016-01-19 15:07:07 -08001927 private void removeTaskViewFromStack(TaskView tv, Task task) {
Winson0d14d4d2015-10-26 17:05:04 -07001928 // Announce for accessibility
1929 tv.announceForAccessibility(getContext().getString(
Winson8aa99592016-01-19 15:07:07 -08001930 R.string.accessibility_recents_item_dismissed, task.title));
Winson0d14d4d2015-10-26 17:05:04 -07001931
1932 // Remove the task from the stack
Winsonbe8e6962016-02-01 14:27:52 -08001933 mStack.removeTask(task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winson20684082016-03-16 17:13:34 -07001934 Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */);
Winson0d14d4d2015-10-26 17:05:04 -07001935 }
Winsona78a8f32015-12-03 10:55:01 -08001936
1937 /**
1938 * Starts an alpha animation on the freeform workspace background.
1939 */
Winsonbe8e6962016-02-01 14:27:52 -08001940 private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
1941 AnimationProps animation) {
Winsona78a8f32015-12-03 10:55:01 -08001942 if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
1943 return;
1944 }
1945
1946 Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
1947 mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
Winson3e874742016-01-07 10:08:17 -08001948 Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
Winsonbe8e6962016-02-01 14:27:52 -08001949 mFreeformWorkspaceBackgroundAnimator.setStartDelay(
1950 animation.getDuration(AnimationProps.ALPHA));
1951 mFreeformWorkspaceBackgroundAnimator.setDuration(
1952 animation.getDuration(AnimationProps.ALPHA));
1953 mFreeformWorkspaceBackgroundAnimator.setInterpolator(
1954 animation.getInterpolator(AnimationProps.ALPHA));
Winsona78a8f32015-12-03 10:55:01 -08001955 mFreeformWorkspaceBackgroundAnimator.start();
1956 }
1957
1958 /**
Winson8aa99592016-01-19 15:07:07 -08001959 * Returns the insert index for the task in the current set of task views. If the given task
Winsona78a8f32015-12-03 10:55:01 -08001960 * is already in the task view list, then this method returns the insert index assuming it
1961 * is first removed at the previous index.
1962 *
1963 * @param task the task we are finding the index for
1964 * @param taskIndex the index of the task in the stack
1965 */
1966 private int findTaskViewInsertIndex(Task task, int taskIndex) {
1967 if (taskIndex != -1) {
1968 List<TaskView> taskViews = getTaskViews();
1969 boolean foundTaskView = false;
1970 int taskViewCount = taskViews.size();
1971 for (int i = 0; i < taskViewCount; i++) {
1972 Task tvTask = taskViews.get(i).getTask();
1973 if (tvTask == task) {
1974 foundTaskView = true;
1975 } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
1976 if (foundTaskView) {
1977 return i - 1;
1978 } else {
1979 return i;
1980 }
1981 }
1982 }
1983 }
1984 return -1;
1985 }
Winson Chungde750de2015-12-11 10:26:06 -05001986
1987 /**
Winson6ea25882016-01-13 10:59:04 -08001988 * Reads current system flags related to accessibility and screen pinning.
1989 */
1990 private void readSystemFlags() {
1991 SystemServicesProxy ssp = Recents.getSystemServices();
1992 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
1993 mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
1994 Settings.System.LOCK_TO_APP_ENABLED) != 0;
1995 }
Winson Chunga4cc9662014-07-25 12:10:38 -07001996}