blob: 21780a6e88641294dc9a342b80f6aa16298c23af [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;
Winson619e40c2016-03-25 16:12:35 -070025import android.annotation.IntDef;
Winson Chung9f49df92014-05-07 18:08:34 -070026import android.content.ComponentName;
Winson Chung303e1ff2014-03-07 15:06:19 -080027import android.content.Context;
Winsonfc48b072016-04-21 11:20:11 -070028import android.content.res.Configuration;
Winsonbb410952015-12-04 14:34:11 -080029import android.content.res.Resources;
Jorim Jaggi900fb482015-06-02 15:07:33 -070030import android.graphics.Canvas;
Winson Chung303e1ff2014-03-07 15:06:19 -080031import android.graphics.Rect;
Winsona78a8f32015-12-03 10:55:01 -080032import android.graphics.drawable.Drawable;
Winsonde0591a2015-12-04 17:24:35 -080033import android.graphics.drawable.GradientDrawable;
Winson Chung83ea6f72015-06-17 13:00:23 -070034import android.os.Bundle;
Winson Chung931c51f2015-12-17 17:08:55 -050035import android.provider.Settings;
Winson55003902016-01-12 12:00:37 -080036import android.util.ArrayMap;
37import android.util.ArraySet;
Winson8aa99592016-01-19 15:07:07 -080038import android.util.MutableBoolean;
Winson Chung37c8d8e2014-03-24 14:53:07 -070039import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080040import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080041import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080042import android.view.ViewDebug;
Winson70f0bf72016-02-01 14:05:29 -080043import android.view.ViewGroup;
Winson Chungee445952014-09-09 16:12:59 +020044import android.view.accessibility.AccessibilityEvent;
Winson Chung83ea6f72015-06-17 13:00:23 -070045import android.view.accessibility.AccessibilityNodeInfo;
Winson Chung303e1ff2014-03-07 15:06:19 -080046import android.widget.FrameLayout;
Winsonf0009882016-06-01 12:22:55 -070047import android.widget.ScrollView;
Winsonc0d70582016-01-29 10:24:39 -080048
Winson42329522016-02-05 10:39:46 -080049import com.android.internal.logging.MetricsLogger;
50import com.android.internal.logging.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080051import com.android.systemui.Interpolators;
Winson Chungc6a16232014-04-01 14:04:48 -070052import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070053import com.android.systemui.recents.Recents;
Winsone6c90732015-09-24 16:06:29 -070054import com.android.systemui.recents.RecentsActivity;
Winson2536c7e2015-10-01 15:49:31 -070055import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chung303e1ff2014-03-07 15:06:19 -080056import com.android.systemui.recents.RecentsConfiguration;
Winson4b9cded2016-01-26 16:26:47 -080057import com.android.systemui.recents.RecentsDebugFlags;
Winsone6c90732015-09-24 16:06:29 -070058import com.android.systemui.recents.events.EventBus;
Winsone5f1faa2015-11-20 12:26:23 -080059import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
Winsone693aaf2016-03-01 12:05:59 -080060import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
Winsonef064132016-01-05 12:11:31 -080061import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
Winson4b9cded2016-01-26 16:26:47 -080062import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
Winsone5f1faa2015-11-20 12:26:23 -080063import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
Winsonbc0f8cd2016-03-15 15:44:48 -070064import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winsonc69249f2016-03-28 13:38:39 -070065import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
Winson8b1871d2015-11-20 09:56:20 -080066import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Winsonb61e6542016-02-04 14:37:18 -080067import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Winson Chung48f2cda2015-12-11 13:20:12 -050068import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winsonef064132016-01-05 12:11:31 -080069import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
Winson88737542016-02-17 13:27:33 -080070import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
Winsone6c90732015-09-24 16:06:29 -070071import com.android.systemui.recents.events.activity.PackagesChangedEvent;
Winson8f6ee482016-03-18 17:51:48 -070072import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
Winson397ae742015-11-20 11:27:33 -080073import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
Winsonef064132016-01-05 12:11:31 -080074import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
Winson3b6ba1a2016-03-22 15:37:54 -070075import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070076import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070077import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
Winsonef064132016-01-05 12:11:31 -080078import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winsonb1e71d02015-11-23 12:40:23 -080079import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
Winsone7f138c2015-10-22 16:15:21 -070080import com.android.systemui.recents.events.ui.UserInteractionEvent;
Winsoneca4ab62015-11-04 10:50:28 -080081import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
82import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
Winson27c28f82016-05-05 16:16:50 -070083import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
Winsoneca4ab62015-11-04 10:50:28 -080084import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
85import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070086import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
87import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
88import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
Winson Chungffa2ec62014-07-03 15:54:42 -070089import com.android.systemui.recents.misc.DozeTrigger;
Winson Chungee445952014-09-09 16:12:59 +020090import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungbf5dbf12014-09-16 00:58:25 +020091import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080092import com.android.systemui.recents.model.Task;
93import com.android.systemui.recents.model.TaskStack;
Winson Chung303e1ff2014-03-07 15:06:19 -080094
Winsond72c3152016-04-05 15:33:35 -070095import java.io.PrintWriter;
Winson619e40c2016-03-25 16:12:35 -070096import java.lang.annotation.Retention;
97import java.lang.annotation.RetentionPolicy;
Winson Chung303e1ff2014-03-07 15:06:19 -080098import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -080099import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -0800100
Winson Chung303e1ff2014-03-07 15:06:19 -0800101
102/* The visual representation of a task stack view */
Winson Chung04dfe0d2014-03-14 14:06:29 -0700103public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
Winson Chung012ef362014-07-31 18:36:25 -0700104 TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
Winson1c846142016-01-22 11:34:38 -0800105 TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
Winsone6c90732015-09-24 16:06:29 -0700106 ViewPool.ViewPoolConsumer<TaskView, Task> {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700107
Winsond72c3152016-04-05 15:33:35 -0700108 private static final String TAG = "TaskStackView";
109
Winson8f6ee482016-03-18 17:51:48 -0700110 // The thresholds at which to show/hide the stack action button.
111 private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
112 private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
Winsonc29ff002015-11-20 16:00:45 -0800113
Winson8aa99592016-01-19 15:07:07 -0800114 public static final int DEFAULT_SYNC_STACK_DURATION = 200;
Winson3f32e7e2016-04-20 17:18:08 -0700115 public static final int SLOW_SYNC_STACK_DURATION = 250;
Winsonf24f2162016-01-05 12:11:55 -0800116 private static final int DRAG_SCALE_DURATION = 175;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100117 static final float DRAG_SCALE_FACTOR = 1.05f;
Winson Chung06266772015-12-11 10:24:21 -0500118
Winsonf8597b22016-03-23 18:44:26 -0700119 private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
Winson96e61342016-03-15 16:47:19 -0700120 private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
121
Winson619e40c2016-03-25 16:12:35 -0700122 // The actions to perform when resetting to initial state,
123 @Retention(RetentionPolicy.SOURCE)
124 @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
125 public @interface InitialStateAction {}
126 /** Do not update the stack and layout to the initial state. */
127 private static final int INITIAL_STATE_UPDATE_NONE = 0;
128 /** Update both the stack and layout to the initial state. */
129 private static final int INITIAL_STATE_UPDATE_ALL = 1;
130 /** Update only the layout to the initial state. */
131 private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
132
Winsonfc48b072016-04-21 11:20:11 -0700133 private LayoutInflater mInflater;
134 private TaskStack mStack = new TaskStack();
Winson231bc9c2016-02-09 12:31:00 -0800135 @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
Winson36a5a2c2015-10-29 18:04:39 -0700136 TaskStackLayoutAlgorithm mLayoutAlgorithm;
Winsonf9357d92016-03-25 15:14:37 -0700137 // The stable layout algorithm is only used to calculate the task rect with the stable bounds
Winsonfc48b072016-04-21 11:20:11 -0700138 private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
Winson231bc9c2016-02-09 12:31:00 -0800139 @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
Winsonfc48b072016-04-21 11:20:11 -0700140 private TaskStackViewScroller mStackScroller;
Winson231bc9c2016-02-09 12:31:00 -0800141 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
Winsonfc48b072016-04-21 11:20:11 -0700142 private TaskStackViewTouchHandler mTouchHandler;
143 private TaskStackAnimationHelper mAnimationHelper;
144 private GradientDrawable mFreeformWorkspaceBackground;
145 private ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
146 private ViewPool<TaskView, Task> mViewPool;
Winsonf24f2162016-01-05 12:11:55 -0800147
Winsonfc48b072016-04-21 11:20:11 -0700148 private ArrayList<TaskView> mTaskViews = new ArrayList<>();
149 private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
150 private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
151 private AnimationProps mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800152
Winson231bc9c2016-02-09 12:31:00 -0800153 @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
Winsonfc48b072016-04-21 11:20:11 -0700154 private DozeTrigger mUIDozeTrigger;
Winson231bc9c2016-02-09 12:31:00 -0800155 @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Winsonfc48b072016-04-21 11:20:11 -0700156 private Task mFocusedTask;
Winsonf24f2162016-01-05 12:11:55 -0800157
Winsonfc48b072016-04-21 11:20:11 -0700158 private int mTaskCornerRadiusPx;
Winson3e874742016-01-07 10:08:17 -0800159 private int mDividerSize;
Winson4b9cded2016-01-26 16:26:47 -0800160 private int mStartTimerIndicatorDuration;
Winsonf24f2162016-01-05 12:11:55 -0800161
Winson231bc9c2016-02-09 12:31:00 -0800162 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700163 private boolean mTaskViewsClipDirty = true;
Winson231bc9c2016-02-09 12:31:00 -0800164 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700165 private boolean mAwaitingFirstLayout = true;
Winson231bc9c2016-02-09 12:31:00 -0800166 @ViewDebug.ExportedProperty(category="recents")
Winson619e40c2016-03-25 16:12:35 -0700167 @InitialStateAction
Winsonfc48b072016-04-21 11:20:11 -0700168 private int mInitialState = INITIAL_STATE_UPDATE_ALL;
Winson619e40c2016-03-25 16:12:35 -0700169 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700170 private boolean mInMeasureLayout = false;
Winson231bc9c2016-02-09 12:31:00 -0800171 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700172 private boolean mEnterAnimationComplete = false;
Winson231bc9c2016-02-09 12:31:00 -0800173 @ViewDebug.ExportedProperty(category="recents")
Winsonf0009882016-06-01 12:22:55 -0700174 boolean mTouchExplorationEnabled;
Winson231bc9c2016-02-09 12:31:00 -0800175 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800176 boolean mScreenPinningEnabled;
Winson3150e572015-10-23 15:07:24 -0700177
Winson3e874742016-01-07 10:08:17 -0800178 // The stable stack bounds are the full bounds that we were measured with from RecentsView
Winson231bc9c2016-02-09 12:31:00 -0800179 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800180 private Rect mStableStackBounds = new Rect();
Winson3e874742016-01-07 10:08:17 -0800181 // The current stack bounds are dynamic and may change as the user drags and drops
Winson231bc9c2016-02-09 12:31:00 -0800182 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800183 private Rect mStackBounds = new Rect();
Winson59924fe2016-03-17 14:13:18 -0700184 // The current window bounds at the point we were measured
185 @ViewDebug.ExportedProperty(category="recents")
186 private Rect mStableWindowRect = new Rect();
187 // The current window bounds are dynamic and may change as the user drags and drops
188 @ViewDebug.ExportedProperty(category="recents")
189 private Rect mWindowRect = new Rect();
Winsonfc48b072016-04-21 11:20:11 -0700190 // The current display bounds
191 @ViewDebug.ExportedProperty(category="recents")
192 private Rect mDisplayRect = new Rect();
193 // The current display orientation
194 @ViewDebug.ExportedProperty(category="recents")
195 private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
Winsond9529612016-01-28 13:29:49 -0800196
Winson05e46ca2016-02-05 15:40:29 -0800197 private Rect mTmpRect = new Rect();
198 private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
199 private List<TaskView> mTmpTaskViews = new ArrayList<>();
200 private TaskViewTransform mTmpTransform = new TaskViewTransform();
Winsonc4387022016-02-24 12:05:26 -0800201 private int[] mTmpIntPair = new int[2];
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700202 private boolean mResetToInitialStateWhenResized;
203 private int mLastWidth;
204 private int mLastHeight;
Winson Chung931c51f2015-12-17 17:08:55 -0500205
Winson Chungbf5dbf12014-09-16 00:58:25 +0200206 // A convenience update listener to request updating clipping of tasks
Winsoneca4ab62015-11-04 10:50:28 -0800207 private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
Winson Chungbf5dbf12014-09-16 00:58:25 +0200208 new ValueAnimator.AnimatorUpdateListener() {
Winson5b7dd532015-12-01 16:02:12 -0800209 @Override
210 public void onAnimationUpdate(ValueAnimator animation) {
Winson55003902016-01-12 12:00:37 -0800211 if (!mTaskViewsClipDirty) {
212 mTaskViewsClipDirty = true;
213 invalidate();
214 }
Winson5b7dd532015-12-01 16:02:12 -0800215 }
216 };
Winson Chungbf5dbf12014-09-16 00:58:25 +0200217
Winsoneca4ab62015-11-04 10:50:28 -0800218 // The drop targets for a task drag
219 private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
220 @Override
Winson3e874742016-01-07 10:08:17 -0800221 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
222 // This drop target has a fixed bounds and should be checked last, so just fall through
223 // if it is the current target
224 if (!isCurrentTarget) {
225 return mLayoutAlgorithm.mFreeformRect.contains(x, y);
226 }
227 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800228 }
229 };
230
231 private DropTarget mStackDropTarget = new DropTarget() {
232 @Override
Winson3e874742016-01-07 10:08:17 -0800233 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
234 // This drop target has a fixed bounds and should be checked last, so just fall through
235 // if it is the current target
236 if (!isCurrentTarget) {
237 return mLayoutAlgorithm.mStackRect.contains(x, y);
238 }
239 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800240 }
241 };
242
Winson88737542016-02-17 13:27:33 -0800243 public TaskStackView(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800244 super(context);
Winsonde0591a2015-12-04 17:24:35 -0800245 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonbb410952015-12-04 14:34:11 -0800246 Resources res = context.getResources();
247
Winson Chungb0a28ea2014-10-28 15:21:35 -0700248 // Set the stack first
Winson88737542016-02-17 13:27:33 -0800249 mStack.setCallbacks(this);
Winson35f30502015-09-28 11:24:36 -0700250 mViewPool = new ViewPool<>(context, this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700251 mInflater = LayoutInflater.from(context);
Winson1c846142016-01-22 11:34:38 -0800252 mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
Winsonf9357d92016-03-25 15:14:37 -0700253 mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
Winson1c846142016-01-22 11:34:38 -0800254 mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
Winson35f30502015-09-28 11:24:36 -0700255 mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
Winsonf24f2162016-01-05 12:11:55 -0800256 mAnimationHelper = new TaskStackAnimationHelper(context, this);
Winsonbb410952015-12-04 14:34:11 -0800257 mTaskCornerRadiusPx = res.getDimensionPixelSize(
258 R.dimen.recents_task_view_rounded_corners_radius);
Winson3e874742016-01-07 10:08:17 -0800259 mDividerSize = ssp.getDockedDividerSize(context);
Winsonfc48b072016-04-21 11:20:11 -0700260 mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
261 mDisplayRect = ssp.getDisplayRect();
Winson35f30502015-09-28 11:24:36 -0700262
263 int taskBarDismissDozeDelaySeconds = getResources().getInteger(
264 R.integer.recents_task_bar_dismiss_delay_seconds);
265 mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
Winson Chunga26fb782014-06-12 17:52:39 -0700266 @Override
267 public void run() {
268 // Show the task bar dismiss buttons
Winson Chung6ac8bd62015-01-07 16:38:35 -0800269 List<TaskView> taskViews = getTaskViews();
270 int taskViewCount = taskViews.size();
271 for (int i = 0; i < taskViewCount; i++) {
272 TaskView tv = taskViews.get(i);
Winson Chung969f5862014-06-16 17:08:24 -0700273 tv.startNoUserInteractionAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700274 }
275 }
276 });
Winson Chung83ea6f72015-06-17 13:00:23 -0700277 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Winson36a5a2c2015-10-29 18:04:39 -0700278
Winsonde0591a2015-12-04 17:24:35 -0800279 mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
280 R.drawable.recents_freeform_workspace_bg);
Winsona78a8f32015-12-03 10:55:01 -0800281 mFreeformWorkspaceBackground.setCallback(this);
Winsonde0591a2015-12-04 17:24:35 -0800282 if (ssp.hasFreeformWorkspaceSupport()) {
Winson Chungaa4f8002015-12-17 10:27:55 -0500283 mFreeformWorkspaceBackground.setColor(
284 getContext().getColor(R.color.recents_freeform_workspace_bg_color));
Winsonde0591a2015-12-04 17:24:35 -0800285 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800286 }
287
Winsona1ededd2016-03-25 12:23:12 -0700288 @Override
289 protected void onAttachedToWindow() {
290 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
291 super.onAttachedToWindow();
292 readSystemFlags();
293 }
294
295 @Override
296 protected void onDetachedFromWindow() {
297 super.onDetachedFromWindow();
298 EventBus.getDefault().unregister(this);
299 }
300
Winson88737542016-02-17 13:27:33 -0800301 /**
Winsona1ededd2016-03-25 12:23:12 -0700302 * Called from RecentsActivity when it is relaunched.
Winson88737542016-02-17 13:27:33 -0800303 */
Winsona1ededd2016-03-25 12:23:12 -0700304 void onReload(boolean isResumingFromVisible) {
Winson88737542016-02-17 13:27:33 -0800305 if (!isResumingFromVisible) {
306 // Reset the focused task
307 resetFocusedTask(getFocusedTask());
308 }
309
310 // Reset the state of each of the task views
311 List<TaskView> taskViews = new ArrayList<>();
312 taskViews.addAll(getTaskViews());
313 taskViews.addAll(mViewPool.getViews());
314 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsona1ededd2016-03-25 12:23:12 -0700315 taskViews.get(i).onReload(isResumingFromVisible);
Winson88737542016-02-17 13:27:33 -0800316 }
317
318 // Reset the stack state
319 readSystemFlags();
320 mTaskViewsClipDirty = true;
321 mEnterAnimationComplete = false;
322 mUIDozeTrigger.stopDozing();
323 if (isResumingFromVisible) {
324 // Animate in the freeform workspace
325 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
326 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
327 Interpolators.FAST_OUT_SLOW_IN));
328 } else {
329 mStackScroller.reset();
Winsonf9357d92016-03-25 15:14:37 -0700330 mStableLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800331 mLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800332 }
Winson88737542016-02-17 13:27:33 -0800333
Winsona1ededd2016-03-25 12:23:12 -0700334 // Since we always animate to the same place in (the initial state), always reset the stack
335 // to the initial state when resuming
336 mAwaitingFirstLayout = true;
Winson619e40c2016-03-25 16:12:35 -0700337 mInitialState = INITIAL_STATE_UPDATE_ALL;
Winsona1ededd2016-03-25 12:23:12 -0700338 requestLayout();
Winsone6c90732015-09-24 16:06:29 -0700339 }
340
Winson88737542016-02-17 13:27:33 -0800341 /**
342 * Sets the stack tasks of this TaskStackView from the given TaskStack.
343 */
Winsona1ededd2016-03-25 12:23:12 -0700344 public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
Winson88737542016-02-17 13:27:33 -0800345 boolean isInitialized = mLayoutAlgorithm.isInitialized();
Winsond2a03062016-04-15 11:19:07 -0700346
Winsona1ededd2016-03-25 12:23:12 -0700347 // Only notify if we are already initialized, otherwise, everything will pick up all the
348 // new and old tasks when we next layout
Winson88737542016-02-17 13:27:33 -0800349 mStack.setTasks(getContext(), stack.computeAllTasksList(),
Winsona1ededd2016-03-25 12:23:12 -0700350 allowNotifyStackChanges && isInitialized);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700351 }
352
Winson Chungd16c5652015-01-26 16:11:07 -0800353 /** Returns the task stack. */
Winsone693aaf2016-03-01 12:05:59 -0800354 public TaskStack getStack() {
Winson Chungd16c5652015-01-26 16:11:07 -0800355 return mStack;
356 }
357
Winsone693aaf2016-03-01 12:05:59 -0800358 /**
359 * Updates this TaskStackView to the initial state.
360 */
Winson67c79572016-04-13 14:02:18 -0700361 public void updateToInitialState() {
362 mStackScroller.setStackScrollToInitialState();
Winsond2a03062016-04-15 11:19:07 -0700363 mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
Winsone693aaf2016-03-01 12:05:59 -0800364 }
365
Winson Chung6ac8bd62015-01-07 16:38:35 -0800366 /** Updates the list of task views */
367 void updateTaskViewsList() {
368 mTaskViews.clear();
369 int childCount = getChildCount();
370 for (int i = 0; i < childCount; i++) {
371 View v = getChildAt(i);
372 if (v instanceof TaskView) {
373 mTaskViews.add((TaskView) v);
374 }
375 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800376 }
377
378 /** Gets the list of task views */
379 List<TaskView> getTaskViews() {
Winsonf24f2162016-01-05 12:11:55 -0800380 return mTaskViews;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800381 }
382
Winson1b585612015-11-06 09:16:26 -0800383 /**
384 * Returns the front most task view.
385 *
386 * @param stackTasksOnly if set, will return the front most task view in the stack (by default
387 * the front most task view will be freeform since they are placed above
388 * stack tasks)
389 */
390 private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
391 List<TaskView> taskViews = getTaskViews();
392 int taskViewCount = taskViews.size();
393 for (int i = taskViewCount - 1; i >= 0; i--) {
394 TaskView tv = taskViews.get(i);
395 Task task = tv.getTask();
396 if (stackTasksOnly && task.isFreeformTask()) {
397 continue;
398 }
399 return tv;
400 }
401 return null;
402 }
403
404 /**
405 * Finds the child view given a specific {@param task}.
406 */
407 public TaskView getChildViewForTask(Task t) {
408 List<TaskView> taskViews = getTaskViews();
409 int taskViewCount = taskViews.size();
410 for (int i = 0; i < taskViewCount; i++) {
411 TaskView tv = taskViews.get(i);
412 if (tv.getTask() == t) {
413 return tv;
414 }
415 }
416 return null;
417 }
418
Winson Chungd7b2cb12014-06-26 15:08:50 -0700419 /** Returns the stack algorithm for this task stack. */
Winson36a5a2c2015-10-29 18:04:39 -0700420 public TaskStackLayoutAlgorithm getStackAlgorithm() {
Winson Chung012ef362014-07-31 18:36:25 -0700421 return mLayoutAlgorithm;
Winson Chung303e1ff2014-03-07 15:06:19 -0800422 }
423
Winson Chungc6a16232014-04-01 14:04:48 -0700424 /**
Winsonaa0dea72016-04-28 10:59:30 -0700425 * Returns the touch handler for this task stack.
426 */
427 public TaskStackViewTouchHandler getTouchHandler() {
428 return mTouchHandler;
429 }
430
431 /**
Winson8aa99592016-01-19 15:07:07 -0800432 * Adds a task to the ignored set.
Winson Chungc6a16232014-04-01 14:04:48 -0700433 */
Winson8aa99592016-01-19 15:07:07 -0800434 void addIgnoreTask(Task task) {
435 mIgnoreTasks.add(task.key);
436 }
437
438 /**
439 * Removes a task from the ignored set.
440 */
441 void removeIgnoreTask(Task task) {
442 mIgnoreTasks.remove(task.key);
443 }
444
445 /**
446 * Returns whether the specified {@param task} is ignored.
447 */
448 boolean isIgnoredTask(Task task) {
449 return mIgnoreTasks.contains(task.key);
450 }
451
452 /**
453 * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
454 * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
455 * visible range includes all tasks at the target stack scroll. This is useful for ensure that
456 * all views necessary for a transition or animation will be visible at the start.
457 *
458 * This call ignores freeform tasks.
459 *
460 * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
461 * match the size of {@param tasks}
462 * @param tasks The set of tasks for which to generate transforms
463 * @param curStackScroll The current stack scroll
464 * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
465 * The range of the union of the visible views at the current and
466 * target stack scrolls will be returned.
467 * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
468 * Transforms will still be calculated for the ignore tasks.
Winsonc4387022016-02-24 12:05:26 -0800469 * @return the front and back most visible task indices (there may be non visible tasks in
470 * between this range)
Winson8aa99592016-01-19 15:07:07 -0800471 */
Winsonc4387022016-02-24 12:05:26 -0800472 int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
Winson8aa99592016-01-19 15:07:07 -0800473 ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800474 ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
Winson Chungc6a16232014-04-01 14:04:48 -0700475 int taskCount = tasks.size();
Winsonc4387022016-02-24 12:05:26 -0800476 int[] visibleTaskRange = mTmpIntPair;
477 visibleTaskRange[0] = -1;
478 visibleTaskRange[1] = -1;
Winson8aa99592016-01-19 15:07:07 -0800479 boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
Winson Chungc9567c02014-06-16 20:25:51 -0700480
481 // We can reuse the task transforms where possible to reduce object allocation
Winsonc5fd3502016-01-18 15:18:37 -0800482 Utilities.matchTaskListSize(tasks, taskTransforms);
Winson Chungc9567c02014-06-16 20:25:51 -0700483
484 // Update the stack transforms
Winson4993c2f2015-11-19 10:06:06 -0800485 TaskViewTransform frontTransform = null;
Winson8aa99592016-01-19 15:07:07 -0800486 TaskViewTransform frontTransformAtTarget = null;
487 TaskViewTransform transform = null;
488 TaskViewTransform transformAtTarget = null;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700489 for (int i = taskCount - 1; i >= 0; i--) {
Winsona5e6b362015-11-02 17:17:20 -0800490 Task task = tasks.get(i);
Winson8aa99592016-01-19 15:07:07 -0800491
492 // Calculate the current and (if necessary) the target transform for the task
493 transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800494 taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800495 if (useTargetStackScroll && !transform.visible) {
496 // If we have a target stack scroll and the task is not currently visible, then we
497 // just update the transform at the new scroll
498 // TODO: Optimize this
499 transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
500 targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
501 if (transformAtTarget.visible) {
502 transform.copyFrom(transformAtTarget);
503 }
Winson3e874742016-01-07 10:08:17 -0800504 }
505
Winson8aa99592016-01-19 15:07:07 -0800506 // For ignore tasks, only calculate the stack transform and skip the calculation of the
507 // visible stack indices
508 if (ignoreTasksSet.contains(task.key)) {
509 continue;
510 }
Winsonf24f2162016-01-05 12:11:55 -0800511
512 // For freeform tasks, only calculate the stack transform and skip the calculation of
513 // the visible stack indices
Winsona5e6b362015-11-02 17:17:20 -0800514 if (task.isFreeformTask()) {
515 continue;
516 }
517
Winson4993c2f2015-11-19 10:06:06 -0800518 frontTransform = transform;
Winson8aa99592016-01-19 15:07:07 -0800519 frontTransformAtTarget = transformAtTarget;
Winsonc4387022016-02-24 12:05:26 -0800520 if (transform.visible) {
521 if (visibleTaskRange[0] < 0) {
522 visibleTaskRange[0] = i;
523 }
524 visibleTaskRange[1] = i;
525 }
Winson Chungc6a16232014-04-01 14:04:48 -0700526 }
Winsonc4387022016-02-24 12:05:26 -0800527 return visibleTaskRange;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700528 }
529
Winsonf24f2162016-01-05 12:11:55 -0800530 /**
Winson8aa99592016-01-19 15:07:07 -0800531 * Binds the visible {@link TaskView}s at the given target scroll.
Winsonf24f2162016-01-05 12:11:55 -0800532 */
Winson8aa99592016-01-19 15:07:07 -0800533 void bindVisibleTaskViews(float targetStackScroll) {
Winsond2a03062016-04-15 11:19:07 -0700534 bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800535 }
536
537 /**
538 * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
539 * current {@link TaskStack}. This call does not continue on to update their position to the
540 * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
541 * be added/removed from the view hierarchy and placed in the correct Z order and initial
542 * position (if not currently on screen).
543 *
544 * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
545 * includes those visible at the current stack scroll, and all at the
546 * target stack scroll.
Winsone693aaf2016-03-01 12:05:59 -0800547 * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
548 * tasks at their non-overridden task progress
Winson8aa99592016-01-19 15:07:07 -0800549 */
Winsond2a03062016-04-15 11:19:07 -0700550 void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800551 // Get all the task transforms
Winsonc4387022016-02-24 12:05:26 -0800552 ArrayList<Task> tasks = mStack.getStackTasks();
553 int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
Winsond2a03062016-04-15 11:19:07 -0700554 mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
Winsone693aaf2016-03-01 12:05:59 -0800555 ignoreTaskOverrides);
Winsonf24f2162016-01-05 12:11:55 -0800556
557 // Return all the invisible children to the pool
Winson55003902016-01-12 12:00:37 -0800558 mTmpTaskViewMap.clear();
Winson8aa99592016-01-19 15:07:07 -0800559 List<TaskView> taskViews = getTaskViews();
560 int lastFocusedTaskIndex = -1;
561 int taskViewCount = taskViews.size();
Winsonf24f2162016-01-05 12:11:55 -0800562 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800563 TaskView tv = taskViews.get(i);
564 Task task = tv.getTask();
Winsonf24f2162016-01-05 12:11:55 -0800565
Winson3e874742016-01-07 10:08:17 -0800566 // Skip ignored tasks
Winsond2a03062016-04-15 11:19:07 -0700567 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800568 continue;
569 }
570
Winson1d5ff7e2016-03-04 11:21:09 -0800571 // It is possible for the set of lingering TaskViews to differ from the stack if the
572 // stack was updated before the relayout. If the task view is no longer in the stack,
573 // then just return it back to the view pool.
574 int taskIndex = mStack.indexOfStackTask(task);
575 TaskViewTransform transform = null;
576 if (taskIndex != -1) {
577 transform = mCurrentTaskTransforms.get(taskIndex);
578 }
579
580 if (task.isFreeformTask() || (transform != null && transform.visible)) {
Winson55003902016-01-12 12:00:37 -0800581 mTmpTaskViewMap.put(task.key, tv);
Winsonf24f2162016-01-05 12:11:55 -0800582 } else {
Winsonf0009882016-06-01 12:22:55 -0700583 if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
Winsonf24f2162016-01-05 12:11:55 -0800584 lastFocusedTaskIndex = taskIndex;
585 resetFocusedTask(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800586 }
Winsonf24f2162016-01-05 12:11:55 -0800587 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -0800588 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800589 }
Winsonf24f2162016-01-05 12:11:55 -0800590
591 // Pick up all the newly visible children
Winsonc4387022016-02-24 12:05:26 -0800592 for (int i = tasks.size() - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800593 Task task = tasks.get(i);
594 TaskViewTransform transform = mCurrentTaskTransforms.get(i);
Winsonf24f2162016-01-05 12:11:55 -0800595
Winson3e874742016-01-07 10:08:17 -0800596 // Skip ignored tasks
Winsond2a03062016-04-15 11:19:07 -0700597 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800598 continue;
599 }
600
Winsonf24f2162016-01-05 12:11:55 -0800601 // Skip the invisible non-freeform stack tasks
Winson05e46ca2016-02-05 15:40:29 -0800602 if (!task.isFreeformTask() && !transform.visible) {
Winsonf24f2162016-01-05 12:11:55 -0800603 continue;
604 }
605
Winson55003902016-01-12 12:00:37 -0800606 TaskView tv = mTmpTaskViewMap.get(task.key);
Winsonf24f2162016-01-05 12:11:55 -0800607 if (tv == null) {
608 tv = mViewPool.pickUpViewFromPool(task, task);
609 if (task.isFreeformTask()) {
Winsonc69249f2016-03-28 13:38:39 -0700610 updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800611 } else {
Winson68088812016-02-12 16:06:04 -0800612 if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
Winsonc69249f2016-03-28 13:38:39 -0700613 updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
614 AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800615 } else {
Winsonc69249f2016-03-28 13:38:39 -0700616 updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
617 AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800618 }
619 }
620 } else {
621 // Reattach it in the right z order
622 final int taskIndex = mStack.indexOfStackTask(task);
623 final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
624 if (insertIndex != getTaskViews().indexOf(tv)){
625 detachViewFromParent(tv);
626 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
627 updateTaskViewsList();
628 }
629 }
630 }
631
632 // Update the focus if the previous focused task was returned to the view pool
633 if (lastFocusedTaskIndex != -1) {
Winsonf0009882016-06-01 12:22:55 -0700634 int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
635 ? visibleTaskRange[1]
636 : visibleTaskRange[0];
637 setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
638 true /* requestViewFocus */);
639 TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
640 if (focusedTaskView != null) {
641 focusedTaskView.requestAccessibilityFocus();
Winsonf24f2162016-01-05 12:11:55 -0800642 }
643 }
644 }
645
646 /**
Winson27c28f82016-05-05 16:16:50 -0700647 * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
Winsonf24f2162016-01-05 12:11:55 -0800648 */
Winson67c79572016-04-13 14:02:18 -0700649 public void relayoutTaskViews(AnimationProps animation) {
Winson27c28f82016-05-05 16:16:50 -0700650 relayoutTaskViews(animation, null /* animationOverrides */,
651 false /* ignoreTaskOverrides */);
Winson59924fe2016-03-17 14:13:18 -0700652 }
653
654 /**
Winson8aa99592016-01-19 15:07:07 -0800655 * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
656 * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
657 * animations that are current running on those task views, and will ensure that the children
Winson27c28f82016-05-05 16:16:50 -0700658 * {@link TaskView}s will match the set of visible tasks in the stack. If a {@link Task} has
659 * an animation provided in {@param animationOverrides}, that will be used instead.
Winson8aa99592016-01-19 15:07:07 -0800660 */
Winson27c28f82016-05-05 16:16:50 -0700661 private void relayoutTaskViews(AnimationProps animation,
662 ArrayMap<Task, AnimationProps> animationOverrides,
663 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800664 // If we had a deferred animation, cancel that
Winsoneca47ef2016-04-21 16:48:50 -0700665 cancelDeferredTaskViewLayoutAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800666
Winson8aa99592016-01-19 15:07:07 -0800667 // Synchronize the current set of TaskViews
Winsond2a03062016-04-15 11:19:07 -0700668 bindVisibleTaskViews(mStackScroller.getStackScroll(),
Winson59924fe2016-03-17 14:13:18 -0700669 ignoreTaskOverrides /* ignoreTaskOverrides */);
Winsonf24f2162016-01-05 12:11:55 -0800670
671 // Animate them to their final transforms with the given animation
672 List<TaskView> taskViews = getTaskViews();
673 int taskViewCount = taskViews.size();
674 for (int i = 0; i < taskViewCount; i++) {
Winson43336942016-03-07 14:52:59 -0800675 TaskView tv = taskViews.get(i);
Winson27c28f82016-05-05 16:16:50 -0700676 Task task = tv.getTask();
677 int taskIndex = mStack.indexOfStackTask(task);
Winson43336942016-03-07 14:52:59 -0800678 TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
Winsonf24f2162016-01-05 12:11:55 -0800679
Winson27c28f82016-05-05 16:16:50 -0700680 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800681 continue;
682 }
683
Winson27c28f82016-05-05 16:16:50 -0700684 if (animationOverrides != null && animationOverrides.containsKey(task)) {
685 animation = animationOverrides.get(task);
686 }
687
Winsonf24f2162016-01-05 12:11:55 -0800688 updateTaskViewToTransform(tv, transform, animation);
689 }
690 }
691
692 /**
693 * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
694 */
Winsonbe8e6962016-02-01 14:27:52 -0800695 void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
Winson8aa99592016-01-19 15:07:07 -0800696 mDeferredTaskViewLayoutAnimation = animation;
Winson1c846142016-01-22 11:34:38 -0800697 invalidate();
Winsonf24f2162016-01-05 12:11:55 -0800698 }
699
700 /**
701 * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
Winsonbe8e6962016-02-01 14:27:52 -0800702 * given set of {@link AnimationProps} properties.
Winsonf24f2162016-01-05 12:11:55 -0800703 */
704 public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
Winsonbe8e6962016-02-01 14:27:52 -0800705 AnimationProps animation) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500706 if (taskView.isAnimatingTo(transform)) {
707 return;
708 }
709 taskView.cancelTransformAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800710 taskView.updateViewPropertiesToTaskTransform(transform, animation,
711 mRequestUpdateClippingListener);
712 }
713
714 /**
Winson8aa99592016-01-19 15:07:07 -0800715 * Returns the current task transforms of all tasks, falling back to the stack layout if there
716 * is no {@link TaskView} for the task.
Winsonf24f2162016-01-05 12:11:55 -0800717 */
Winson8aa99592016-01-19 15:07:07 -0800718 public void getCurrentTaskTransforms(ArrayList<Task> tasks,
719 ArrayList<TaskViewTransform> transformsOut) {
720 Utilities.matchTaskListSize(tasks, transformsOut);
Winson66474132016-02-23 18:45:47 -0800721 int focusState = mLayoutAlgorithm.getFocusState();
Winson8aa99592016-01-19 15:07:07 -0800722 for (int i = tasks.size() - 1; i >= 0; i--) {
723 Task task = tasks.get(i);
724 TaskViewTransform transform = transformsOut.get(i);
725 TaskView tv = getChildViewForTask(task);
726 if (tv != null) {
727 transform.fillIn(tv);
728 } else {
729 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
Winsone693aaf2016-03-01 12:05:59 -0800730 focusState, transform, null, true /* forceUpdate */,
731 false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800732 }
733 transform.visible = true;
734 }
735 }
736
737 /**
738 * Returns the task transforms for all the tasks in the stack if the stack was at the given
Winson14991502016-02-15 15:40:08 -0800739 * {@param stackScroll} and {@param focusState}.
Winson8aa99592016-01-19 15:07:07 -0800740 */
Winson66474132016-02-23 18:45:47 -0800741 public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
Winsond2a03062016-04-15 11:19:07 -0700742 boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
Winson8aa99592016-01-19 15:07:07 -0800743 Utilities.matchTaskListSize(tasks, transformsOut);
744 for (int i = tasks.size() - 1; i >= 0; i--) {
745 Task task = tasks.get(i);
746 TaskViewTransform transform = transformsOut.get(i);
Winson14991502016-02-15 15:40:08 -0800747 mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
Winsond2a03062016-04-15 11:19:07 -0700748 true /* forceUpdate */, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800749 transform.visible = true;
750 }
751 }
752
753 /**
Winson05e46ca2016-02-05 15:40:29 -0800754 * Cancels the next deferred task view layout.
755 */
756 void cancelDeferredTaskViewLayoutAnimation() {
757 mDeferredTaskViewLayoutAnimation = null;
758 }
759
760 /**
Winson8aa99592016-01-19 15:07:07 -0800761 * Cancels all {@link TaskView} animations.
Winson8aa99592016-01-19 15:07:07 -0800762 */
763 void cancelAllTaskViewAnimations() {
Winsonf24f2162016-01-05 12:11:55 -0800764 List<TaskView> taskViews = getTaskViews();
Winson3e874742016-01-07 10:08:17 -0800765 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsonf24f2162016-01-05 12:11:55 -0800766 final TaskView tv = taskViews.get(i);
Winsoneca47ef2016-04-21 16:48:50 -0700767 if (!mIgnoreTasks.contains(tv.getTask().key)) {
Winson8aa99592016-01-19 15:07:07 -0800768 tv.cancelTransformAnimation();
769 }
Winsonf24f2162016-01-05 12:11:55 -0800770 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800771 }
772
Winson3150e572015-10-23 15:07:24 -0700773 /**
774 * Updates the clip for each of the task views from back to front.
775 */
Winsonf24f2162016-01-05 12:11:55 -0800776 private void clipTaskViews() {
Winson Chung93748a12014-07-13 17:43:31 -0700777 // Update the clip on each task child
Winson Chung6ac8bd62015-01-07 16:38:35 -0800778 List<TaskView> taskViews = getTaskViews();
Winson3150e572015-10-23 15:07:24 -0700779 TaskView tmpTv = null;
Winson8aa99592016-01-19 15:07:07 -0800780 TaskView prevVisibleTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800781 int taskViewCount = taskViews.size();
Winson3150e572015-10-23 15:07:24 -0700782 for (int i = 0; i < taskViewCount; i++) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800783 TaskView tv = taskViews.get(i);
Winson3150e572015-10-23 15:07:24 -0700784 TaskView frontTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800785 int clipBottom = 0;
Winson8aa99592016-01-19 15:07:07 -0800786
Winson05e46ca2016-02-05 15:40:29 -0800787 if (isIgnoredTask(tv.getTask())) {
Winson8aa99592016-01-19 15:07:07 -0800788 // For each of the ignore tasks, update the translationZ of its TaskView to be
789 // between the translationZ of the tasks immediately underneath it
790 if (prevVisibleTv != null) {
791 tv.setTranslationZ(Math.max(tv.getTranslationZ(),
792 prevVisibleTv.getTranslationZ() + 0.1f));
793 }
794 }
795
Winson3150e572015-10-23 15:07:24 -0700796 if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800797 // Find the next view to clip against
Winson3150e572015-10-23 15:07:24 -0700798 for (int j = i + 1; j < taskViewCount; j++) {
799 tmpTv = taskViews.get(j);
Winsonef064132016-01-05 12:11:31 -0800800
Winson3150e572015-10-23 15:07:24 -0700801 if (tmpTv.shouldClipViewInStack()) {
802 frontTv = tmpTv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800803 break;
Winson Chung93748a12014-07-13 17:43:31 -0700804 }
805 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800806
807 // Clip against the next view, this is just an approximation since we are
808 // stacked and we can make assumptions about the visibility of the this
809 // task relative to the ones in front of it.
Winson3150e572015-10-23 15:07:24 -0700810 if (frontTv != null) {
Winsonbb410952015-12-04 14:34:11 -0800811 float taskBottom = tv.getBottom();
812 float frontTaskTop = frontTv.getTop();
Winson3150e572015-10-23 15:07:24 -0700813 if (frontTaskTop < taskBottom) {
814 // Map the stack view space coordinate (the rects) to view space
Winsonbb410952015-12-04 14:34:11 -0800815 clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
Winson3150e572015-10-23 15:07:24 -0700816 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800817 }
Winson Chung93748a12014-07-13 17:43:31 -0700818 }
Winsonf24f2162016-01-05 12:11:55 -0800819 tv.getViewBounds().setClipBottom(clipBottom);
Winsone693aaf2016-03-01 12:05:59 -0800820 tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
Winson8aa99592016-01-19 15:07:07 -0800821 prevVisibleTv = tv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800822 }
Winsonf24f2162016-01-05 12:11:55 -0800823 mTaskViewsClipDirty = false;
Winson Chung93748a12014-07-13 17:43:31 -0700824 }
825
Winson3e874742016-01-07 10:08:17 -0800826 /**
Winson8aa99592016-01-19 15:07:07 -0800827 * Updates the layout algorithm min and max virtual scroll bounds.
Winson8aa99592016-01-19 15:07:07 -0800828 */
Winson003eda62016-03-11 14:56:00 -0800829 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800830 // Compute the min and max scroll values
Winsond2a03062016-04-15 11:19:07 -0700831 mLayoutAlgorithm.update(mStack, mIgnoreTasks);
Winson Chung303e1ff2014-03-07 15:06:19 -0800832
Winson8aa99592016-01-19 15:07:07 -0800833 // Update the freeform workspace background
Winsona5e6b362015-11-02 17:17:20 -0800834 SystemServicesProxy ssp = Recents.getSystemServices();
835 if (ssp.hasFreeformWorkspaceSupport()) {
836 mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
Winsona5e6b362015-11-02 17:17:20 -0800837 mFreeformWorkspaceBackground.setBounds(mTmpRect);
838 }
839
Winson Chung303e1ff2014-03-07 15:06:19 -0800840 if (boundScrollToNewMinMax) {
Winson Chung012ef362014-07-31 18:36:25 -0700841 mStackScroller.boundScroll();
Winson Chung303e1ff2014-03-07 15:06:19 -0800842 }
843 }
844
Winson27c28f82016-05-05 16:16:50 -0700845 /**
846 * Updates the stack layout to its stable places.
847 */
848 private void updateLayoutToStableBounds() {
849 mWindowRect.set(mStableWindowRect);
850 mStackBounds.set(mStableStackBounds);
851 mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
852 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
853 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
854 updateLayoutAlgorithm(true /* boundScroll */);
855 }
856
Winson Chung012ef362014-07-31 18:36:25 -0700857 /** Returns the scroller. */
858 public TaskStackViewScroller getScroller() {
859 return mStackScroller;
860 }
861
Winson0d14d4d2015-10-26 17:05:04 -0700862 /**
863 * Sets the focused task to the provided (bounded taskIndex).
Winsone5f1faa2015-11-20 12:26:23 -0800864 *
865 * @return whether or not the stack will scroll as a part of this focus change
Winson0d14d4d2015-10-26 17:05:04 -0700866 */
Winsonf24f2162016-01-05 12:11:55 -0800867 private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
Winsonaaf33bc2015-12-03 12:02:38 -0800868 final boolean requestViewFocus) {
Winson4b9cded2016-01-26 16:26:47 -0800869 return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800870 }
871
872 /**
Winson05e46ca2016-02-05 15:40:29 -0800873 * Sets the focused task to the provided (bounded focusTaskIndex).
Peter Schillerb124d562015-12-11 21:31:17 -0800874 *
875 * @return whether or not the stack will scroll as a part of this focus change
876 */
Winson05e46ca2016-02-05 15:40:29 -0800877 private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
878 boolean requestViewFocus, int timerIndicatorDuration) {
Winson0d14d4d2015-10-26 17:05:04 -0700879 // Find the next task to focus
Winson4b057c62016-01-12 15:01:52 -0800880 int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
Winson68088812016-02-12 16:06:04 -0800881 Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
Winson0d14d4d2015-10-26 17:05:04 -0700882 final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
Winson250608a2015-11-24 15:00:31 -0800883 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700884
Winson0d14d4d2015-10-26 17:05:04 -0700885 // Reset the last focused task state if changed
Winsonaaf33bc2015-12-03 12:02:38 -0800886 if (mFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800887 // Cancel the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800888 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800889 final TaskView tv = getChildViewForTask(mFocusedTask);
890 if (tv != null) {
891 tv.getHeaderView().cancelFocusTimerIndicator();
892 }
893 }
Winsonb433c5b2016-01-20 17:11:29 -0800894
895 resetFocusedTask(mFocusedTask);
Winson0d14d4d2015-10-26 17:05:04 -0700896 }
897
Winsone5f1faa2015-11-20 12:26:23 -0800898 boolean willScroll = false;
Winsonaaf33bc2015-12-03 12:02:38 -0800899 mFocusedTask = newFocusedTask;
Peter Schillerb124d562015-12-11 21:31:17 -0800900
Winsonaaf33bc2015-12-03 12:02:38 -0800901 if (newFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800902 // Start the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800903 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800904 final TaskView tv = getChildViewForTask(mFocusedTask);
905 if (tv != null) {
Winson4b9cded2016-01-26 16:26:47 -0800906 tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
Peter Schillerb124d562015-12-11 21:31:17 -0800907 } else {
908 // The view is null; set a flag for later
Winson4b9cded2016-01-26 16:26:47 -0800909 mStartTimerIndicatorDuration = timerIndicatorDuration;
Peter Schillerb124d562015-12-11 21:31:17 -0800910 }
911 }
912
Winson0d14d4d2015-10-26 17:05:04 -0700913 if (scrollToTask) {
Winson1c846142016-01-22 11:34:38 -0800914 // Cancel any running enter animations at this point when we scroll or change focus
915 if (!mEnterAnimationComplete) {
916 cancelAllTaskViewAnimations();
917 }
918
Winsone693aaf2016-03-01 12:05:59 -0800919 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winson05e46ca2016-02-05 15:40:29 -0800920 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
921 requestViewFocus);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700922 } else {
Winson05e46ca2016-02-05 15:40:29 -0800923 // Focus the task view
924 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
925 if (newFocusedTaskView != null) {
926 newFocusedTaskView.setFocusedState(true, requestViewFocus);
927 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700928 }
929 }
Winsone5f1faa2015-11-20 12:26:23 -0800930 return willScroll;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700931 }
932
Winson Chungd213a1e2014-10-02 11:18:30 -0700933 /**
Winson0d14d4d2015-10-26 17:05:04 -0700934 * Sets the focused task relative to the currently focused task.
935 *
Winsone5f1faa2015-11-20 12:26:23 -0800936 * @param forward whether to go to the next task in the stack (along the curve) or the previous
Winson1b585612015-11-06 09:16:26 -0800937 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
938 * if the currently focused task is not a stack task, will set the focus
939 * to the first visible stack task
Winson0d14d4d2015-10-26 17:05:04 -0700940 * @param animated determines whether to actually draw the highlight along with the change in
941 * focus.
Winson Chungd213a1e2014-10-02 11:18:30 -0700942 */
Winson1b585612015-11-06 09:16:26 -0800943 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
Winsonf0009882016-06-01 12:22:55 -0700944 setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800945 }
946
947 /**
948 * Sets the focused task relative to the currently focused task.
949 *
950 * @param forward whether to go to the next task in the stack (along the curve) or the previous
951 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
952 * if the currently focused task is not a stack task, will set the focus
953 * to the first visible stack task
954 * @param animated determines whether to actually draw the highlight along with the change in
955 * focus.
956 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
957 * happens.
Winson4b9cded2016-01-26 16:26:47 -0800958 * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
Peter Schillerb124d562015-12-11 21:31:17 -0800959 */
960 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
Winsonf0009882016-06-01 12:22:55 -0700961 boolean cancelWindowAnimations, int timerIndicatorDuration) {
962 Task focusedTask = getFocusedTask();
963 int newIndex = mStack.indexOfStackTask(focusedTask);
964 if (focusedTask != null) {
Winson1b585612015-11-06 09:16:26 -0800965 if (stackTasksOnly) {
Winson250608a2015-11-24 15:00:31 -0800966 List<Task> tasks = mStack.getStackTasks();
Winsonf0009882016-06-01 12:22:55 -0700967 if (focusedTask.isFreeformTask()) {
Winson1b585612015-11-06 09:16:26 -0800968 // Try and focus the front most stack task
969 TaskView tv = getFrontMostTaskView(stackTasksOnly);
970 if (tv != null) {
Winson250608a2015-11-24 15:00:31 -0800971 newIndex = mStack.indexOfStackTask(tv.getTask());
Winson1b585612015-11-06 09:16:26 -0800972 }
973 } else {
974 // Try the next task if it is a stack task
Winsonaaf33bc2015-12-03 12:02:38 -0800975 int tmpNewIndex = newIndex + (forward ? -1 : 1);
Winson1b585612015-11-06 09:16:26 -0800976 if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
977 Task t = tasks.get(tmpNewIndex);
978 if (!t.isFreeformTask()) {
979 newIndex = tmpNewIndex;
980 }
981 }
982 }
983 } else {
Winson8b1871d2015-11-20 09:56:20 -0800984 // No restrictions, lets just move to the new task (looping forward/backwards if
985 // necessary)
Winson4b057c62016-01-12 15:01:52 -0800986 int taskCount = mStack.getTaskCount();
Winsonaaf33bc2015-12-03 12:02:38 -0800987 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
Winson1b585612015-11-06 09:16:26 -0800988 }
989 } else {
Winson23b0d3f2016-02-15 17:43:01 -0800990 // We don't have a focused task
991 float stackScroll = mStackScroller.getStackScroll();
992 ArrayList<Task> tasks = mStack.getStackTasks();
993 int taskCount = tasks.size();
994 if (forward) {
995 // Walk backwards and focus the next task smaller than the current stack scroll
996 for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
997 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
998 if (Float.compare(taskP, stackScroll) <= 0) {
999 break;
1000 }
1001 }
1002 } else {
1003 // Walk forwards and focus the next task larger than the current stack scroll
1004 for (newIndex = 0; newIndex < taskCount; newIndex++) {
1005 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1006 if (Float.compare(taskP, stackScroll) >= 0) {
1007 break;
1008 }
1009 }
Winson1b585612015-11-06 09:16:26 -08001010 }
1011 }
1012 if (newIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001013 boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
Winson4b9cded2016-01-26 16:26:47 -08001014 true /* requestViewFocus */, timerIndicatorDuration);
Winsone5f1faa2015-11-20 12:26:23 -08001015 if (willScroll && cancelWindowAnimations) {
1016 // As we iterate to the next/previous task, cancel any current/lagging window
1017 // transition animations
1018 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1019 }
Winson1b585612015-11-06 09:16:26 -08001020 }
Winson Chunga0e88b52014-08-11 19:25:42 -07001021 }
1022
Winson0d14d4d2015-10-26 17:05:04 -07001023 /**
1024 * Resets the focused task.
1025 */
Winsona0731a12015-12-02 15:10:14 -08001026 void resetFocusedTask(Task task) {
1027 if (task != null) {
1028 TaskView tv = getChildViewForTask(task);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001029 if (tv != null) {
Winsonf24f2162016-01-05 12:11:55 -08001030 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001031 }
Winson Chungb0a28ea2014-10-28 15:21:35 -07001032 }
Winsonaaf33bc2015-12-03 12:02:38 -08001033 mFocusedTask = null;
Winson Chungb0a28ea2014-10-28 15:21:35 -07001034 }
1035
Winson142af422015-11-09 10:39:57 -08001036 /**
1037 * Returns the focused task.
1038 */
1039 Task getFocusedTask() {
Winsonaaf33bc2015-12-03 12:02:38 -08001040 return mFocusedTask;
Winson142af422015-11-09 10:39:57 -08001041 }
1042
Winsonf0009882016-06-01 12:22:55 -07001043 /**
1044 * Returns the accessibility focused task.
1045 */
1046 Task getAccessibilityFocusedTask() {
1047 List<TaskView> taskViews = getTaskViews();
1048 int taskViewCount = taskViews.size();
1049 for (int i = 0; i < taskViewCount; i++) {
1050 TaskView tv = taskViews.get(i);
1051 if (Utilities.isDescendentAccessibilityFocused(tv)) {
1052 return tv.getTask();
1053 }
1054 }
1055 TaskView frontTv = getFrontMostTaskView(true /* stackTasksOnly */);
1056 if (frontTv != null) {
1057 return frontTv.getTask();
1058 }
1059 return null;
1060 }
1061
Winson Chung303e1ff2014-03-07 15:06:19 -08001062 @Override
Winson Chungee445952014-09-09 16:12:59 +02001063 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1064 super.onInitializeAccessibilityEvent(event);
Winson Chung6ac8bd62015-01-07 16:38:35 -08001065 List<TaskView> taskViews = getTaskViews();
1066 int taskViewCount = taskViews.size();
1067 if (taskViewCount > 0) {
1068 TaskView backMostTask = taskViews.get(0);
1069 TaskView frontMostTask = taskViews.get(taskViewCount - 1);
Winson250608a2015-11-24 15:00:31 -08001070 event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
1071 event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
Winson Chung296278a2015-12-17 12:09:02 -05001072 event.setContentDescription(frontMostTask.getTask().title);
Winson Chungee445952014-09-09 16:12:59 +02001073 }
Winson4b057c62016-01-12 15:01:52 -08001074 event.setItemCount(mStack.getTaskCount());
Winson59924fe2016-03-17 14:13:18 -07001075
1076 int stackHeight = mLayoutAlgorithm.mStackRect.height();
1077 event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
1078 event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
Winson Chungee445952014-09-09 16:12:59 +02001079 }
1080
1081 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001082 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1083 super.onInitializeAccessibilityNodeInfo(info);
1084 List<TaskView> taskViews = getTaskViews();
1085 int taskViewCount = taskViews.size();
Winsonf0009882016-06-01 12:22:55 -07001086 if (taskViewCount > 1) {
1087 // Find the accessibility focused task
1088 Task focusedTask = getAccessibilityFocusedTask();
Winson Chung83ea6f72015-06-17 13:00:23 -07001089 info.setScrollable(true);
Winsonf0009882016-06-01 12:22:55 -07001090 int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);
Winsonaaf33bc2015-12-03 12:02:38 -08001091 if (focusedTaskIndex > 0) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001092 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1093 }
Winsonf0009882016-06-01 12:22:55 -07001094 if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
1095 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1096 }
Winson Chung83ea6f72015-06-17 13:00:23 -07001097 }
1098 }
1099
1100 @Override
1101 public CharSequence getAccessibilityClassName() {
Winsonf0009882016-06-01 12:22:55 -07001102 return ScrollView.class.getName();
Winson Chung83ea6f72015-06-17 13:00:23 -07001103 }
1104
1105 @Override
1106 public boolean performAccessibilityAction(int action, Bundle arguments) {
1107 if (super.performAccessibilityAction(action, arguments)) {
1108 return true;
1109 }
Winsonf0009882016-06-01 12:22:55 -07001110 Task focusedTask = getAccessibilityFocusedTask();
1111 int taskIndex = mStack.indexOfStackTask(focusedTask);
1112 if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
1113 switch (action) {
1114 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
1115 setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
1116 0);
1117 return true;
1118 }
1119 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
1120 setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
1121 0);
1122 return true;
1123 }
Winson Chung83ea6f72015-06-17 13:00:23 -07001124 }
1125 }
1126 return false;
1127 }
1128
1129 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -08001130 public boolean onInterceptTouchEvent(MotionEvent ev) {
1131 return mTouchHandler.onInterceptTouchEvent(ev);
1132 }
1133
1134 @Override
1135 public boolean onTouchEvent(MotionEvent ev) {
1136 return mTouchHandler.onTouchEvent(ev);
1137 }
1138
1139 @Override
Winson Chungd213a1e2014-10-02 11:18:30 -07001140 public boolean onGenericMotionEvent(MotionEvent ev) {
1141 return mTouchHandler.onGenericMotionEvent(ev);
1142 }
1143
1144 @Override
Winson Chungd7b2cb12014-06-26 15:08:50 -07001145 public void computeScroll() {
Winsonf24f2162016-01-05 12:11:55 -08001146 if (mStackScroller.computeScroll()) {
1147 // Notify accessibility
1148 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
1149 }
Winson8aa99592016-01-19 15:07:07 -08001150 if (mDeferredTaskViewLayoutAnimation != null) {
1151 relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
Winsonf24f2162016-01-05 12:11:55 -08001152 mTaskViewsClipDirty = true;
Winson8aa99592016-01-19 15:07:07 -08001153 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -08001154 }
1155 if (mTaskViewsClipDirty) {
1156 clipTaskViews();
1157 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001158 }
1159
Winson3e874742016-01-07 10:08:17 -08001160 /**
Winson8aa99592016-01-19 15:07:07 -08001161 * Computes the maximum number of visible tasks and thumbnails. Requires that
Winsoneca4ab62015-11-04 10:50:28 -08001162 * updateLayoutForStack() is called first.
Winson Chunga91c2932014-11-07 15:02:38 -08001163 */
Winson36a5a2c2015-10-29 18:04:39 -07001164 public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
Winson250608a2015-11-24 15:00:31 -08001165 return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
Winson Chunga91c2932014-11-07 15:02:38 -08001166 }
1167
Winson3e874742016-01-07 10:08:17 -08001168 /**
Winson59924fe2016-03-17 14:13:18 -07001169 * Updates the system insets.
Winson3e874742016-01-07 10:08:17 -08001170 */
Winson59924fe2016-03-17 14:13:18 -07001171 public void setSystemInsets(Rect systemInsets) {
Winson67c79572016-04-13 14:02:18 -07001172 boolean changed = false;
1173 changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
1174 changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
1175 if (changed) {
Winson49df4202016-01-25 17:33:34 -08001176 requestLayout();
1177 }
Winson147ecaf2015-09-16 16:49:55 -07001178 }
1179
Winson Chunga91c2932014-11-07 15:02:38 -08001180 /**
Winson Chunga4ccb862014-08-22 15:26:27 -07001181 * This is called with the full window width and height to allow stack view children to
Winson Chungdcfa7972014-07-22 12:27:13 -07001182 * perform the full screen transition down.
Winson Chungf7bca432014-04-30 17:11:13 -07001183 */
Winson Chung303e1ff2014-03-07 15:06:19 -08001184 @Override
1185 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson70f0bf72016-02-01 14:05:29 -08001186 mInMeasureLayout = true;
Winson Chung303e1ff2014-03-07 15:06:19 -08001187 int width = MeasureSpec.getSize(widthMeasureSpec);
1188 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -08001189
Winson59924fe2016-03-17 14:13:18 -07001190 // Update the stable stack bounds, but only update the current stack bounds if the stable
1191 // bounds have changed. This is because we may get spurious measures while dragging where
1192 // our current stack bounds reflect the target drop region.
Winsonfc48b072016-04-21 11:20:11 -07001193 mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
Winsoncbb625b2016-07-06 15:24:15 -07001194 mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
1195 mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
Winson59924fe2016-03-17 14:13:18 -07001196 if (!mTmpRect.equals(mStableStackBounds)) {
1197 mStableStackBounds.set(mTmpRect);
1198 mStackBounds.set(mTmpRect);
1199 mStableWindowRect.set(0, 0, width, height);
1200 mWindowRect.set(0, 0, width, height);
1201 }
1202
Winson8aa99592016-01-19 15:07:07 -08001203 // Compute the rects in the stack algorithm
Winsonfc48b072016-04-21 11:20:11 -07001204 mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds,
Winsonf9357d92016-03-25 15:14:37 -07001205 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winsonfc48b072016-04-21 11:20:11 -07001206 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001207 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winson67c79572016-04-13 14:02:18 -07001208 updateLayoutAlgorithm(false /* boundScroll */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001209
Winsonf24f2162016-01-05 12:11:55 -08001210 // If this is the first layout, then scroll to the front of the stack, then update the
1211 // TaskViews with the stack so that we can lay them out
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001212 boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
1213 && mResetToInitialStateWhenResized;
1214 if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE
1215 || resetToInitialState) {
1216 if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
Winson67c79572016-04-13 14:02:18 -07001217 updateToInitialState();
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001218 mResetToInitialStateWhenResized = false;
Winson67c79572016-04-13 14:02:18 -07001219 }
Winson670ea712016-04-12 17:02:26 -07001220 if (!mAwaitingFirstLayout) {
1221 mInitialState = INITIAL_STATE_UPDATE_NONE;
1222 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001223 }
Winsone693aaf2016-03-01 12:05:59 -08001224
Winson8aa99592016-01-19 15:07:07 -08001225 // Rebind all the views, including the ignore ones
Winsond2a03062016-04-15 11:19:07 -07001226 bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001227
Winson Chungdcfa7972014-07-22 12:27:13 -07001228 // Measure each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001229 mTmpTaskViews.clear();
1230 mTmpTaskViews.addAll(getTaskViews());
1231 mTmpTaskViews.addAll(mViewPool.getViews());
1232 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001233 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001234 measureTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001235 }
1236
1237 setMeasuredDimension(width, height);
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001238 mLastWidth = width;
1239 mLastHeight = height;
Winson70f0bf72016-02-01 14:05:29 -08001240 mInMeasureLayout = false;
1241 }
1242
1243 /**
1244 * Measures a TaskView.
1245 */
1246 private void measureTaskView(TaskView tv) {
Winson67c79572016-04-13 14:02:18 -07001247 Rect padding = new Rect();
Winson70f0bf72016-02-01 14:05:29 -08001248 if (tv.getBackground() != null) {
Winson67c79572016-04-13 14:02:18 -07001249 tv.getBackground().getPadding(padding);
Winson70f0bf72016-02-01 14:05:29 -08001250 }
Winson67c79572016-04-13 14:02:18 -07001251 mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
1252 mTmpRect.union(mLayoutAlgorithm.mTaskRect);
Winson70f0bf72016-02-01 14:05:29 -08001253 tv.measure(
Winson67c79572016-04-13 14:02:18 -07001254 MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
Winson70f0bf72016-02-01 14:05:29 -08001255 MeasureSpec.EXACTLY),
Winson67c79572016-04-13 14:02:18 -07001256 MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
Winson70f0bf72016-02-01 14:05:29 -08001257 MeasureSpec.EXACTLY));
Winson Chung303e1ff2014-03-07 15:06:19 -08001258 }
1259
1260 @Override
1261 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson36a5a2c2015-10-29 18:04:39 -07001262 // Layout each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001263 mTmpTaskViews.clear();
1264 mTmpTaskViews.addAll(getTaskViews());
1265 mTmpTaskViews.addAll(mViewPool.getViews());
1266 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001267 for (int i = 0; i < taskViewCount; i++) {
Winsonc69249f2016-03-28 13:38:39 -07001268 layoutTaskView(changed, mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001269 }
1270
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001271 if (changed) {
Winsona2236f12015-11-13 16:10:01 -08001272 if (mStackScroller.isScrollOutOfBounds()) {
1273 mStackScroller.boundScroll();
1274 }
Winsonf24f2162016-01-05 12:11:55 -08001275 }
Winsonc69249f2016-03-28 13:38:39 -07001276
Winson8aa99592016-01-19 15:07:07 -08001277 // Relayout all of the task views including the ignored ones
Winson67c79572016-04-13 14:02:18 -07001278 relayoutTaskViews(AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -08001279 clipTaskViews();
1280
1281 if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
1282 mAwaitingFirstLayout = false;
Winson670ea712016-04-12 17:02:26 -07001283 mInitialState = INITIAL_STATE_UPDATE_NONE;
Winsonf24f2162016-01-05 12:11:55 -08001284 onFirstLayout();
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001285 }
Winson Chungdcfa7972014-07-22 12:27:13 -07001286 }
Winson Chung24cf1522014-05-29 12:03:33 -07001287
Winson70f0bf72016-02-01 14:05:29 -08001288 /**
1289 * Lays out a TaskView.
1290 */
Winsonc69249f2016-03-28 13:38:39 -07001291 private void layoutTaskView(boolean changed, TaskView tv) {
1292 if (changed) {
Winson67c79572016-04-13 14:02:18 -07001293 Rect padding = new Rect();
Winsonc69249f2016-03-28 13:38:39 -07001294 if (tv.getBackground() != null) {
Winson67c79572016-04-13 14:02:18 -07001295 tv.getBackground().getPadding(padding);
Winsonc69249f2016-03-28 13:38:39 -07001296 }
Winson67c79572016-04-13 14:02:18 -07001297 mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
1298 mTmpRect.union(mLayoutAlgorithm.mTaskRect);
Winsonc69249f2016-03-28 13:38:39 -07001299 tv.cancelTransformAnimation();
Winson67c79572016-04-13 14:02:18 -07001300 tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
1301 mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
Winson70f0bf72016-02-01 14:05:29 -08001302 } else {
Winsonc69249f2016-03-28 13:38:39 -07001303 // If the layout has not changed, then just lay it out again in-place
1304 tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
Winson70f0bf72016-02-01 14:05:29 -08001305 }
Winson70f0bf72016-02-01 14:05:29 -08001306 }
1307
Winson Chungdcfa7972014-07-22 12:27:13 -07001308 /** Handler for the first layout. */
1309 void onFirstLayout() {
Winsonf24f2162016-01-05 12:11:55 -08001310 // Setup the view for the enter animation
1311 mAnimationHelper.prepareForEnterAnimation();
Winson Chung083baf92014-07-11 10:32:42 -07001312
Winsona78a8f32015-12-03 10:55:01 -08001313 // Animate in the freeform workspace
Winsonbe8e6962016-02-01 14:27:52 -08001314 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
1315 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
1316 Interpolators.FAST_OUT_SLOW_IN));
Winsona78a8f32015-12-03 10:55:01 -08001317
Winson0d14d4d2015-10-26 17:05:04 -07001318 // Set the task focused state without requesting view focus, and leave the focus animations
1319 // until after the enter-animation
Winson5da43472015-11-04 17:39:55 -08001320 RecentsConfiguration config = Recents.getConfiguration();
1321 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson4b9cded2016-01-26 16:26:47 -08001322 int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
Winson5da43472015-11-04 17:39:55 -08001323 if (focusedTaskIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001324 setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
Winson5da43472015-11-04 17:39:55 -08001325 false /* requestViewFocus */);
Winson0983e022015-10-13 17:21:42 -07001326 }
Winson Chung860e2d82014-12-04 11:43:02 -08001327
Winson8f6ee482016-03-18 17:51:48 -07001328 // Update the stack action button visibility
Winson3b6ba1a2016-03-22 15:37:54 -07001329 if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1330 mStack.getTaskCount() > 0) {
Winson8f6ee482016-03-18 17:51:48 -07001331 EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
Winson Chungde750de2015-12-11 10:26:06 -05001332 } else {
Winson8f6ee482016-03-18 17:51:48 -07001333 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winsonc29ff002015-11-20 16:00:45 -08001334 }
Winson Chung24cf1522014-05-29 12:03:33 -07001335 }
1336
Winson671e8f92016-01-12 13:16:56 -08001337 public boolean isTouchPointInView(float x, float y, TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001338 mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
1339 mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
1340 return mTmpRect.contains((int) x, (int) y);
1341 }
1342
1343 /**
1344 * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
1345 * calculating the scroll position before and after a layout change.
1346 */
1347 public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
1348 for (int i = tasks.size() - 1; i >= 0; i--) {
1349 Task task = tasks.get(i);
1350
1351 // Ignore deleting tasks
Winson05e46ca2016-02-05 15:40:29 -08001352 if (isIgnoredTask(task)) {
Winson8aa99592016-01-19 15:07:07 -08001353 if (i == tasks.size() - 1) {
1354 isFrontMostTask.value = true;
1355 }
1356 continue;
1357 }
1358 return task;
1359 }
1360 return null;
Winson Chung303e1ff2014-03-07 15:06:19 -08001361 }
1362
Jorim Jaggi900fb482015-06-02 15:07:33 -07001363 @Override
Winsonbe8e6962016-02-01 14:27:52 -08001364 protected void onDraw(Canvas canvas) {
1365 super.onDraw(canvas);
1366
Winson36a5a2c2015-10-29 18:04:39 -07001367 // Draw the freeform workspace background
Winson805578d2015-11-23 14:47:37 -08001368 SystemServicesProxy ssp = Recents.getSystemServices();
1369 if (ssp.hasFreeformWorkspaceSupport()) {
1370 if (mFreeformWorkspaceBackground.getAlpha() > 0) {
1371 mFreeformWorkspaceBackground.draw(canvas);
1372 }
Winson36a5a2c2015-10-29 18:04:39 -07001373 }
Jorim Jaggi900fb482015-06-02 15:07:33 -07001374 }
1375
Winsona78a8f32015-12-03 10:55:01 -08001376 @Override
1377 protected boolean verifyDrawable(Drawable who) {
1378 if (who == mFreeformWorkspaceBackground) {
1379 return true;
1380 }
1381 return super.verifyDrawable(who);
1382 }
1383
Winsona5e6b362015-11-02 17:17:20 -08001384 /**
1385 * Launches the freeform tasks.
1386 */
1387 public boolean launchFreeformTasks() {
Winsonf24f2162016-01-05 12:11:55 -08001388 ArrayList<Task> tasks = mStack.getFreeformTasks();
1389 if (!tasks.isEmpty()) {
1390 Task frontTask = tasks.get(tasks.size() - 1);
1391 if (frontTask != null && frontTask.isFreeformTask()) {
1392 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
1393 frontTask, null, INVALID_STACK_ID, false));
1394 return true;
1395 }
Winsona5e6b362015-11-02 17:17:20 -08001396 }
1397 return false;
1398 }
1399
Winson Chung303e1ff2014-03-07 15:06:19 -08001400 /**** TaskStackCallbacks Implementation ****/
1401
1402 @Override
Winson Chung06266772015-12-11 10:24:21 -05001403 public void onStackTaskAdded(TaskStack stack, Task newTask) {
1404 // Update the min/max scroll and animate other task views into their new positions
Winson8aa99592016-01-19 15:07:07 -08001405 updateLayoutAlgorithm(true /* boundScroll */);
Winson Chung06266772015-12-11 10:24:21 -05001406
1407 // Animate all the tasks into place
Winson409d99a2016-05-10 15:03:46 -07001408 relayoutTaskViews(mAwaitingFirstLayout
1409 ? AnimationProps.IMMEDIATE
1410 : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winson Chung06266772015-12-11 10:24:21 -05001411 }
1412
Winson8aa99592016-01-19 15:07:07 -08001413 /**
1414 * We expect that the {@link TaskView} associated with the removed task is already hidden.
1415 */
Winson Chung06266772015-12-11 10:24:21 -05001416 @Override
Winson6c8217a2016-05-25 10:53:53 -07001417 public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
1418 AnimationProps animation, boolean fromDockGesture) {
Winsonaaf33bc2015-12-03 12:02:38 -08001419 if (mFocusedTask == removedTask) {
Winsona0731a12015-12-02 15:10:14 -08001420 resetFocusedTask(removedTask);
1421 }
1422
Winson8aa99592016-01-19 15:07:07 -08001423 // Remove the view associated with this task, we can't rely on updateTransforms
1424 // to work here because the task is no longer in the list
1425 TaskView tv = getChildViewForTask(removedTask);
1426 if (tv != null) {
1427 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -08001428 }
1429
Winson8aa99592016-01-19 15:07:07 -08001430 // Remove the task from the ignored set
1431 removeIgnoreTask(removedTask);
1432
1433 // If requested, relayout with the given animation
1434 if (animation != null) {
1435 updateLayoutAlgorithm(true /* boundScroll */);
1436 relayoutTaskViews(animation);
1437 }
Winsonf24f2162016-01-05 12:11:55 -08001438
Winson Chung931c51f2015-12-17 17:08:55 -05001439 // Update the new front most task's action button
1440 if (mScreenPinningEnabled && newFrontMostTask != null) {
Winson Chung1f24c7e2014-07-11 17:06:48 -07001441 TaskView frontTv = getChildViewForTask(newFrontMostTask);
1442 if (frontTv != null) {
Winson Chung931c51f2015-12-17 17:08:55 -05001443 frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
Winson Chung1f24c7e2014-07-11 17:06:48 -07001444 }
1445 }
1446
Winson397ae742015-11-20 11:27:33 -08001447 // If there are no remaining tasks, then just close recents
Winson4b057c62016-01-12 15:01:52 -08001448 if (mStack.getTaskCount() == 0) {
Winson20684082016-03-16 17:13:34 -07001449 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
1450 ? R.string.recents_empty_message
1451 : R.string.recents_empty_message_dismissed_all));
Winson Chung9f49df92014-05-07 18:08:34 -07001452 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001453 }
1454
Winsona1ededd2016-03-25 12:23:12 -07001455 @Override
Winson3b6ba1a2016-03-22 15:37:54 -07001456 public void onStackTasksRemoved(TaskStack stack) {
1457 // Reset the focused task
1458 resetFocusedTask(getFocusedTask());
1459
1460 // Return all the views to the pool
1461 List<TaskView> taskViews = new ArrayList<>();
1462 taskViews.addAll(getTaskViews());
1463 for (int i = taskViews.size() - 1; i >= 0; i--) {
1464 mViewPool.returnViewToPool(taskViews.get(i));
1465 }
1466
1467 // Remove all the ignore tasks
1468 mIgnoreTasks.clear();
1469
1470 // If there are no remaining tasks, then just close recents
1471 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
1472 R.string.recents_empty_message_dismissed_all));
1473 }
1474
1475 @Override
Winsona1ededd2016-03-25 12:23:12 -07001476 public void onStackTasksUpdated(TaskStack stack) {
1477 // Update the layout and immediately layout
1478 updateLayoutAlgorithm(false /* boundScroll */);
1479 relayoutTaskViews(AnimationProps.IMMEDIATE);
1480
1481 // Rebind all the task views. This will not trigger new resources to be loaded
1482 // unless they have actually changed
1483 List<TaskView> taskViews = getTaskViews();
1484 int taskViewCount = taskViews.size();
1485 for (int i = 0; i < taskViewCount; i++) {
1486 TaskView tv = taskViews.get(i);
1487 bindTaskView(tv, tv.getTask());
1488 }
1489 }
1490
Winson Chung303e1ff2014-03-07 15:06:19 -08001491 /**** ViewPoolConsumer Implementation ****/
1492
1493 @Override
1494 public TaskView createView(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -07001495 return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
Winson Chung303e1ff2014-03-07 15:06:19 -08001496 }
1497
1498 @Override
Winson05e46ca2016-02-05 15:40:29 -08001499 public void onReturnViewToPool(TaskView tv) {
Winson Chung931c51f2015-12-17 17:08:55 -05001500 final Task task = tv.getTask();
Winson Chung303e1ff2014-03-07 15:06:19 -08001501
Winson43336942016-03-07 14:52:59 -08001502 // Unbind the task from the task view
1503 unbindTaskView(tv, task);
Winson Chung303e1ff2014-03-07 15:06:19 -08001504
Winson Chung931c51f2015-12-17 17:08:55 -05001505 // Reset the view properties and view state
Winsonf0009882016-06-01 12:22:55 -07001506 tv.clearAccessibilityFocus();
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001507 tv.resetViewProperties();
Winsonf24f2162016-01-05 12:11:55 -08001508 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungb0a28ea2014-10-28 15:21:35 -07001509 tv.setClipViewInStack(false);
Winson Chung931c51f2015-12-17 17:08:55 -05001510 if (mScreenPinningEnabled) {
Winsonf24f2162016-01-05 12:11:55 -08001511 tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
Winson Chung931c51f2015-12-17 17:08:55 -05001512 }
Winson3c107162016-01-22 15:53:00 -08001513
1514 // Detach the view from the hierarchy
1515 detachViewFromParent(tv);
1516 // Update the task views list after removing the task view
1517 updateTaskViewsList();
Winson Chung303e1ff2014-03-07 15:06:19 -08001518 }
1519
1520 @Override
Winson05e46ca2016-02-05 15:40:29 -08001521 public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001522 // Find the index where this task should be placed in the stack
Winson250608a2015-11-24 15:00:31 -08001523 int taskIndex = mStack.indexOfStackTask(task);
Winsona78a8f32015-12-03 10:55:01 -08001524 int insertIndex = findTaskViewInsertIndex(task, taskIndex);
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001525
Winson Chung303e1ff2014-03-07 15:06:19 -08001526 // Add/attach the view to the hierarchy
Winson Chung303e1ff2014-03-07 15:06:19 -08001527 if (isNewView) {
Winson70f0bf72016-02-01 14:05:29 -08001528 if (mInMeasureLayout) {
1529 // If we are measuring the layout, then just add the view normally as it will be
1530 // laid out during the layout pass
1531 addView(tv, insertIndex);
1532 } else {
1533 // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
1534 // pass, and we should layout the new child ourselves
1535 ViewGroup.LayoutParams params = tv.getLayoutParams();
1536 if (params == null) {
1537 params = generateDefaultLayoutParams();
1538 }
1539 addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
1540 measureTaskView(tv);
Winsonc69249f2016-03-28 13:38:39 -07001541 layoutTaskView(true /* changed */, tv);
Winson70f0bf72016-02-01 14:05:29 -08001542 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001543 } else {
1544 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
1545 }
Winson Chung6ac8bd62015-01-07 16:38:35 -08001546 // Update the task views list after adding the new task view
1547 updateTaskViewsList();
Winson Chungb0a28ea2014-10-28 15:21:35 -07001548
Winson43336942016-03-07 14:52:59 -08001549 // Bind the task view to the new task
1550 bindTaskView(tv, task);
Winson3c107162016-01-22 15:53:00 -08001551
1552 // If the doze trigger has already fired, then update the state for this task view
Winsone693aaf2016-03-01 12:05:59 -08001553 if (mUIDozeTrigger.isAsleep()) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +00001554 tv.setNoUserInteractionState();
1555 }
Winson3c107162016-01-22 15:53:00 -08001556
Winson Chungb0a28ea2014-10-28 15:21:35 -07001557 // Set the new state for this view, including the callbacks and view clipping
1558 tv.setCallbacks(this);
1559 tv.setTouchEnabled(true);
1560 tv.setClipViewInStack(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001561 if (mFocusedTask == task) {
Winsonf24f2162016-01-05 12:11:55 -08001562 tv.setFocusedState(true, false /* requestViewFocus */);
Winson4b9cded2016-01-26 16:26:47 -08001563 if (mStartTimerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -08001564 // The timer indicator couldn't be started before, so start it now
Winson4b9cded2016-01-26 16:26:47 -08001565 tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
1566 mStartTimerIndicatorDuration = 0;
Peter Schillerb124d562015-12-11 21:31:17 -08001567 }
Winsona0731a12015-12-02 15:10:14 -08001568 }
Winson Chung931c51f2015-12-17 17:08:55 -05001569
1570 // Restore the action button visibility if it is the front most task view
Winson35a8b042016-01-22 09:41:09 -08001571 if (mScreenPinningEnabled && tv.getTask() ==
1572 mStack.getStackFrontMostTask(false /* includeFreeform */)) {
Winson Chung931c51f2015-12-17 17:08:55 -05001573 tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
1574 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001575 }
1576
1577 @Override
1578 public boolean hasPreferredData(TaskView tv, Task preferredData) {
1579 return (tv.getTask() == preferredData);
1580 }
1581
Winson43336942016-03-07 14:52:59 -08001582 private void bindTaskView(TaskView tv, Task task) {
1583 // Rebind the task and request that this task's data be filled into the TaskView
Winsonfc48b072016-04-21 11:20:11 -07001584 tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
Winson43336942016-03-07 14:52:59 -08001585
1586 // Load the task data
Winsond2a03062016-04-15 11:19:07 -07001587 Recents.getTaskLoader().loadTaskData(task);
Winson43336942016-03-07 14:52:59 -08001588 }
1589
1590 private void unbindTaskView(TaskView tv, Task task) {
1591 // Report that this task's data is no longer being used
1592 Recents.getTaskLoader().unloadTaskData(task);
1593 }
1594
Winson Chung303e1ff2014-03-07 15:06:19 -08001595 /**** TaskViewCallbacks Implementation ****/
1596
1597 @Override
Winson Chung93748a12014-07-13 17:43:31 -07001598 public void onTaskViewClipStateChanged(TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001599 if (!mTaskViewsClipDirty) {
1600 mTaskViewsClipDirty = true;
1601 invalidate();
1602 }
Winson Chung93748a12014-07-13 17:43:31 -07001603 }
1604
Winson1c846142016-01-22 11:34:38 -08001605 /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
1606
1607 @Override
Winson66474132016-02-23 18:45:47 -08001608 public void onFocusStateChanged(int prevFocusState, int curFocusState) {
Winson1c846142016-01-22 11:34:38 -08001609 if (mDeferredTaskViewLayoutAnimation == null) {
1610 mUIDozeTrigger.poke();
Winsonbe8e6962016-02-01 14:27:52 -08001611 relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
Winson1c846142016-01-22 11:34:38 -08001612 }
1613 }
1614
Winson Chung012ef362014-07-31 18:36:25 -07001615 /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
1616
1617 @Override
Winson14991502016-02-15 15:40:08 -08001618 public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
Winson Chung012ef362014-07-31 18:36:25 -07001619 mUIDozeTrigger.poke();
Winson8aa99592016-01-19 15:07:07 -08001620 if (animation != null) {
1621 relayoutTaskViewsOnNextFrame(animation);
1622 }
Winsonc29ff002015-11-20 16:00:45 -08001623
Winson49df4202016-01-25 17:33:34 -08001624 if (mEnterAnimationComplete) {
Winson8f6ee482016-03-18 17:51:48 -07001625 if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
Winson3b6ba1a2016-03-22 15:37:54 -07001626 curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1627 mStack.getTaskCount() > 0) {
Winson8f6ee482016-03-18 17:51:48 -07001628 EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
1629 } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1630 curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1631 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winson49df4202016-01-25 17:33:34 -08001632 }
Winsonc29ff002015-11-20 16:00:45 -08001633 }
Winson Chung012ef362014-07-31 18:36:25 -07001634 }
1635
Winsone6c90732015-09-24 16:06:29 -07001636 /**** EventBus Events ****/
Winson Chung9f49df92014-05-07 18:08:34 -07001637
Winsone6c90732015-09-24 16:06:29 -07001638 public final void onBusEvent(PackagesChangedEvent event) {
Winson Chung04400672014-10-17 14:53:30 -07001639 // Compute which components need to be removed
Winson55003902016-01-12 12:00:37 -08001640 ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
Winsone7f138c2015-10-22 16:15:21 -07001641 event.packageName, event.userId);
Winson Chung04400672014-10-17 14:53:30 -07001642
Winson Chung9f49df92014-05-07 18:08:34 -07001643 // For other tasks, just remove them directly if they no longer exist
Winson250608a2015-11-24 15:00:31 -08001644 ArrayList<Task> tasks = mStack.getStackTasks();
Winson Chung9f49df92014-05-07 18:08:34 -07001645 for (int i = tasks.size() - 1; i >= 0; i--) {
1646 final Task t = tasks.get(i);
Winsone7f138c2015-10-22 16:15:21 -07001647 if (removedComponents.contains(t.key.getComponent())) {
Winson0d14d4d2015-10-26 17:05:04 -07001648 final TaskView tv = getChildViewForTask(t);
Winson Chung9f49df92014-05-07 18:08:34 -07001649 if (tv != null) {
1650 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001651 tv.dismissTask();
Winson Chung9f49df92014-05-07 18:08:34 -07001652 } else {
1653 // Otherwise, remove the task from the stack immediately
Winson20684082016-03-16 17:13:34 -07001654 mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson Chung9f49df92014-05-07 18:08:34 -07001655 }
1656 }
1657 }
1658 }
Winson2536c7e2015-10-01 15:49:31 -07001659
Winson Chung48f2cda2015-12-11 13:20:12 -05001660 public final void onBusEvent(LaunchTaskEvent event) {
1661 // Cancel any doze triggers once a task is launched
1662 mUIDozeTrigger.stopDozing();
1663 }
1664
Winsonb61e6542016-02-04 14:37:18 -08001665 public final void onBusEvent(LaunchNextTaskRequestEvent event) {
1666 int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
1667 if (launchTaskIndex != -1) {
1668 launchTaskIndex = Math.max(0, launchTaskIndex - 1);
1669 } else {
1670 launchTaskIndex = mStack.getTaskCount() - 1;
1671 }
1672 if (launchTaskIndex != -1) {
1673 // Stop all animations
Winsonb61e6542016-02-04 14:37:18 -08001674 cancelAllTaskViewAnimations();
1675
Winson96e61342016-03-15 16:47:19 -07001676 final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
Winsonf8597b22016-03-23 18:44:26 -07001677 float curScroll = mStackScroller.getStackScroll();
1678 float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
1679 float absScrollDiff = Math.abs(targetScroll - curScroll);
1680 if (getChildViewForTask(launchTask) == null || absScrollDiff > 0.35f) {
1681 int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
1682 absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
1683 mStackScroller.animateScroll(targetScroll,
Winson96e61342016-03-15 16:47:19 -07001684 duration, new Runnable() {
1685 @Override
1686 public void run() {
1687 EventBus.getDefault().send(new LaunchTaskEvent(
1688 getChildViewForTask(launchTask), launchTask, null,
1689 INVALID_STACK_ID, false /* screenPinningRequested */));
1690 }
1691 });
1692 } else {
1693 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
1694 launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
1695 }
Winsond9342902016-02-25 10:18:33 -08001696
1697 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
1698 launchTask.key.getComponent().toString());
Winsonbc0f8cd2016-03-15 15:44:48 -07001699 } else if (mStack.getTaskCount() == 0) {
1700 // If there are no tasks, then just hide recents back to home.
1701 EventBus.getDefault().send(new HideRecentsEvent(false, true));
Winsonb61e6542016-02-04 14:37:18 -08001702 }
1703 }
1704
Winsonef064132016-01-05 12:11:31 -08001705 public final void onBusEvent(LaunchTaskStartedEvent event) {
Winsonf24f2162016-01-05 12:11:55 -08001706 mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
1707 event.getAnimationTrigger());
Winson0d14d4d2015-10-26 17:05:04 -07001708 }
Winson2536c7e2015-10-01 15:49:31 -07001709
Winsonef064132016-01-05 12:11:31 -08001710 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
1711 // Stop any scrolling
Winson61560f02016-05-02 16:36:20 -07001712 mTouchHandler.cancelNonDismissTaskAnimations();
Winsonef064132016-01-05 12:11:31 -08001713 mStackScroller.stopScroller();
1714 mStackScroller.stopBoundScrollAnimation();
Winson60df2462016-04-23 16:06:50 -07001715 cancelDeferredTaskViewLayoutAnimation();
Winson2536c7e2015-10-01 15:49:31 -07001716
Winsonef064132016-01-05 12:11:31 -08001717 // Start the task animations
Winsonf24f2162016-01-05 12:11:55 -08001718 mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001719
1720 // Dismiss the freeform workspace background
Winson50448632016-02-01 18:04:59 -08001721 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
Winsonbe8e6962016-02-01 14:27:52 -08001722 animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
1723 Interpolators.FAST_OUT_SLOW_IN));
Winson0d14d4d2015-10-26 17:05:04 -07001724 }
Winson42be4312015-10-10 11:51:08 -07001725
Winson0d14d4d2015-10-26 17:05:04 -07001726 public final void onBusEvent(DismissFocusedTaskViewEvent event) {
Winsonaaf33bc2015-12-03 12:02:38 -08001727 if (mFocusedTask != null) {
1728 TaskView tv = getChildViewForTask(mFocusedTask);
1729 if (tv != null) {
1730 tv.dismissTask();
1731 }
1732 resetFocusedTask(mFocusedTask);
Winson2536c7e2015-10-01 15:49:31 -07001733 }
1734 }
Winsone7f138c2015-10-22 16:15:21 -07001735
Winson3b6ba1a2016-03-22 15:37:54 -07001736 public final void onBusEvent(DismissTaskViewEvent event) {
Winsonef064132016-01-05 12:11:31 -08001737 // For visible children, defer removing the task until after the animation
Winson3b6ba1a2016-03-22 15:37:54 -07001738 mAnimationHelper.startDeleteTaskAnimation(event.taskView, event.getAnimationTrigger());
1739 }
1740
1741 public final void onBusEvent(final DismissAllTaskViewsEvent event) {
1742 // Keep track of the tasks which will have their data removed
1743 ArrayList<Task> tasks = new ArrayList<>(mStack.getStackTasks());
1744 mAnimationHelper.startDeleteAllTasksAnimation(getTaskViews(), event.getAnimationTrigger());
1745 event.addPostAnimationCallback(new Runnable() {
1746 @Override
1747 public void run() {
1748 // Announce for accessibility
1749 announceForAccessibility(getContext().getString(
1750 R.string.accessibility_recents_all_items_dismissed));
1751
1752 // Remove all tasks and delete the task data for all tasks
1753 mStack.removeAllTasks();
1754 for (int i = tasks.size() - 1; i >= 0; i--) {
1755 EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
1756 }
1757
1758 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
1759 }
1760 });
1761
Winsonef064132016-01-05 12:11:31 -08001762 }
1763
1764 public final void onBusEvent(TaskViewDismissedEvent event) {
Winson3b6ba1a2016-03-22 15:37:54 -07001765 // Announce for accessibility
1766 announceForAccessibility(getContext().getString(
1767 R.string.accessibility_recents_item_dismissed, event.task.title));
1768
1769 // Remove the task from the stack
Winson61560f02016-05-02 16:36:20 -07001770 mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
Winsonef064132016-01-05 12:11:31 -08001771 EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
Winson42329522016-02-05 10:39:46 -08001772
1773 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
1774 event.task.key.getComponent().toString());
Winsonef064132016-01-05 12:11:31 -08001775 }
1776
1777 public final void onBusEvent(FocusNextTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001778 // Stop any scrolling
1779 mStackScroller.stopScroller();
1780 mStackScroller.stopBoundScrollAnimation();
1781
Peter Schillerb124d562015-12-11 21:31:17 -08001782 setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
Winson4b9cded2016-01-26 16:26:47 -08001783 event.timerIndicatorDuration);
Winsonef064132016-01-05 12:11:31 -08001784 }
1785
1786 public final void onBusEvent(FocusPreviousTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001787 // Stop any scrolling
1788 mStackScroller.stopScroller();
1789 mStackScroller.stopBoundScrollAnimation();
1790
Winsonef064132016-01-05 12:11:31 -08001791 setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
1792 }
1793
Winsone7f138c2015-10-22 16:15:21 -07001794 public final void onBusEvent(UserInteractionEvent event) {
1795 // Poke the doze trigger on user interaction
1796 mUIDozeTrigger.poke();
Winson4b9cded2016-01-26 16:26:47 -08001797
1798 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
1799 if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
Winsonc5ef63f2016-01-21 14:39:23 -08001800 TaskView tv = getChildViewForTask(mFocusedTask);
1801 if (tv != null) {
1802 tv.getHeaderView().cancelFocusTimerIndicator();
1803 }
Peter Schillerb124d562015-12-11 21:31:17 -08001804 }
Winsone7f138c2015-10-22 16:15:21 -07001805 }
1806
Winsoneca4ab62015-11-04 10:50:28 -08001807 public final void onBusEvent(DragStartEvent event) {
Winson70f0bf72016-02-01 14:05:29 -08001808 // Ensure that the drag task is not animated
1809 addIgnoreTask(event.task);
1810
Winsoneca4ab62015-11-04 10:50:28 -08001811 if (event.task.isFreeformTask()) {
1812 // Animate to the front of the stack
Winsond9529612016-01-28 13:29:49 -08001813 mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
Winsoneca4ab62015-11-04 10:50:28 -08001814 }
Winsonf24f2162016-01-05 12:11:55 -08001815
1816 // Enlarge the dragged view slightly
1817 float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
1818 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
1819 mTmpTransform, null);
1820 mTmpTransform.scale = finalScale;
Winson3e874742016-01-07 10:08:17 -08001821 mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
Winson0a461f02016-04-19 10:51:12 -07001822 mTmpTransform.dimAlpha = 0f;
Winsonf24f2162016-01-05 12:11:55 -08001823 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08001824 new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winsoneca4ab62015-11-04 10:50:28 -08001825 }
1826
1827 public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
1828 SystemServicesProxy ssp = Recents.getSystemServices();
1829 if (ssp.hasFreeformWorkspaceSupport()) {
1830 event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
1831 event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
1832 }
1833 }
1834
1835 public final void onBusEvent(DragDropTargetChangedEvent event) {
Winson3f32e7e2016-04-20 17:18:08 -07001836 AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
1837 Interpolators.FAST_OUT_SLOW_IN);
Winson59924fe2016-03-17 14:13:18 -07001838 boolean ignoreTaskOverrides = false;
Winson3e874742016-01-07 10:08:17 -08001839 if (event.dropTarget instanceof TaskStack.DockState) {
1840 // Calculate the new task stack bounds that matches the window size that Recents will
1841 // have after the drop
1842 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
Winson670ea712016-04-12 17:02:26 -07001843 Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
1844 // When docked, the nav bar insets are consumed and the activity is measured without
1845 // insets. However, the window bounds include the insets, so we need to subtract them
1846 // here to make them identical.
1847 int height = getMeasuredHeight();
1848 height -= systemInsets.bottom;
1849 systemInsets.bottom = 0;
Winsonfc48b072016-04-21 11:20:11 -07001850 mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
Winson67c79572016-04-13 14:02:18 -07001851 height, mDividerSize, systemInsets,
Winson59924fe2016-03-17 14:13:18 -07001852 mLayoutAlgorithm, getResources(), mWindowRect));
Winson670ea712016-04-12 17:02:26 -07001853 mLayoutAlgorithm.setSystemInsets(systemInsets);
Winsonfc48b072016-04-21 11:20:11 -07001854 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001855 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1856 updateLayoutAlgorithm(true /* boundScroll */);
Winson59924fe2016-03-17 14:13:18 -07001857 ignoreTaskOverrides = true;
Winson3e874742016-01-07 10:08:17 -08001858 } else {
Winson8aa99592016-01-19 15:07:07 -08001859 // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
1860 // task view, so add it back to the ignore set after updating the layout
Winson8aa99592016-01-19 15:07:07 -08001861 removeIgnoreTask(event.task);
Winson27c28f82016-05-05 16:16:50 -07001862 updateLayoutToStableBounds();
Winson8aa99592016-01-19 15:07:07 -08001863 addIgnoreTask(event.task);
Winson3e874742016-01-07 10:08:17 -08001864 }
Winson27c28f82016-05-05 16:16:50 -07001865 relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
Winsoneca4ab62015-11-04 10:50:28 -08001866 }
1867
1868 public final void onBusEvent(final DragEndEvent event) {
Winson479f7442015-11-25 15:16:27 -08001869 // We don't handle drops on the dock regions
1870 if (event.dropTarget instanceof TaskStack.DockState) {
Winson59924fe2016-03-17 14:13:18 -07001871 // However, we do need to reset the overrides, since the last state of this task stack
1872 // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
1873 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winsoneca4ab62015-11-04 10:50:28 -08001874 return;
1875 }
1876
Winson479f7442015-11-25 15:16:27 -08001877 boolean isFreeformTask = event.task.isFreeformTask();
1878 boolean hasChangedStacks =
1879 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
1880 (isFreeformTask && event.dropTarget == mStackDropTarget);
Winson479f7442015-11-25 15:16:27 -08001881
Winson5b7dd532015-12-01 16:02:12 -08001882 if (hasChangedStacks) {
Winson479f7442015-11-25 15:16:27 -08001883 // Move the task to the right position in the stack (ie. the front of the stack if
Winson8aa99592016-01-19 15:07:07 -08001884 // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
Winson479f7442015-11-25 15:16:27 -08001885 // before we update their stack ids, otherwise, the keys will have changed.
1886 if (event.dropTarget == mFreeformWorkspaceDropTarget) {
1887 mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001888 } else if (event.dropTarget == mStackDropTarget) {
1889 mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001890 }
Winson8aa99592016-01-19 15:07:07 -08001891 updateLayoutAlgorithm(true /* boundScroll */);
Winson479f7442015-11-25 15:16:27 -08001892
1893 // Move the task to the new stack in the system after the animation completes
Winson Chungaaeaac12015-12-16 16:49:36 -05001894 event.addPostAnimationCallback(new Runnable() {
Winson479f7442015-11-25 15:16:27 -08001895 @Override
1896 public void run() {
1897 SystemServicesProxy ssp = Recents.getSystemServices();
1898 ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
1899 }
1900 });
Winsoneca4ab62015-11-04 10:50:28 -08001901 }
Winsoneca4ab62015-11-04 10:50:28 -08001902
Winson27c28f82016-05-05 16:16:50 -07001903 // Restore the task, so that relayout will apply to it below
Winson8aa99592016-01-19 15:07:07 -08001904 removeIgnoreTask(event.task);
Winson27c28f82016-05-05 16:16:50 -07001905
1906 // Convert the dragging task view back to its final layout-space rect
1907 Utilities.setViewFrameFromTranslation(event.taskView);
1908
1909 // Animate all the tasks into place
1910 ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
1911 animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
1912 Interpolators.FAST_OUT_SLOW_IN,
1913 event.getAnimationTrigger().decrementOnAnimationEnd()));
1914 relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
1915 Interpolators.FAST_OUT_SLOW_IN));
1916 event.getAnimationTrigger().increment();
1917 }
1918
1919 public final void onBusEvent(final DragEndCancelledEvent event) {
1920 // Restore the pre-drag task stack bounds, including the dragging task view
1921 removeIgnoreTask(event.task);
1922 updateLayoutToStableBounds();
1923
1924 // Convert the dragging task view back to its final layout-space rect
1925 Utilities.setViewFrameFromTranslation(event.taskView);
1926
1927 // Animate all the tasks into place
1928 ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
1929 animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
1930 Interpolators.FAST_OUT_SLOW_IN,
1931 event.getAnimationTrigger().decrementOnAnimationEnd()));
1932 relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
1933 Interpolators.FAST_OUT_SLOW_IN));
1934 event.getAnimationTrigger().increment();
Winsoneca4ab62015-11-04 10:50:28 -08001935 }
1936
Winson8b1871d2015-11-20 09:56:20 -08001937 public final void onBusEvent(IterateRecentsEvent event) {
Winson Chungd6b78a32015-12-15 10:22:45 -05001938 if (!mEnterAnimationComplete) {
1939 // Cancel the previous task's window transition before animating the focused state
1940 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1941 }
Winson8b1871d2015-11-20 09:56:20 -08001942 }
1943
Winsone5f1faa2015-11-20 12:26:23 -08001944 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
1945 mEnterAnimationComplete = true;
Winsonef064132016-01-05 12:11:31 -08001946
Winson4b057c62016-01-12 15:01:52 -08001947 if (mStack.getTaskCount() > 0) {
Winsonef064132016-01-05 12:11:31 -08001948 // Start the task enter animations
Winsonf24f2162016-01-05 12:11:55 -08001949 mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001950
1951 // Add a runnable to the post animation ref counter to clear all the views
1952 event.addPostAnimationCallback(new Runnable() {
1953 @Override
1954 public void run() {
1955 // Start the dozer to trigger to trigger any UI that shows after a timeout
1956 mUIDozeTrigger.startDozing();
1957
1958 // Update the focused state here -- since we only set the focused task without
1959 // requesting view focus in onFirstLayout(), actually request view focus and
1960 // animate the focused state if we are alt-tabbing now, after the window enter
1961 // animation is completed
1962 if (mFocusedTask != null) {
1963 RecentsConfiguration config = Recents.getConfiguration();
1964 RecentsActivityLaunchState launchState = config.getLaunchState();
1965 setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
1966 false /* scrollToTask */, launchState.launchedWithAltTab);
Winsonf0009882016-06-01 12:22:55 -07001967 TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
1968 if (mTouchExplorationEnabled && focusedTaskView != null) {
1969 focusedTaskView.requestAccessibilityFocus();
1970 }
Winsonef064132016-01-05 12:11:31 -08001971 }
Winson4b9cded2016-01-26 16:26:47 -08001972
1973 EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent());
Winsonef064132016-01-05 12:11:31 -08001974 }
1975 });
1976 }
Winsone5f1faa2015-11-20 12:26:23 -08001977 }
1978
Winsonb1e71d02015-11-23 12:40:23 -08001979 public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
1980 List<TaskView> taskViews = getTaskViews();
1981 int taskViewCount = taskViews.size();
1982 for (int i = 0; i < taskViewCount; i++) {
1983 TaskView tv = taskViews.get(i);
1984 Task task = tv.getTask();
Winson Chung296278a2015-12-17 12:09:02 -05001985 if (task.isFreeformTask()) {
Winsonb1e71d02015-11-23 12:40:23 -08001986 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
1987 }
1988 }
1989 }
1990
Winsond2a03062016-04-15 11:19:07 -07001991 public final void onBusEvent(final MultiWindowStateChangedEvent event) {
Winson95ee8732016-04-26 14:46:58 -07001992 if (event.inMultiWindow || !event.showDeferredAnimation) {
Winsond2a03062016-04-15 11:19:07 -07001993 setTasks(event.stack, true /* allowNotifyStackChanges */);
1994 } else {
1995 // Reset the launch state before handling the multiwindow change
1996 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
1997 launchState.reset();
1998
Winson67c79572016-04-13 14:02:18 -07001999 // Defer until the next frame to ensure that we have received all the system insets, and
2000 // initial layout updates
Winsond2a03062016-04-15 11:19:07 -07002001 event.getAnimationTrigger().increment();
Winson67c79572016-04-13 14:02:18 -07002002 post(new Runnable() {
Winson931845f2016-02-24 19:38:41 -08002003 @Override
2004 public void run() {
Winson67c79572016-04-13 14:02:18 -07002005 // Scroll the stack to the front to see the undocked task
Winsond2a03062016-04-15 11:19:07 -07002006 mAnimationHelper.startNewStackScrollAnimation(event.stack,
2007 event.getAnimationTrigger());
2008 event.getAnimationTrigger().decrement();
Winson931845f2016-02-24 19:38:41 -08002009 }
2010 });
2011 }
Winsond9529612016-01-28 13:29:49 -08002012 }
2013
Winsone693aaf2016-03-01 12:05:59 -08002014 public final void onBusEvent(ConfigurationChangedEvent event) {
Winsonfc48b072016-04-21 11:20:11 -07002015 if (event.fromDeviceOrientationChange) {
2016 mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
2017 mDisplayRect = Recents.getSystemServices().getDisplayRect();
Winson399d21e2016-05-18 13:26:29 -07002018
2019 // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
2020 // wrong bounds in the new layout
2021 mStackScroller.stopScroller();
Winsonfc48b072016-04-21 11:20:11 -07002022 }
Winson670ea712016-04-12 17:02:26 -07002023 reloadOnConfigurationChange();
Winson619e40c2016-03-25 16:12:35 -07002024
2025 // Notify the task views of the configuration change so they can reload their resources
2026 if (!event.fromMultiWindow) {
2027 mTmpTaskViews.clear();
2028 mTmpTaskViews.addAll(getTaskViews());
2029 mTmpTaskViews.addAll(mViewPool.getViews());
2030 int taskViewCount = mTmpTaskViews.size();
2031 for (int i = 0; i < taskViewCount; i++) {
2032 mTmpTaskViews.get(i).onConfigurationChanged();
2033 }
2034 }
2035
Winson44849b82016-03-29 10:45:45 -07002036 // Trigger a new layout and update to the initial state if necessary
Winson67c79572016-04-13 14:02:18 -07002037 if (event.fromMultiWindow) {
Winson44849b82016-03-29 10:45:45 -07002038 mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
Winson670ea712016-04-12 17:02:26 -07002039 requestLayout();
Winson67c79572016-04-13 14:02:18 -07002040 } else if (event.fromDeviceOrientationChange) {
2041 mInitialState = INITIAL_STATE_UPDATE_ALL;
2042 requestLayout();
Winson44849b82016-03-29 10:45:45 -07002043 }
Winson670ea712016-04-12 17:02:26 -07002044 }
2045
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07002046 public final void onBusEvent(RecentsGrowingEvent event) {
2047 mResetToInitialStateWhenResized = true;
2048 }
2049
Winson670ea712016-04-12 17:02:26 -07002050 public void reloadOnConfigurationChange() {
2051 mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
2052 mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winsone693aaf2016-03-01 12:05:59 -08002053 }
2054
Winson0d14d4d2015-10-26 17:05:04 -07002055 /**
Winsona78a8f32015-12-03 10:55:01 -08002056 * Starts an alpha animation on the freeform workspace background.
2057 */
Winsonbe8e6962016-02-01 14:27:52 -08002058 private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
2059 AnimationProps animation) {
Winsona78a8f32015-12-03 10:55:01 -08002060 if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
2061 return;
2062 }
2063
2064 Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
2065 mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
Winson3e874742016-01-07 10:08:17 -08002066 Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
Winsonbe8e6962016-02-01 14:27:52 -08002067 mFreeformWorkspaceBackgroundAnimator.setStartDelay(
2068 animation.getDuration(AnimationProps.ALPHA));
2069 mFreeformWorkspaceBackgroundAnimator.setDuration(
2070 animation.getDuration(AnimationProps.ALPHA));
2071 mFreeformWorkspaceBackgroundAnimator.setInterpolator(
2072 animation.getInterpolator(AnimationProps.ALPHA));
Winsona78a8f32015-12-03 10:55:01 -08002073 mFreeformWorkspaceBackgroundAnimator.start();
2074 }
2075
2076 /**
Winson8aa99592016-01-19 15:07:07 -08002077 * Returns the insert index for the task in the current set of task views. If the given task
Winsona78a8f32015-12-03 10:55:01 -08002078 * is already in the task view list, then this method returns the insert index assuming it
2079 * is first removed at the previous index.
2080 *
2081 * @param task the task we are finding the index for
2082 * @param taskIndex the index of the task in the stack
2083 */
2084 private int findTaskViewInsertIndex(Task task, int taskIndex) {
2085 if (taskIndex != -1) {
2086 List<TaskView> taskViews = getTaskViews();
2087 boolean foundTaskView = false;
2088 int taskViewCount = taskViews.size();
2089 for (int i = 0; i < taskViewCount; i++) {
2090 Task tvTask = taskViews.get(i).getTask();
2091 if (tvTask == task) {
2092 foundTaskView = true;
2093 } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
2094 if (foundTaskView) {
2095 return i - 1;
2096 } else {
2097 return i;
2098 }
2099 }
2100 }
2101 }
2102 return -1;
2103 }
Winson Chungde750de2015-12-11 10:26:06 -05002104
2105 /**
Winson6ea25882016-01-13 10:59:04 -08002106 * Reads current system flags related to accessibility and screen pinning.
2107 */
2108 private void readSystemFlags() {
2109 SystemServicesProxy ssp = Recents.getSystemServices();
2110 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
2111 mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
2112 Settings.System.LOCK_TO_APP_ENABLED) != 0;
2113 }
Winsond72c3152016-04-05 15:33:35 -07002114
2115 public void dump(String prefix, PrintWriter writer) {
2116 String innerPrefix = prefix + " ";
2117 String id = Integer.toHexString(System.identityHashCode(this));
2118
2119 writer.print(prefix); writer.print(TAG);
2120 writer.print(" hasDefRelayout=");
2121 writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
2122 writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
2123 writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
2124 writer.print(" initialState="); writer.print(mInitialState);
2125 writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
2126 writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
2127 writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
2128 writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
2129 writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
2130 writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
2131 writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
2132 writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
2133 writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
2134 writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
Winsonfc48b072016-04-21 11:20:11 -07002135 writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
2136 writer.print(" orientation="); writer.print(mDisplayOrientation);
Winsond72c3152016-04-05 15:33:35 -07002137 writer.print(" [0x"); writer.print(id); writer.print("]");
2138 writer.println();
2139
2140 if (mFocusedTask != null) {
2141 writer.print(innerPrefix);
2142 writer.print("Focused task: ");
Winson67c79572016-04-13 14:02:18 -07002143 mFocusedTask.dump("", writer);
Winsond72c3152016-04-05 15:33:35 -07002144 }
2145
2146 mLayoutAlgorithm.dump(innerPrefix, writer);
2147 mStackScroller.dump(innerPrefix, writer);
2148 }
Winson Chunga4cc9662014-07-25 12:10:38 -07002149}