blob: 90328714c51e025b9a0b56d40ba0e2309d050821 [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;
Winsonbb410952015-12-04 14:34:11 -080028import android.content.res.Resources;
Jorim Jaggi900fb482015-06-02 15:07:33 -070029import android.graphics.Canvas;
Winson Chung303e1ff2014-03-07 15:06:19 -080030import android.graphics.Rect;
Winsona78a8f32015-12-03 10:55:01 -080031import android.graphics.drawable.Drawable;
Winsonde0591a2015-12-04 17:24:35 -080032import android.graphics.drawable.GradientDrawable;
Winson Chung83ea6f72015-06-17 13:00:23 -070033import android.os.Bundle;
Winson Chung4ab4d832015-12-11 10:25:46 -050034import android.os.Parcelable;
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;
Winsonc0d70582016-01-29 10:24:39 -080047
Winson42329522016-02-05 10:39:46 -080048import com.android.internal.logging.MetricsLogger;
49import com.android.internal.logging.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080050import com.android.systemui.Interpolators;
Winson Chungc6a16232014-04-01 14:04:48 -070051import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070052import com.android.systemui.recents.Recents;
Winsone6c90732015-09-24 16:06:29 -070053import com.android.systemui.recents.RecentsActivity;
Winson2536c7e2015-10-01 15:49:31 -070054import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chung303e1ff2014-03-07 15:06:19 -080055import com.android.systemui.recents.RecentsConfiguration;
Winson4b9cded2016-01-26 16:26:47 -080056import com.android.systemui.recents.RecentsDebugFlags;
Winsone6c90732015-09-24 16:06:29 -070057import com.android.systemui.recents.events.EventBus;
Winsone5f1faa2015-11-20 12:26:23 -080058import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
Winsone693aaf2016-03-01 12:05:59 -080059import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
Winsonef064132016-01-05 12:11:31 -080060import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
Winson4b9cded2016-01-26 16:26:47 -080061import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
Winsone5f1faa2015-11-20 12:26:23 -080062import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
Winson8f6ee482016-03-18 17:51:48 -070063import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
Winsonbc0f8cd2016-03-15 15:44:48 -070064import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winson8b1871d2015-11-20 09:56:20 -080065import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Winsonb61e6542016-02-04 14:37:18 -080066import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Winson Chung48f2cda2015-12-11 13:20:12 -050067import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winsonef064132016-01-05 12:11:31 -080068import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
Winson88737542016-02-17 13:27:33 -080069import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
Winsone6c90732015-09-24 16:06:29 -070070import com.android.systemui.recents.events.activity.PackagesChangedEvent;
Winson8f6ee482016-03-18 17:51:48 -070071import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
Winson397ae742015-11-20 11:27:33 -080072import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
Winsonef064132016-01-05 12:11:31 -080073import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
Winson0d14d4d2015-10-26 17:05:04 -070074import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Winsonef064132016-01-05 12:11:31 -080075import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winsonb1e71d02015-11-23 12:40:23 -080076import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
Winsone7f138c2015-10-22 16:15:21 -070077import com.android.systemui.recents.events.ui.UserInteractionEvent;
Winsoneca4ab62015-11-04 10:50:28 -080078import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
79import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
80import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
81import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070082import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
83import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
84import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
Winson Chungffa2ec62014-07-03 15:54:42 -070085import com.android.systemui.recents.misc.DozeTrigger;
Winson Chungee445952014-09-09 16:12:59 +020086import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungbf5dbf12014-09-16 00:58:25 +020087import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080088import com.android.systemui.recents.model.Task;
89import com.android.systemui.recents.model.TaskStack;
Winson Chung303e1ff2014-03-07 15:06:19 -080090
Winson619e40c2016-03-25 16:12:35 -070091import java.lang.annotation.Retention;
92import java.lang.annotation.RetentionPolicy;
Winson Chung303e1ff2014-03-07 15:06:19 -080093import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -080094import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -080095
Winson Chung303e1ff2014-03-07 15:06:19 -080096
97/* The visual representation of a task stack view */
Winson Chung04dfe0d2014-03-14 14:06:29 -070098public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
Winson Chung012ef362014-07-31 18:36:25 -070099 TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
Winson1c846142016-01-22 11:34:38 -0800100 TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
Winsone6c90732015-09-24 16:06:29 -0700101 ViewPool.ViewPoolConsumer<TaskView, Task> {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700102
Winson Chung4ab4d832015-12-11 10:25:46 -0500103 private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
104 private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
105 "saved_instance_state_layout_focused_state";
106 private final static String KEY_SAVED_STATE_LAYOUT_STACK_SCROLL =
107 "saved_instance_state_layout_stack_scroll";
108
Winson8f6ee482016-03-18 17:51:48 -0700109 // The thresholds at which to show/hide the stack action button.
110 private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
111 private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
Winsonc29ff002015-11-20 16:00:45 -0800112
Winson8aa99592016-01-19 15:07:07 -0800113 public static final int DEFAULT_SYNC_STACK_DURATION = 200;
Winsonf24f2162016-01-05 12:11:55 -0800114 private static final int DRAG_SCALE_DURATION = 175;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100115 static final float DRAG_SCALE_FACTOR = 1.05f;
Winson Chung06266772015-12-11 10:24:21 -0500116
Winsonf8597b22016-03-23 18:44:26 -0700117 private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
Winson96e61342016-03-15 16:47:19 -0700118 private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
119
Winson8aa99592016-01-19 15:07:07 -0800120 private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
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
Winson05e46ca2016-02-05 15:40:29 -0800133 LayoutInflater mInflater;
Winson88737542016-02-17 13:27:33 -0800134 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
138 TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
Winson231bc9c2016-02-09 12:31:00 -0800139 @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
Winson Chung012ef362014-07-31 18:36:25 -0700140 TaskStackViewScroller mStackScroller;
Winson231bc9c2016-02-09 12:31:00 -0800141 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
Winson Chung303e1ff2014-03-07 15:06:19 -0800142 TaskStackViewTouchHandler mTouchHandler;
Winsonf24f2162016-01-05 12:11:55 -0800143 TaskStackAnimationHelper mAnimationHelper;
Winsonde0591a2015-12-04 17:24:35 -0800144 GradientDrawable mFreeformWorkspaceBackground;
Winsona78a8f32015-12-03 10:55:01 -0800145 ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -0800146 ViewPool<TaskView, Task> mViewPool;
Winsonf24f2162016-01-05 12:11:55 -0800147
148 ArrayList<TaskView> mTaskViews = new ArrayList<>();
Winsone6c90732015-09-24 16:06:29 -0700149 ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
Winson8aa99592016-01-19 15:07:07 -0800150 ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
Winsonbe8e6962016-02-01 14:27:52 -0800151 AnimationProps mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800152
Winson231bc9c2016-02-09 12:31:00 -0800153 @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
Winson Chungd543c1b2014-06-23 15:06:45 -0700154 DozeTrigger mUIDozeTrigger;
Winson231bc9c2016-02-09 12:31:00 -0800155 @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Winsonaaf33bc2015-12-03 12:02:38 -0800156 Task mFocusedTask;
Winsonf24f2162016-01-05 12:11:55 -0800157
Winsonbb410952015-12-04 14:34:11 -0800158 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")
Winsonf24f2162016-01-05 12:11:55 -0800163 boolean mTaskViewsClipDirty = true;
Winson231bc9c2016-02-09 12:31:00 -0800164 @ViewDebug.ExportedProperty(category="recents")
Winson Chung303e1ff2014-03-07 15:06:19 -0800165 boolean mAwaitingFirstLayout = true;
Winson231bc9c2016-02-09 12:31:00 -0800166 @ViewDebug.ExportedProperty(category="recents")
Winson619e40c2016-03-25 16:12:35 -0700167 @InitialStateAction
168 int mInitialState = INITIAL_STATE_UPDATE_ALL;
169 @ViewDebug.ExportedProperty(category="recents")
Winson70f0bf72016-02-01 14:05:29 -0800170 boolean mInMeasureLayout = false;
Winson231bc9c2016-02-09 12:31:00 -0800171 @ViewDebug.ExportedProperty(category="recents")
Winsone5f1faa2015-11-20 12:26:23 -0800172 boolean mEnterAnimationComplete = false;
Winson231bc9c2016-02-09 12:31:00 -0800173 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800174 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();
Winsond9529612016-01-28 13:29:49 -0800190
Winson05e46ca2016-02-05 15:40:29 -0800191 private Rect mTmpRect = new Rect();
192 private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
193 private List<TaskView> mTmpTaskViews = new ArrayList<>();
194 private TaskViewTransform mTmpTransform = new TaskViewTransform();
Winson59924fe2016-03-17 14:13:18 -0700195 private ArrayList<TaskViewTransform> mTmpTaskTransforms = new ArrayList<>();
Winsonc4387022016-02-24 12:05:26 -0800196 private int[] mTmpIntPair = new int[2];
Winson Chung931c51f2015-12-17 17:08:55 -0500197
Winson Chungbf5dbf12014-09-16 00:58:25 +0200198 // A convenience update listener to request updating clipping of tasks
Winsoneca4ab62015-11-04 10:50:28 -0800199 private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
Winson Chungbf5dbf12014-09-16 00:58:25 +0200200 new ValueAnimator.AnimatorUpdateListener() {
Winson5b7dd532015-12-01 16:02:12 -0800201 @Override
202 public void onAnimationUpdate(ValueAnimator animation) {
Winson55003902016-01-12 12:00:37 -0800203 if (!mTaskViewsClipDirty) {
204 mTaskViewsClipDirty = true;
205 invalidate();
206 }
Winson5b7dd532015-12-01 16:02:12 -0800207 }
208 };
Winson Chungbf5dbf12014-09-16 00:58:25 +0200209
Winsoneca4ab62015-11-04 10:50:28 -0800210 // The drop targets for a task drag
211 private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
212 @Override
Winson3e874742016-01-07 10:08:17 -0800213 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
214 // This drop target has a fixed bounds and should be checked last, so just fall through
215 // if it is the current target
216 if (!isCurrentTarget) {
217 return mLayoutAlgorithm.mFreeformRect.contains(x, y);
218 }
219 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800220 }
221 };
222
223 private DropTarget mStackDropTarget = new DropTarget() {
224 @Override
Winson3e874742016-01-07 10:08:17 -0800225 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
226 // This drop target has a fixed bounds and should be checked last, so just fall through
227 // if it is the current target
228 if (!isCurrentTarget) {
229 return mLayoutAlgorithm.mStackRect.contains(x, y);
230 }
231 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800232 }
233 };
234
Winson88737542016-02-17 13:27:33 -0800235 public TaskStackView(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800236 super(context);
Winsonde0591a2015-12-04 17:24:35 -0800237 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonbb410952015-12-04 14:34:11 -0800238 Resources res = context.getResources();
239
Winson Chungb0a28ea2014-10-28 15:21:35 -0700240 // Set the stack first
Winson88737542016-02-17 13:27:33 -0800241 mStack.setCallbacks(this);
Winson35f30502015-09-28 11:24:36 -0700242 mViewPool = new ViewPool<>(context, this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700243 mInflater = LayoutInflater.from(context);
Winson1c846142016-01-22 11:34:38 -0800244 mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
Winsonf9357d92016-03-25 15:14:37 -0700245 mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
Winson1c846142016-01-22 11:34:38 -0800246 mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
Winson35f30502015-09-28 11:24:36 -0700247 mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
Winsonf24f2162016-01-05 12:11:55 -0800248 mAnimationHelper = new TaskStackAnimationHelper(context, this);
Winsonbb410952015-12-04 14:34:11 -0800249 mTaskCornerRadiusPx = res.getDimensionPixelSize(
250 R.dimen.recents_task_view_rounded_corners_radius);
Winson3e874742016-01-07 10:08:17 -0800251 mDividerSize = ssp.getDockedDividerSize(context);
Winson35f30502015-09-28 11:24:36 -0700252
253 int taskBarDismissDozeDelaySeconds = getResources().getInteger(
254 R.integer.recents_task_bar_dismiss_delay_seconds);
255 mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
Winson Chunga26fb782014-06-12 17:52:39 -0700256 @Override
257 public void run() {
258 // Show the task bar dismiss buttons
Winson Chung6ac8bd62015-01-07 16:38:35 -0800259 List<TaskView> taskViews = getTaskViews();
260 int taskViewCount = taskViews.size();
261 for (int i = 0; i < taskViewCount; i++) {
262 TaskView tv = taskViews.get(i);
Winson Chung969f5862014-06-16 17:08:24 -0700263 tv.startNoUserInteractionAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700264 }
265 }
266 });
Winson Chung83ea6f72015-06-17 13:00:23 -0700267 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Winson36a5a2c2015-10-29 18:04:39 -0700268
Winsonde0591a2015-12-04 17:24:35 -0800269 mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
270 R.drawable.recents_freeform_workspace_bg);
Winsona78a8f32015-12-03 10:55:01 -0800271 mFreeformWorkspaceBackground.setCallback(this);
Winsonde0591a2015-12-04 17:24:35 -0800272 if (ssp.hasFreeformWorkspaceSupport()) {
Winson Chungaa4f8002015-12-17 10:27:55 -0500273 mFreeformWorkspaceBackground.setColor(
274 getContext().getColor(R.color.recents_freeform_workspace_bg_color));
Winsonde0591a2015-12-04 17:24:35 -0800275 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800276 }
277
Winsona1ededd2016-03-25 12:23:12 -0700278 @Override
279 protected void onAttachedToWindow() {
280 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
281 super.onAttachedToWindow();
282 readSystemFlags();
283 }
284
285 @Override
286 protected void onDetachedFromWindow() {
287 super.onDetachedFromWindow();
288 EventBus.getDefault().unregister(this);
289 }
290
Winson88737542016-02-17 13:27:33 -0800291 /**
Winsona1ededd2016-03-25 12:23:12 -0700292 * Called from RecentsActivity when it is relaunched.
Winson88737542016-02-17 13:27:33 -0800293 */
Winsona1ededd2016-03-25 12:23:12 -0700294 void onReload(boolean isResumingFromVisible) {
Winson88737542016-02-17 13:27:33 -0800295 if (!isResumingFromVisible) {
296 // Reset the focused task
297 resetFocusedTask(getFocusedTask());
298 }
299
300 // Reset the state of each of the task views
301 List<TaskView> taskViews = new ArrayList<>();
302 taskViews.addAll(getTaskViews());
303 taskViews.addAll(mViewPool.getViews());
304 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsona1ededd2016-03-25 12:23:12 -0700305 taskViews.get(i).onReload(isResumingFromVisible);
Winson88737542016-02-17 13:27:33 -0800306 }
307
308 // Reset the stack state
309 readSystemFlags();
310 mTaskViewsClipDirty = true;
311 mEnterAnimationComplete = false;
312 mUIDozeTrigger.stopDozing();
313 if (isResumingFromVisible) {
314 // Animate in the freeform workspace
315 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
316 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
317 Interpolators.FAST_OUT_SLOW_IN));
318 } else {
319 mStackScroller.reset();
Winsonf9357d92016-03-25 15:14:37 -0700320 mStableLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800321 mLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800322 }
Winson88737542016-02-17 13:27:33 -0800323
Winsona1ededd2016-03-25 12:23:12 -0700324 // Since we always animate to the same place in (the initial state), always reset the stack
325 // to the initial state when resuming
326 mAwaitingFirstLayout = true;
Winson619e40c2016-03-25 16:12:35 -0700327 mInitialState = INITIAL_STATE_UPDATE_ALL;
Winsona1ededd2016-03-25 12:23:12 -0700328 requestLayout();
Winsone6c90732015-09-24 16:06:29 -0700329 }
330
Winson88737542016-02-17 13:27:33 -0800331 /**
332 * Sets the stack tasks of this TaskStackView from the given TaskStack.
333 */
Winsona1ededd2016-03-25 12:23:12 -0700334 public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
Winson88737542016-02-17 13:27:33 -0800335 boolean isInitialized = mLayoutAlgorithm.isInitialized();
Winsona1ededd2016-03-25 12:23:12 -0700336 // Only notify if we are already initialized, otherwise, everything will pick up all the
337 // new and old tasks when we next layout
Winson88737542016-02-17 13:27:33 -0800338 mStack.setTasks(getContext(), stack.computeAllTasksList(),
Winsona1ededd2016-03-25 12:23:12 -0700339 allowNotifyStackChanges && isInitialized);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700340 }
341
Winson Chungd16c5652015-01-26 16:11:07 -0800342 /** Returns the task stack. */
Winsone693aaf2016-03-01 12:05:59 -0800343 public TaskStack getStack() {
Winson Chungd16c5652015-01-26 16:11:07 -0800344 return mStack;
345 }
346
Winsone693aaf2016-03-01 12:05:59 -0800347 /**
348 * Updates this TaskStackView to the initial state.
349 */
Winson619e40c2016-03-25 16:12:35 -0700350 public void updateToInitialState(boolean scrollToInitialState) {
351 if (scrollToInitialState) {
352 mStackScroller.setStackScrollToInitialState();
353 }
Winsone693aaf2016-03-01 12:05:59 -0800354 mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
355 }
356
Winson Chung6ac8bd62015-01-07 16:38:35 -0800357 /** Updates the list of task views */
358 void updateTaskViewsList() {
359 mTaskViews.clear();
360 int childCount = getChildCount();
361 for (int i = 0; i < childCount; i++) {
362 View v = getChildAt(i);
363 if (v instanceof TaskView) {
364 mTaskViews.add((TaskView) v);
365 }
366 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800367 }
368
369 /** Gets the list of task views */
370 List<TaskView> getTaskViews() {
Winsonf24f2162016-01-05 12:11:55 -0800371 return mTaskViews;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800372 }
373
Winson1b585612015-11-06 09:16:26 -0800374 /**
375 * Returns the front most task view.
376 *
377 * @param stackTasksOnly if set, will return the front most task view in the stack (by default
378 * the front most task view will be freeform since they are placed above
379 * stack tasks)
380 */
381 private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
382 List<TaskView> taskViews = getTaskViews();
383 int taskViewCount = taskViews.size();
384 for (int i = taskViewCount - 1; i >= 0; i--) {
385 TaskView tv = taskViews.get(i);
386 Task task = tv.getTask();
387 if (stackTasksOnly && task.isFreeformTask()) {
388 continue;
389 }
390 return tv;
391 }
392 return null;
393 }
394
395 /**
396 * Finds the child view given a specific {@param task}.
397 */
398 public TaskView getChildViewForTask(Task t) {
399 List<TaskView> taskViews = getTaskViews();
400 int taskViewCount = taskViews.size();
401 for (int i = 0; i < taskViewCount; i++) {
402 TaskView tv = taskViews.get(i);
403 if (tv.getTask() == t) {
404 return tv;
405 }
406 }
407 return null;
408 }
409
Winson Chungd7b2cb12014-06-26 15:08:50 -0700410 /** Returns the stack algorithm for this task stack. */
Winson36a5a2c2015-10-29 18:04:39 -0700411 public TaskStackLayoutAlgorithm getStackAlgorithm() {
Winson Chung012ef362014-07-31 18:36:25 -0700412 return mLayoutAlgorithm;
Winson Chung303e1ff2014-03-07 15:06:19 -0800413 }
414
Winson Chungc6a16232014-04-01 14:04:48 -0700415 /**
Winson8aa99592016-01-19 15:07:07 -0800416 * Adds a task to the ignored set.
Winson Chungc6a16232014-04-01 14:04:48 -0700417 */
Winson8aa99592016-01-19 15:07:07 -0800418 void addIgnoreTask(Task task) {
419 mIgnoreTasks.add(task.key);
420 }
421
422 /**
423 * Removes a task from the ignored set.
424 */
425 void removeIgnoreTask(Task task) {
426 mIgnoreTasks.remove(task.key);
427 }
428
429 /**
430 * Returns whether the specified {@param task} is ignored.
431 */
432 boolean isIgnoredTask(Task task) {
433 return mIgnoreTasks.contains(task.key);
434 }
435
436 /**
437 * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
438 * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
439 * visible range includes all tasks at the target stack scroll. This is useful for ensure that
440 * all views necessary for a transition or animation will be visible at the start.
441 *
442 * This call ignores freeform tasks.
443 *
444 * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
445 * match the size of {@param tasks}
446 * @param tasks The set of tasks for which to generate transforms
447 * @param curStackScroll The current stack scroll
448 * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
449 * The range of the union of the visible views at the current and
450 * target stack scrolls will be returned.
451 * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
452 * Transforms will still be calculated for the ignore tasks.
Winsonc4387022016-02-24 12:05:26 -0800453 * @return the front and back most visible task indices (there may be non visible tasks in
454 * between this range)
Winson8aa99592016-01-19 15:07:07 -0800455 */
Winsonc4387022016-02-24 12:05:26 -0800456 int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
Winson8aa99592016-01-19 15:07:07 -0800457 ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800458 ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
Winson Chungc6a16232014-04-01 14:04:48 -0700459 int taskCount = tasks.size();
Winsonc4387022016-02-24 12:05:26 -0800460 int[] visibleTaskRange = mTmpIntPair;
461 visibleTaskRange[0] = -1;
462 visibleTaskRange[1] = -1;
Winson8aa99592016-01-19 15:07:07 -0800463 boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
Winson Chungc9567c02014-06-16 20:25:51 -0700464
465 // We can reuse the task transforms where possible to reduce object allocation
Winsonc5fd3502016-01-18 15:18:37 -0800466 Utilities.matchTaskListSize(tasks, taskTransforms);
Winson Chungc9567c02014-06-16 20:25:51 -0700467
468 // Update the stack transforms
Winson4993c2f2015-11-19 10:06:06 -0800469 TaskViewTransform frontTransform = null;
Winson8aa99592016-01-19 15:07:07 -0800470 TaskViewTransform frontTransformAtTarget = null;
471 TaskViewTransform transform = null;
472 TaskViewTransform transformAtTarget = null;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700473 for (int i = taskCount - 1; i >= 0; i--) {
Winsona5e6b362015-11-02 17:17:20 -0800474 Task task = tasks.get(i);
Winson8aa99592016-01-19 15:07:07 -0800475
476 // Calculate the current and (if necessary) the target transform for the task
477 transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800478 taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800479 if (useTargetStackScroll && !transform.visible) {
480 // If we have a target stack scroll and the task is not currently visible, then we
481 // just update the transform at the new scroll
482 // TODO: Optimize this
483 transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
484 targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
485 if (transformAtTarget.visible) {
486 transform.copyFrom(transformAtTarget);
487 }
Winson3e874742016-01-07 10:08:17 -0800488 }
489
Winson8aa99592016-01-19 15:07:07 -0800490 // For ignore tasks, only calculate the stack transform and skip the calculation of the
491 // visible stack indices
492 if (ignoreTasksSet.contains(task.key)) {
493 continue;
494 }
Winsonf24f2162016-01-05 12:11:55 -0800495
496 // For freeform tasks, only calculate the stack transform and skip the calculation of
497 // the visible stack indices
Winsona5e6b362015-11-02 17:17:20 -0800498 if (task.isFreeformTask()) {
499 continue;
500 }
501
Winson4993c2f2015-11-19 10:06:06 -0800502 frontTransform = transform;
Winson8aa99592016-01-19 15:07:07 -0800503 frontTransformAtTarget = transformAtTarget;
Winsonc4387022016-02-24 12:05:26 -0800504 if (transform.visible) {
505 if (visibleTaskRange[0] < 0) {
506 visibleTaskRange[0] = i;
507 }
508 visibleTaskRange[1] = i;
509 }
Winson Chungc6a16232014-04-01 14:04:48 -0700510 }
Winsonc4387022016-02-24 12:05:26 -0800511 return visibleTaskRange;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700512 }
513
Winsonf24f2162016-01-05 12:11:55 -0800514 /**
Winson8aa99592016-01-19 15:07:07 -0800515 * Binds the visible {@link TaskView}s at the given target scroll.
Winsonf24f2162016-01-05 12:11:55 -0800516 */
Winson8aa99592016-01-19 15:07:07 -0800517 void bindVisibleTaskViews(float targetStackScroll) {
Winsone693aaf2016-03-01 12:05:59 -0800518 bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
519 }
520
521 void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
522 bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800523 }
524
525 /**
526 * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
527 * current {@link TaskStack}. This call does not continue on to update their position to the
528 * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
529 * be added/removed from the view hierarchy and placed in the correct Z order and initial
530 * position (if not currently on screen).
531 *
532 * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
533 * includes those visible at the current stack scroll, and all at the
534 * target stack scroll.
535 * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
536 * {@link TaskView}s
Winsone693aaf2016-03-01 12:05:59 -0800537 * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
538 * tasks at their non-overridden task progress
Winson8aa99592016-01-19 15:07:07 -0800539 */
Winsone693aaf2016-03-01 12:05:59 -0800540 void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
541 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800542 // Get all the task transforms
Winsonc4387022016-02-24 12:05:26 -0800543 ArrayList<Task> tasks = mStack.getStackTasks();
544 int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
Winsone693aaf2016-03-01 12:05:59 -0800545 mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
546 ignoreTaskOverrides);
Winsonf24f2162016-01-05 12:11:55 -0800547
548 // Return all the invisible children to the pool
Winson55003902016-01-12 12:00:37 -0800549 mTmpTaskViewMap.clear();
Winson8aa99592016-01-19 15:07:07 -0800550 List<TaskView> taskViews = getTaskViews();
551 int lastFocusedTaskIndex = -1;
552 int taskViewCount = taskViews.size();
Winsonf24f2162016-01-05 12:11:55 -0800553 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800554 TaskView tv = taskViews.get(i);
555 Task task = tv.getTask();
Winsonf24f2162016-01-05 12:11:55 -0800556
Winson3e874742016-01-07 10:08:17 -0800557 // Skip ignored tasks
Winson8aa99592016-01-19 15:07:07 -0800558 if (ignoreTasksSet.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800559 continue;
560 }
561
Winson1d5ff7e2016-03-04 11:21:09 -0800562 // It is possible for the set of lingering TaskViews to differ from the stack if the
563 // stack was updated before the relayout. If the task view is no longer in the stack,
564 // then just return it back to the view pool.
565 int taskIndex = mStack.indexOfStackTask(task);
566 TaskViewTransform transform = null;
567 if (taskIndex != -1) {
568 transform = mCurrentTaskTransforms.get(taskIndex);
569 }
570
571 if (task.isFreeformTask() || (transform != null && transform.visible)) {
Winson55003902016-01-12 12:00:37 -0800572 mTmpTaskViewMap.put(task.key, tv);
Winsonf24f2162016-01-05 12:11:55 -0800573 } else {
574 if (mTouchExplorationEnabled) {
575 lastFocusedTaskIndex = taskIndex;
576 resetFocusedTask(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800577 }
Winsonf24f2162016-01-05 12:11:55 -0800578 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -0800579 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800580 }
Winsonf24f2162016-01-05 12:11:55 -0800581
582 // Pick up all the newly visible children
Winsonc4387022016-02-24 12:05:26 -0800583 for (int i = tasks.size() - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800584 Task task = tasks.get(i);
585 TaskViewTransform transform = mCurrentTaskTransforms.get(i);
Winsonf24f2162016-01-05 12:11:55 -0800586
Winson3e874742016-01-07 10:08:17 -0800587 // Skip ignored tasks
Winson8aa99592016-01-19 15:07:07 -0800588 if (ignoreTasksSet.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800589 continue;
590 }
591
Winsonf24f2162016-01-05 12:11:55 -0800592 // Skip the invisible non-freeform stack tasks
Winson05e46ca2016-02-05 15:40:29 -0800593 if (!task.isFreeformTask() && !transform.visible) {
Winsonf24f2162016-01-05 12:11:55 -0800594 continue;
595 }
596
Winson55003902016-01-12 12:00:37 -0800597 TaskView tv = mTmpTaskViewMap.get(task.key);
Winsonf24f2162016-01-05 12:11:55 -0800598 if (tv == null) {
599 tv = mViewPool.pickUpViewFromPool(task, task);
600 if (task.isFreeformTask()) {
Winsonbe8e6962016-02-01 14:27:52 -0800601 tv.updateViewPropertiesToTaskTransform(transform, AnimationProps.IMMEDIATE,
Winsonf24f2162016-01-05 12:11:55 -0800602 mRequestUpdateClippingListener);
603 } else {
Winson68088812016-02-12 16:06:04 -0800604 if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
Winsonf24f2162016-01-05 12:11:55 -0800605 tv.updateViewPropertiesToTaskTransform(
606 mLayoutAlgorithm.getBackOfStackTransform(),
Winsonbe8e6962016-02-01 14:27:52 -0800607 AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
Winsonf24f2162016-01-05 12:11:55 -0800608 } else {
609 tv.updateViewPropertiesToTaskTransform(
610 mLayoutAlgorithm.getFrontOfStackTransform(),
Winsonbe8e6962016-02-01 14:27:52 -0800611 AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
Winsonf24f2162016-01-05 12:11:55 -0800612 }
613 }
614 } else {
615 // Reattach it in the right z order
616 final int taskIndex = mStack.indexOfStackTask(task);
617 final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
618 if (insertIndex != getTaskViews().indexOf(tv)){
619 detachViewFromParent(tv);
620 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
621 updateTaskViewsList();
622 }
623 }
624 }
625
626 // Update the focus if the previous focused task was returned to the view pool
627 if (lastFocusedTaskIndex != -1) {
Winsonc4387022016-02-24 12:05:26 -0800628 if (lastFocusedTaskIndex < visibleTaskRange[1]) {
629 setFocusedTask(visibleTaskRange[1], false /* scrollToTask */,
Winsonf24f2162016-01-05 12:11:55 -0800630 true /* requestViewFocus */);
631 } else {
Winsonc4387022016-02-24 12:05:26 -0800632 setFocusedTask(visibleTaskRange[0], false /* scrollToTask */,
Winsonf24f2162016-01-05 12:11:55 -0800633 true /* requestViewFocus */);
634 }
635 }
636 }
637
638 /**
Winson59924fe2016-03-17 14:13:18 -0700639 * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
Winsonf24f2162016-01-05 12:11:55 -0800640 */
Winsonbe8e6962016-02-01 14:27:52 -0800641 void relayoutTaskViews(AnimationProps animation) {
Winson59924fe2016-03-17 14:13:18 -0700642 relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
643 }
644
645 /**
646 * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
647 */
648 void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet) {
649 relayoutTaskViews(animation, ignoreTasksSet, false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800650 }
Winson3e874742016-01-07 10:08:17 -0800651
Winson8aa99592016-01-19 15:07:07 -0800652 /**
653 * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
654 * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
655 * animations that are current running on those task views, and will ensure that the children
656 * {@link TaskView}s will match the set of visible tasks in the stack.
657 *
658 * @param ignoreTasksSet the set of tasks to ignore in the relayout
659 */
Winson59924fe2016-03-17 14:13:18 -0700660 void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
661 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800662 // If we had a deferred animation, cancel that
Winson8aa99592016-01-19 15:07:07 -0800663 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800664
Winson8aa99592016-01-19 15:07:07 -0800665 // Synchronize the current set of TaskViews
Winsone693aaf2016-03-01 12:05:59 -0800666 bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
Winson59924fe2016-03-17 14:13:18 -0700667 ignoreTaskOverrides /* ignoreTaskOverrides */);
Winsonf24f2162016-01-05 12:11:55 -0800668
669 // Animate them to their final transforms with the given animation
670 List<TaskView> taskViews = getTaskViews();
671 int taskViewCount = taskViews.size();
672 for (int i = 0; i < taskViewCount; i++) {
Winson43336942016-03-07 14:52:59 -0800673 TaskView tv = taskViews.get(i);
674 int taskIndex = mStack.indexOfStackTask(tv.getTask());
675 TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
Winsonf24f2162016-01-05 12:11:55 -0800676
Winson8aa99592016-01-19 15:07:07 -0800677 if (ignoreTasksSet.contains(tv.getTask().key)) {
Winson3e874742016-01-07 10:08:17 -0800678 continue;
679 }
680
Winsonf24f2162016-01-05 12:11:55 -0800681 updateTaskViewToTransform(tv, transform, animation);
682 }
683 }
684
685 /**
686 * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
687 */
Winsonbe8e6962016-02-01 14:27:52 -0800688 void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
Winson8aa99592016-01-19 15:07:07 -0800689 mDeferredTaskViewLayoutAnimation = animation;
Winson1c846142016-01-22 11:34:38 -0800690 invalidate();
Winsonf24f2162016-01-05 12:11:55 -0800691 }
692
693 /**
694 * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
Winsonbe8e6962016-02-01 14:27:52 -0800695 * given set of {@link AnimationProps} properties.
Winsonf24f2162016-01-05 12:11:55 -0800696 */
697 public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
Winsonbe8e6962016-02-01 14:27:52 -0800698 AnimationProps animation) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500699 if (taskView.isAnimatingTo(transform)) {
700 return;
701 }
702 taskView.cancelTransformAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800703 taskView.updateViewPropertiesToTaskTransform(transform, animation,
704 mRequestUpdateClippingListener);
705 }
706
707 /**
Winson8aa99592016-01-19 15:07:07 -0800708 * Returns the current task transforms of all tasks, falling back to the stack layout if there
709 * is no {@link TaskView} for the task.
Winsonf24f2162016-01-05 12:11:55 -0800710 */
Winson8aa99592016-01-19 15:07:07 -0800711 public void getCurrentTaskTransforms(ArrayList<Task> tasks,
712 ArrayList<TaskViewTransform> transformsOut) {
713 Utilities.matchTaskListSize(tasks, transformsOut);
Winson66474132016-02-23 18:45:47 -0800714 int focusState = mLayoutAlgorithm.getFocusState();
Winson8aa99592016-01-19 15:07:07 -0800715 for (int i = tasks.size() - 1; i >= 0; i--) {
716 Task task = tasks.get(i);
717 TaskViewTransform transform = transformsOut.get(i);
718 TaskView tv = getChildViewForTask(task);
719 if (tv != null) {
720 transform.fillIn(tv);
721 } else {
722 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
Winsone693aaf2016-03-01 12:05:59 -0800723 focusState, transform, null, true /* forceUpdate */,
724 false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800725 }
726 transform.visible = true;
727 }
728 }
729
730 /**
731 * Returns the task transforms for all the tasks in the stack if the stack was at the given
Winson14991502016-02-15 15:40:08 -0800732 * {@param stackScroll} and {@param focusState}.
Winson8aa99592016-01-19 15:07:07 -0800733 */
Winson66474132016-02-23 18:45:47 -0800734 public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
Winson8aa99592016-01-19 15:07:07 -0800735 ArrayList<TaskViewTransform> transformsOut) {
736 Utilities.matchTaskListSize(tasks, transformsOut);
737 for (int i = tasks.size() - 1; i >= 0; i--) {
738 Task task = tasks.get(i);
739 TaskViewTransform transform = transformsOut.get(i);
Winson14991502016-02-15 15:40:08 -0800740 mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
Winsone693aaf2016-03-01 12:05:59 -0800741 true /* forceUpdate */, true /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800742 transform.visible = true;
743 }
744 }
745
746 /**
Winson05e46ca2016-02-05 15:40:29 -0800747 * Cancels the next deferred task view layout.
748 */
749 void cancelDeferredTaskViewLayoutAnimation() {
750 mDeferredTaskViewLayoutAnimation = null;
751 }
752
753 /**
Winson8aa99592016-01-19 15:07:07 -0800754 * Cancels all {@link TaskView} animations.
755 *
756 * @see #cancelAllTaskViewAnimations(ArraySet<Task.TaskKey>)
757 */
758 void cancelAllTaskViewAnimations() {
759 cancelAllTaskViewAnimations(mIgnoreTasks);
760 }
761
762 /**
763 * Cancels all {@link TaskView} animations.
764 *
765 * @param ignoreTasksSet The set of tasks to continue running their animations.
766 */
767 void cancelAllTaskViewAnimations(ArraySet<Task.TaskKey> ignoreTasksSet) {
Winsonf24f2162016-01-05 12:11:55 -0800768 List<TaskView> taskViews = getTaskViews();
Winson3e874742016-01-07 10:08:17 -0800769 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsonf24f2162016-01-05 12:11:55 -0800770 final TaskView tv = taskViews.get(i);
Winson8aa99592016-01-19 15:07:07 -0800771 if (!ignoreTasksSet.contains(tv.getTask().key)) {
772 tv.cancelTransformAnimation();
773 }
Winsonf24f2162016-01-05 12:11:55 -0800774 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800775 }
776
Winson3150e572015-10-23 15:07:24 -0700777 /**
778 * Updates the clip for each of the task views from back to front.
779 */
Winsonf24f2162016-01-05 12:11:55 -0800780 private void clipTaskViews() {
Winson Chung93748a12014-07-13 17:43:31 -0700781 // Update the clip on each task child
Winson Chung6ac8bd62015-01-07 16:38:35 -0800782 List<TaskView> taskViews = getTaskViews();
Winson3150e572015-10-23 15:07:24 -0700783 TaskView tmpTv = null;
Winson8aa99592016-01-19 15:07:07 -0800784 TaskView prevVisibleTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800785 int taskViewCount = taskViews.size();
Winson3150e572015-10-23 15:07:24 -0700786 for (int i = 0; i < taskViewCount; i++) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800787 TaskView tv = taskViews.get(i);
Winson3150e572015-10-23 15:07:24 -0700788 TaskView frontTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800789 int clipBottom = 0;
Winson8aa99592016-01-19 15:07:07 -0800790
Winson05e46ca2016-02-05 15:40:29 -0800791 if (isIgnoredTask(tv.getTask())) {
Winson8aa99592016-01-19 15:07:07 -0800792 // For each of the ignore tasks, update the translationZ of its TaskView to be
793 // between the translationZ of the tasks immediately underneath it
794 if (prevVisibleTv != null) {
795 tv.setTranslationZ(Math.max(tv.getTranslationZ(),
796 prevVisibleTv.getTranslationZ() + 0.1f));
797 }
798 }
799
Winson3150e572015-10-23 15:07:24 -0700800 if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800801 // Find the next view to clip against
Winson3150e572015-10-23 15:07:24 -0700802 for (int j = i + 1; j < taskViewCount; j++) {
803 tmpTv = taskViews.get(j);
Winsonef064132016-01-05 12:11:31 -0800804
Winson3150e572015-10-23 15:07:24 -0700805 if (tmpTv.shouldClipViewInStack()) {
806 frontTv = tmpTv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800807 break;
Winson Chung93748a12014-07-13 17:43:31 -0700808 }
809 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800810
811 // Clip against the next view, this is just an approximation since we are
812 // stacked and we can make assumptions about the visibility of the this
813 // task relative to the ones in front of it.
Winson3150e572015-10-23 15:07:24 -0700814 if (frontTv != null) {
Winsonbb410952015-12-04 14:34:11 -0800815 float taskBottom = tv.getBottom();
816 float frontTaskTop = frontTv.getTop();
Winson3150e572015-10-23 15:07:24 -0700817 if (frontTaskTop < taskBottom) {
818 // Map the stack view space coordinate (the rects) to view space
Winsonbb410952015-12-04 14:34:11 -0800819 clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
Winson3150e572015-10-23 15:07:24 -0700820 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800821 }
Winson Chung93748a12014-07-13 17:43:31 -0700822 }
Winsonf24f2162016-01-05 12:11:55 -0800823 tv.getViewBounds().setClipBottom(clipBottom);
Winsone693aaf2016-03-01 12:05:59 -0800824 tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
Winson8aa99592016-01-19 15:07:07 -0800825 prevVisibleTv = tv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800826 }
Winsonf24f2162016-01-05 12:11:55 -0800827 mTaskViewsClipDirty = false;
Winson Chung93748a12014-07-13 17:43:31 -0700828 }
829
Winson3e874742016-01-07 10:08:17 -0800830 /**
Winson8aa99592016-01-19 15:07:07 -0800831 * Updates the layout algorithm min and max virtual scroll bounds.
832 *
833 * @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
834 */
Winson003eda62016-03-11 14:56:00 -0800835 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
Winson8aa99592016-01-19 15:07:07 -0800836 updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
837 }
838
839 /**
Winson3e874742016-01-07 10:08:17 -0800840 * Updates the min and max virtual scroll bounds.
841 *
Winson8aa99592016-01-19 15:07:07 -0800842 * @param ignoreTasksSet the set of tasks to ignore in the relayout
Winson3e874742016-01-07 10:08:17 -0800843 */
Winson003eda62016-03-11 14:56:00 -0800844 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
Winson8aa99592016-01-19 15:07:07 -0800845 ArraySet<Task.TaskKey> ignoreTasksSet) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800846 // Compute the min and max scroll values
Winson3e874742016-01-07 10:08:17 -0800847 mLayoutAlgorithm.update(mStack, ignoreTasksSet);
Winson Chung303e1ff2014-03-07 15:06:19 -0800848
Winson8aa99592016-01-19 15:07:07 -0800849 // Update the freeform workspace background
Winsona5e6b362015-11-02 17:17:20 -0800850 SystemServicesProxy ssp = Recents.getSystemServices();
851 if (ssp.hasFreeformWorkspaceSupport()) {
852 mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
Winsona5e6b362015-11-02 17:17:20 -0800853 mFreeformWorkspaceBackground.setBounds(mTmpRect);
854 }
855
Winson Chung303e1ff2014-03-07 15:06:19 -0800856 if (boundScrollToNewMinMax) {
Winson Chung012ef362014-07-31 18:36:25 -0700857 mStackScroller.boundScroll();
Winson Chung303e1ff2014-03-07 15:06:19 -0800858 }
859 }
860
Winson Chung012ef362014-07-31 18:36:25 -0700861 /** Returns the scroller. */
862 public TaskStackViewScroller getScroller() {
863 return mStackScroller;
864 }
865
Winson0d14d4d2015-10-26 17:05:04 -0700866 /**
867 * Sets the focused task to the provided (bounded taskIndex).
Winsone5f1faa2015-11-20 12:26:23 -0800868 *
869 * @return whether or not the stack will scroll as a part of this focus change
Winson0d14d4d2015-10-26 17:05:04 -0700870 */
Winsonf24f2162016-01-05 12:11:55 -0800871 private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
Winsonaaf33bc2015-12-03 12:02:38 -0800872 final boolean requestViewFocus) {
Winson4b9cded2016-01-26 16:26:47 -0800873 return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800874 }
875
876 /**
Winson05e46ca2016-02-05 15:40:29 -0800877 * Sets the focused task to the provided (bounded focusTaskIndex).
Peter Schillerb124d562015-12-11 21:31:17 -0800878 *
879 * @return whether or not the stack will scroll as a part of this focus change
880 */
Winson05e46ca2016-02-05 15:40:29 -0800881 private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
882 boolean requestViewFocus, int timerIndicatorDuration) {
Winson0d14d4d2015-10-26 17:05:04 -0700883 // Find the next task to focus
Winson4b057c62016-01-12 15:01:52 -0800884 int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
Winson68088812016-02-12 16:06:04 -0800885 Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
Winson0d14d4d2015-10-26 17:05:04 -0700886 final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
Winson250608a2015-11-24 15:00:31 -0800887 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700888
Winson0d14d4d2015-10-26 17:05:04 -0700889 // Reset the last focused task state if changed
Winsonaaf33bc2015-12-03 12:02:38 -0800890 if (mFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800891 // Cancel the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800892 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800893 final TaskView tv = getChildViewForTask(mFocusedTask);
894 if (tv != null) {
895 tv.getHeaderView().cancelFocusTimerIndicator();
896 }
897 }
Winsonb433c5b2016-01-20 17:11:29 -0800898
899 resetFocusedTask(mFocusedTask);
Winson0d14d4d2015-10-26 17:05:04 -0700900 }
901
Winsone5f1faa2015-11-20 12:26:23 -0800902 boolean willScroll = false;
Winsonaaf33bc2015-12-03 12:02:38 -0800903 mFocusedTask = newFocusedTask;
Peter Schillerb124d562015-12-11 21:31:17 -0800904
Winsonaaf33bc2015-12-03 12:02:38 -0800905 if (newFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800906 // Start the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800907 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800908 final TaskView tv = getChildViewForTask(mFocusedTask);
909 if (tv != null) {
Winson4b9cded2016-01-26 16:26:47 -0800910 tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
Peter Schillerb124d562015-12-11 21:31:17 -0800911 } else {
912 // The view is null; set a flag for later
Winson4b9cded2016-01-26 16:26:47 -0800913 mStartTimerIndicatorDuration = timerIndicatorDuration;
Peter Schillerb124d562015-12-11 21:31:17 -0800914 }
915 }
916
Winson0d14d4d2015-10-26 17:05:04 -0700917 if (scrollToTask) {
Winson1c846142016-01-22 11:34:38 -0800918 // Cancel any running enter animations at this point when we scroll or change focus
919 if (!mEnterAnimationComplete) {
920 cancelAllTaskViewAnimations();
921 }
922
Winsone693aaf2016-03-01 12:05:59 -0800923 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winson05e46ca2016-02-05 15:40:29 -0800924 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
925 requestViewFocus);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700926 } else {
Winson05e46ca2016-02-05 15:40:29 -0800927 // Focus the task view
928 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
929 if (newFocusedTaskView != null) {
930 newFocusedTaskView.setFocusedState(true, requestViewFocus);
931 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700932 }
933 }
Winsone5f1faa2015-11-20 12:26:23 -0800934 return willScroll;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700935 }
936
Winson Chungd213a1e2014-10-02 11:18:30 -0700937 /**
Winson0d14d4d2015-10-26 17:05:04 -0700938 * Sets the focused task relative to the currently focused task.
939 *
Winsone5f1faa2015-11-20 12:26:23 -0800940 * @param forward whether to go to the next task in the stack (along the curve) or the previous
Winson1b585612015-11-06 09:16:26 -0800941 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
942 * if the currently focused task is not a stack task, will set the focus
943 * to the first visible stack task
Winson0d14d4d2015-10-26 17:05:04 -0700944 * @param animated determines whether to actually draw the highlight along with the change in
945 * focus.
Winson Chungd213a1e2014-10-02 11:18:30 -0700946 */
Winson1b585612015-11-06 09:16:26 -0800947 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
Winsone5f1faa2015-11-20 12:26:23 -0800948 setRelativeFocusedTask(forward, stackTasksOnly, animated, false);
949 }
950
951 /**
952 * Sets the focused task relative to the currently focused task.
953 *
954 * @param forward whether to go to the next task in the stack (along the curve) or the previous
955 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
956 * if the currently focused task is not a stack task, will set the focus
957 * to the first visible stack task
958 * @param animated determines whether to actually draw the highlight along with the change in
959 * focus.
960 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
Peter Schillerb124d562015-12-11 21:31:17 -0800961 * happens.
Winsone5f1faa2015-11-20 12:26:23 -0800962 */
963 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
964 boolean cancelWindowAnimations) {
Winson4b9cded2016-01-26 16:26:47 -0800965 setRelativeFocusedTask(forward, stackTasksOnly, animated, cancelWindowAnimations, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800966 }
967
968 /**
969 * Sets the focused task relative to the currently focused task.
970 *
971 * @param forward whether to go to the next task in the stack (along the curve) or the previous
972 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
973 * if the currently focused task is not a stack task, will set the focus
974 * to the first visible stack task
975 * @param animated determines whether to actually draw the highlight along with the change in
976 * focus.
977 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
978 * happens.
Winson4b9cded2016-01-26 16:26:47 -0800979 * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
Peter Schillerb124d562015-12-11 21:31:17 -0800980 */
981 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
Winson4b9cded2016-01-26 16:26:47 -0800982 boolean cancelWindowAnimations,
983 int timerIndicatorDuration) {
Winsonaaf33bc2015-12-03 12:02:38 -0800984 int newIndex = mStack.indexOfStackTask(mFocusedTask);
985 if (mFocusedTask != null) {
Winson1b585612015-11-06 09:16:26 -0800986 if (stackTasksOnly) {
Winson250608a2015-11-24 15:00:31 -0800987 List<Task> tasks = mStack.getStackTasks();
Winsonaaf33bc2015-12-03 12:02:38 -0800988 if (mFocusedTask.isFreeformTask()) {
Winson1b585612015-11-06 09:16:26 -0800989 // Try and focus the front most stack task
990 TaskView tv = getFrontMostTaskView(stackTasksOnly);
991 if (tv != null) {
Winson250608a2015-11-24 15:00:31 -0800992 newIndex = mStack.indexOfStackTask(tv.getTask());
Winson1b585612015-11-06 09:16:26 -0800993 }
994 } else {
995 // Try the next task if it is a stack task
Winsonaaf33bc2015-12-03 12:02:38 -0800996 int tmpNewIndex = newIndex + (forward ? -1 : 1);
Winson1b585612015-11-06 09:16:26 -0800997 if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
998 Task t = tasks.get(tmpNewIndex);
999 if (!t.isFreeformTask()) {
1000 newIndex = tmpNewIndex;
1001 }
1002 }
1003 }
1004 } else {
Winson8b1871d2015-11-20 09:56:20 -08001005 // No restrictions, lets just move to the new task (looping forward/backwards if
1006 // necessary)
Winson4b057c62016-01-12 15:01:52 -08001007 int taskCount = mStack.getTaskCount();
Winsonaaf33bc2015-12-03 12:02:38 -08001008 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
Winson1b585612015-11-06 09:16:26 -08001009 }
1010 } else {
Winson23b0d3f2016-02-15 17:43:01 -08001011 // We don't have a focused task
1012 float stackScroll = mStackScroller.getStackScroll();
1013 ArrayList<Task> tasks = mStack.getStackTasks();
1014 int taskCount = tasks.size();
1015 if (forward) {
1016 // Walk backwards and focus the next task smaller than the current stack scroll
1017 for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
1018 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1019 if (Float.compare(taskP, stackScroll) <= 0) {
1020 break;
1021 }
1022 }
1023 } else {
1024 // Walk forwards and focus the next task larger than the current stack scroll
1025 for (newIndex = 0; newIndex < taskCount; newIndex++) {
1026 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1027 if (Float.compare(taskP, stackScroll) >= 0) {
1028 break;
1029 }
1030 }
Winson1b585612015-11-06 09:16:26 -08001031 }
1032 }
1033 if (newIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001034 boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
Winson4b9cded2016-01-26 16:26:47 -08001035 true /* requestViewFocus */, timerIndicatorDuration);
Winsone5f1faa2015-11-20 12:26:23 -08001036 if (willScroll && cancelWindowAnimations) {
1037 // As we iterate to the next/previous task, cancel any current/lagging window
1038 // transition animations
1039 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1040 }
Winson1b585612015-11-06 09:16:26 -08001041 }
Winson Chunga0e88b52014-08-11 19:25:42 -07001042 }
1043
Winson0d14d4d2015-10-26 17:05:04 -07001044 /**
1045 * Resets the focused task.
1046 */
Winsona0731a12015-12-02 15:10:14 -08001047 void resetFocusedTask(Task task) {
1048 if (task != null) {
1049 TaskView tv = getChildViewForTask(task);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001050 if (tv != null) {
Winsonf24f2162016-01-05 12:11:55 -08001051 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001052 }
Winson Chungb0a28ea2014-10-28 15:21:35 -07001053 }
Winsonaaf33bc2015-12-03 12:02:38 -08001054 mFocusedTask = null;
Winson Chungb0a28ea2014-10-28 15:21:35 -07001055 }
1056
Winson142af422015-11-09 10:39:57 -08001057 /**
1058 * Returns the focused task.
1059 */
1060 Task getFocusedTask() {
Winsonaaf33bc2015-12-03 12:02:38 -08001061 return mFocusedTask;
Winson142af422015-11-09 10:39:57 -08001062 }
1063
Winson Chung303e1ff2014-03-07 15:06:19 -08001064 @Override
Winson Chungee445952014-09-09 16:12:59 +02001065 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1066 super.onInitializeAccessibilityEvent(event);
Winson Chung6ac8bd62015-01-07 16:38:35 -08001067 List<TaskView> taskViews = getTaskViews();
1068 int taskViewCount = taskViews.size();
1069 if (taskViewCount > 0) {
1070 TaskView backMostTask = taskViews.get(0);
1071 TaskView frontMostTask = taskViews.get(taskViewCount - 1);
Winson250608a2015-11-24 15:00:31 -08001072 event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
1073 event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
Winson Chung296278a2015-12-17 12:09:02 -05001074 event.setContentDescription(frontMostTask.getTask().title);
Winson Chungee445952014-09-09 16:12:59 +02001075 }
Winson4b057c62016-01-12 15:01:52 -08001076 event.setItemCount(mStack.getTaskCount());
Winson59924fe2016-03-17 14:13:18 -07001077
1078 int stackHeight = mLayoutAlgorithm.mStackRect.height();
1079 event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
1080 event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
Winson Chungee445952014-09-09 16:12:59 +02001081 }
1082
1083 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001084 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1085 super.onInitializeAccessibilityNodeInfo(info);
1086 List<TaskView> taskViews = getTaskViews();
1087 int taskViewCount = taskViews.size();
Winsonaaf33bc2015-12-03 12:02:38 -08001088 if (taskViewCount > 1 && mFocusedTask != null) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001089 info.setScrollable(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001090 int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask);
1091 if (focusedTaskIndex > 0) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001092 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1093 }
Winson4b057c62016-01-12 15:01:52 -08001094 if (focusedTaskIndex < mStack.getTaskCount() - 1) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001095 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1096 }
1097 }
1098 }
1099
1100 @Override
Winson Chung4ab4d832015-12-11 10:25:46 -05001101 protected Parcelable onSaveInstanceState() {
1102 Bundle savedState = new Bundle();
1103 savedState.putParcelable(KEY_SAVED_STATE_SUPER, super.onSaveInstanceState());
Winson66474132016-02-23 18:45:47 -08001104 savedState.putInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState());
Winson Chung4ab4d832015-12-11 10:25:46 -05001105 savedState.putFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL, mStackScroller.getStackScroll());
1106 return super.onSaveInstanceState();
1107 }
1108
1109 @Override
1110 protected void onRestoreInstanceState(Parcelable state) {
1111 Bundle savedState = (Bundle) state;
1112 super.onRestoreInstanceState(savedState.getParcelable(KEY_SAVED_STATE_SUPER));
1113
Winson66474132016-02-23 18:45:47 -08001114 mLayoutAlgorithm.setFocusState(savedState.getInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE));
Winson Chung4ab4d832015-12-11 10:25:46 -05001115 mStackScroller.setStackScroll(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL));
1116 }
1117
1118 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001119 public CharSequence getAccessibilityClassName() {
1120 return TaskStackView.class.getName();
1121 }
1122
1123 @Override
1124 public boolean performAccessibilityAction(int action, Bundle arguments) {
1125 if (super.performAccessibilityAction(action, arguments)) {
1126 return true;
1127 }
Winson0d14d4d2015-10-26 17:05:04 -07001128 switch (action) {
1129 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
Winson1b585612015-11-06 09:16:26 -08001130 setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */);
Winson0d14d4d2015-10-26 17:05:04 -07001131 return true;
1132 }
1133 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
Winson1b585612015-11-06 09:16:26 -08001134 setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */);
Winson0d14d4d2015-10-26 17:05:04 -07001135 return true;
Winson Chung83ea6f72015-06-17 13:00:23 -07001136 }
1137 }
1138 return false;
1139 }
1140
1141 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -08001142 public boolean onInterceptTouchEvent(MotionEvent ev) {
1143 return mTouchHandler.onInterceptTouchEvent(ev);
1144 }
1145
1146 @Override
1147 public boolean onTouchEvent(MotionEvent ev) {
1148 return mTouchHandler.onTouchEvent(ev);
1149 }
1150
1151 @Override
Winson Chungd213a1e2014-10-02 11:18:30 -07001152 public boolean onGenericMotionEvent(MotionEvent ev) {
1153 return mTouchHandler.onGenericMotionEvent(ev);
1154 }
1155
1156 @Override
Winson Chungd7b2cb12014-06-26 15:08:50 -07001157 public void computeScroll() {
Winsonf24f2162016-01-05 12:11:55 -08001158 if (mStackScroller.computeScroll()) {
1159 // Notify accessibility
1160 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
1161 }
Winson8aa99592016-01-19 15:07:07 -08001162 if (mDeferredTaskViewLayoutAnimation != null) {
1163 relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
Winsonf24f2162016-01-05 12:11:55 -08001164 mTaskViewsClipDirty = true;
Winson8aa99592016-01-19 15:07:07 -08001165 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -08001166 }
1167 if (mTaskViewsClipDirty) {
1168 clipTaskViews();
1169 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001170 }
1171
Winson3e874742016-01-07 10:08:17 -08001172 /**
Winson8aa99592016-01-19 15:07:07 -08001173 * Computes the maximum number of visible tasks and thumbnails. Requires that
Winsoneca4ab62015-11-04 10:50:28 -08001174 * updateLayoutForStack() is called first.
Winson Chunga91c2932014-11-07 15:02:38 -08001175 */
Winson36a5a2c2015-10-29 18:04:39 -07001176 public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
Winson250608a2015-11-24 15:00:31 -08001177 return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
Winson Chunga91c2932014-11-07 15:02:38 -08001178 }
1179
Winson3e874742016-01-07 10:08:17 -08001180 /**
Winson59924fe2016-03-17 14:13:18 -07001181 * Updates the system insets.
Winson3e874742016-01-07 10:08:17 -08001182 */
Winson59924fe2016-03-17 14:13:18 -07001183 public void setSystemInsets(Rect systemInsets) {
Winson49df4202016-01-25 17:33:34 -08001184 if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
Winsonf9357d92016-03-25 15:14:37 -07001185 mStableLayoutAlgorithm.setSystemInsets(systemInsets);
Winson49df4202016-01-25 17:33:34 -08001186 mLayoutAlgorithm.setSystemInsets(systemInsets);
Winson49df4202016-01-25 17:33:34 -08001187 requestLayout();
1188 }
Winson147ecaf2015-09-16 16:49:55 -07001189 }
1190
Winson Chunga91c2932014-11-07 15:02:38 -08001191 /**
Winson Chunga4ccb862014-08-22 15:26:27 -07001192 * This is called with the full window width and height to allow stack view children to
Winson Chungdcfa7972014-07-22 12:27:13 -07001193 * perform the full screen transition down.
Winson Chungf7bca432014-04-30 17:11:13 -07001194 */
Winson Chung303e1ff2014-03-07 15:06:19 -08001195 @Override
1196 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson70f0bf72016-02-01 14:05:29 -08001197 mInMeasureLayout = true;
Winson Chung303e1ff2014-03-07 15:06:19 -08001198 int width = MeasureSpec.getSize(widthMeasureSpec);
1199 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -08001200
Winson59924fe2016-03-17 14:13:18 -07001201 // Update the stable stack bounds, but only update the current stack bounds if the stable
1202 // bounds have changed. This is because we may get spurious measures while dragging where
1203 // our current stack bounds reflect the target drop region.
1204 mLayoutAlgorithm.getTaskStackBounds(new Rect(0, 0, width, height),
Winson008ee15f2016-03-18 17:17:25 -07001205 mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
Winson59924fe2016-03-17 14:13:18 -07001206 if (!mTmpRect.equals(mStableStackBounds)) {
1207 mStableStackBounds.set(mTmpRect);
1208 mStackBounds.set(mTmpRect);
1209 mStableWindowRect.set(0, 0, width, height);
1210 mWindowRect.set(0, 0, width, height);
1211 }
1212
Winson8aa99592016-01-19 15:07:07 -08001213 // Compute the rects in the stack algorithm
Winsonf9357d92016-03-25 15:14:37 -07001214 mStableLayoutAlgorithm.initialize(mStableWindowRect, mStableStackBounds,
1215 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winson59924fe2016-03-17 14:13:18 -07001216 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001217 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1218 updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
Winson Chung303e1ff2014-03-07 15:06:19 -08001219
Winsonf24f2162016-01-05 12:11:55 -08001220 // If this is the first layout, then scroll to the front of the stack, then update the
1221 // TaskViews with the stack so that we can lay them out
Winson619e40c2016-03-25 16:12:35 -07001222 if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE) {
1223 updateToInitialState(mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY);
1224 mInitialState = INITIAL_STATE_UPDATE_NONE;
Winson Chung303e1ff2014-03-07 15:06:19 -08001225 }
Winsone693aaf2016-03-01 12:05:59 -08001226
Winson8aa99592016-01-19 15:07:07 -08001227 // Rebind all the views, including the ignore ones
Winsone693aaf2016-03-01 12:05:59 -08001228 bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
1229 false /* ignoreTaskOverrides */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001230
Winson Chungdcfa7972014-07-22 12:27:13 -07001231 // Measure each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001232 mTmpTaskViews.clear();
1233 mTmpTaskViews.addAll(getTaskViews());
1234 mTmpTaskViews.addAll(mViewPool.getViews());
1235 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001236 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001237 measureTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001238 }
1239
1240 setMeasuredDimension(width, height);
Winson70f0bf72016-02-01 14:05:29 -08001241 mInMeasureLayout = false;
1242 }
1243
1244 /**
1245 * Measures a TaskView.
1246 */
1247 private void measureTaskView(TaskView tv) {
1248 if (tv.getBackground() != null) {
1249 tv.getBackground().getPadding(mTmpRect);
1250 } else {
1251 mTmpRect.setEmpty();
1252 }
Winsonf9357d92016-03-25 15:14:37 -07001253 Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
Winson70f0bf72016-02-01 14:05:29 -08001254 tv.measure(
Winsonf9357d92016-03-25 15:14:37 -07001255 MeasureSpec.makeMeasureSpec(taskRect.width() + mTmpRect.left + mTmpRect.right,
Winson70f0bf72016-02-01 14:05:29 -08001256 MeasureSpec.EXACTLY),
Winsonf9357d92016-03-25 15:14:37 -07001257 MeasureSpec.makeMeasureSpec(taskRect.height() + mTmpRect.top + mTmpRect.bottom,
Winson70f0bf72016-02-01 14:05:29 -08001258 MeasureSpec.EXACTLY));
Winson Chung303e1ff2014-03-07 15:06:19 -08001259 }
1260
1261 @Override
1262 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson36a5a2c2015-10-29 18:04:39 -07001263 // Layout each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001264 mTmpTaskViews.clear();
1265 mTmpTaskViews.addAll(getTaskViews());
1266 mTmpTaskViews.addAll(mViewPool.getViews());
1267 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001268 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001269 layoutTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001270 }
1271
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001272 if (changed) {
Winsona2236f12015-11-13 16:10:01 -08001273 if (mStackScroller.isScrollOutOfBounds()) {
1274 mStackScroller.boundScroll();
1275 }
Winsonf24f2162016-01-05 12:11:55 -08001276 }
Winson8aa99592016-01-19 15:07:07 -08001277 // Relayout all of the task views including the ignored ones
Winsonbe8e6962016-02-01 14:27:52 -08001278 relayoutTaskViews(AnimationProps.IMMEDIATE, EMPTY_TASK_SET);
Winsonf24f2162016-01-05 12:11:55 -08001279 clipTaskViews();
1280
1281 if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
1282 mAwaitingFirstLayout = false;
1283 onFirstLayout();
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001284 }
Winson Chungdcfa7972014-07-22 12:27:13 -07001285 }
Winson Chung24cf1522014-05-29 12:03:33 -07001286
Winson70f0bf72016-02-01 14:05:29 -08001287 /**
1288 * Lays out a TaskView.
1289 */
1290 private void layoutTaskView(TaskView tv) {
1291 if (tv.getBackground() != null) {
1292 tv.getBackground().getPadding(mTmpRect);
1293 } else {
1294 mTmpRect.setEmpty();
1295 }
Winsonf9357d92016-03-25 15:14:37 -07001296 Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
Winson59924fe2016-03-17 14:13:18 -07001297 tv.cancelTransformAnimation();
Winson70f0bf72016-02-01 14:05:29 -08001298 tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
1299 taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
1300 }
1301
Winson Chungdcfa7972014-07-22 12:27:13 -07001302 /** Handler for the first layout. */
1303 void onFirstLayout() {
Winsonf24f2162016-01-05 12:11:55 -08001304 // Setup the view for the enter animation
1305 mAnimationHelper.prepareForEnterAnimation();
Winson Chung083baf92014-07-11 10:32:42 -07001306
Winsona78a8f32015-12-03 10:55:01 -08001307 // Animate in the freeform workspace
Winsonbe8e6962016-02-01 14:27:52 -08001308 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
1309 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
1310 Interpolators.FAST_OUT_SLOW_IN));
Winsona78a8f32015-12-03 10:55:01 -08001311
Winson0d14d4d2015-10-26 17:05:04 -07001312 // Set the task focused state without requesting view focus, and leave the focus animations
1313 // until after the enter-animation
Winson5da43472015-11-04 17:39:55 -08001314 RecentsConfiguration config = Recents.getConfiguration();
1315 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson4b9cded2016-01-26 16:26:47 -08001316 int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
Winson5da43472015-11-04 17:39:55 -08001317 if (focusedTaskIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001318 setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
Winson5da43472015-11-04 17:39:55 -08001319 false /* requestViewFocus */);
Winson0983e022015-10-13 17:21:42 -07001320 }
Winson Chung860e2d82014-12-04 11:43:02 -08001321
Winson8f6ee482016-03-18 17:51:48 -07001322 // Update the stack action button visibility
1323 if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1324 EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
Winson Chungde750de2015-12-11 10:26:06 -05001325 } else {
Winson8f6ee482016-03-18 17:51:48 -07001326 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winsonc29ff002015-11-20 16:00:45 -08001327 }
Winson Chung24cf1522014-05-29 12:03:33 -07001328 }
1329
Winson671e8f92016-01-12 13:16:56 -08001330 public boolean isTouchPointInView(float x, float y, TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001331 mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
1332 mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
1333 return mTmpRect.contains((int) x, (int) y);
1334 }
1335
1336 /**
1337 * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
1338 * calculating the scroll position before and after a layout change.
1339 */
1340 public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
1341 for (int i = tasks.size() - 1; i >= 0; i--) {
1342 Task task = tasks.get(i);
1343
1344 // Ignore deleting tasks
Winson05e46ca2016-02-05 15:40:29 -08001345 if (isIgnoredTask(task)) {
Winson8aa99592016-01-19 15:07:07 -08001346 if (i == tasks.size() - 1) {
1347 isFrontMostTask.value = true;
1348 }
1349 continue;
1350 }
1351 return task;
1352 }
1353 return null;
Winson Chung303e1ff2014-03-07 15:06:19 -08001354 }
1355
Jorim Jaggi900fb482015-06-02 15:07:33 -07001356 @Override
Winsonbe8e6962016-02-01 14:27:52 -08001357 protected void onDraw(Canvas canvas) {
1358 super.onDraw(canvas);
1359
Winson36a5a2c2015-10-29 18:04:39 -07001360 // Draw the freeform workspace background
Winson805578d2015-11-23 14:47:37 -08001361 SystemServicesProxy ssp = Recents.getSystemServices();
1362 if (ssp.hasFreeformWorkspaceSupport()) {
1363 if (mFreeformWorkspaceBackground.getAlpha() > 0) {
1364 mFreeformWorkspaceBackground.draw(canvas);
1365 }
Winson36a5a2c2015-10-29 18:04:39 -07001366 }
Jorim Jaggi900fb482015-06-02 15:07:33 -07001367 }
1368
Winsona78a8f32015-12-03 10:55:01 -08001369 @Override
1370 protected boolean verifyDrawable(Drawable who) {
1371 if (who == mFreeformWorkspaceBackground) {
1372 return true;
1373 }
1374 return super.verifyDrawable(who);
1375 }
1376
Winsona5e6b362015-11-02 17:17:20 -08001377 /**
1378 * Launches the freeform tasks.
1379 */
1380 public boolean launchFreeformTasks() {
Winsonf24f2162016-01-05 12:11:55 -08001381 ArrayList<Task> tasks = mStack.getFreeformTasks();
1382 if (!tasks.isEmpty()) {
1383 Task frontTask = tasks.get(tasks.size() - 1);
1384 if (frontTask != null && frontTask.isFreeformTask()) {
1385 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
1386 frontTask, null, INVALID_STACK_ID, false));
1387 return true;
1388 }
Winsona5e6b362015-11-02 17:17:20 -08001389 }
1390 return false;
1391 }
1392
Winson Chung303e1ff2014-03-07 15:06:19 -08001393 /**** TaskStackCallbacks Implementation ****/
1394
1395 @Override
Winson Chung06266772015-12-11 10:24:21 -05001396 public void onStackTaskAdded(TaskStack stack, Task newTask) {
1397 // Update the min/max scroll and animate other task views into their new positions
Winson8aa99592016-01-19 15:07:07 -08001398 updateLayoutAlgorithm(true /* boundScroll */);
Winson Chung06266772015-12-11 10:24:21 -05001399
1400 // Animate all the tasks into place
Winsonbe8e6962016-02-01 14:27:52 -08001401 relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winsonc0d70582016-01-29 10:24:39 -08001402 Interpolators.FAST_OUT_SLOW_IN));
Winson Chung06266772015-12-11 10:24:21 -05001403 }
1404
Winson8aa99592016-01-19 15:07:07 -08001405 /**
1406 * We expect that the {@link TaskView} associated with the removed task is already hidden.
1407 */
Winson Chung06266772015-12-11 10:24:21 -05001408 @Override
Winsonaaf33bc2015-12-03 12:02:38 -08001409 public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Winson20684082016-03-16 17:13:34 -07001410 Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
Winsonaaf33bc2015-12-03 12:02:38 -08001411 if (mFocusedTask == removedTask) {
Winsona0731a12015-12-02 15:10:14 -08001412 resetFocusedTask(removedTask);
1413 }
1414
Winson8aa99592016-01-19 15:07:07 -08001415 // Remove the view associated with this task, we can't rely on updateTransforms
1416 // to work here because the task is no longer in the list
1417 TaskView tv = getChildViewForTask(removedTask);
1418 if (tv != null) {
1419 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -08001420 }
1421
Winson8aa99592016-01-19 15:07:07 -08001422 // Remove the task from the ignored set
1423 removeIgnoreTask(removedTask);
1424
1425 // If requested, relayout with the given animation
1426 if (animation != null) {
1427 updateLayoutAlgorithm(true /* boundScroll */);
1428 relayoutTaskViews(animation);
1429 }
Winsonf24f2162016-01-05 12:11:55 -08001430
Winson Chung931c51f2015-12-17 17:08:55 -05001431 // Update the new front most task's action button
1432 if (mScreenPinningEnabled && newFrontMostTask != null) {
Winson Chung1f24c7e2014-07-11 17:06:48 -07001433 TaskView frontTv = getChildViewForTask(newFrontMostTask);
1434 if (frontTv != null) {
Winson Chung931c51f2015-12-17 17:08:55 -05001435 frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
Winson Chung1f24c7e2014-07-11 17:06:48 -07001436 }
1437 }
1438
Winson397ae742015-11-20 11:27:33 -08001439 // If there are no remaining tasks, then just close recents
Winson4b057c62016-01-12 15:01:52 -08001440 if (mStack.getTaskCount() == 0) {
Winson20684082016-03-16 17:13:34 -07001441 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
1442 ? R.string.recents_empty_message
1443 : R.string.recents_empty_message_dismissed_all));
Winson Chung9f49df92014-05-07 18:08:34 -07001444 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001445 }
1446
Winsona1ededd2016-03-25 12:23:12 -07001447 @Override
1448 public void onStackTasksUpdated(TaskStack stack) {
1449 // Update the layout and immediately layout
1450 updateLayoutAlgorithm(false /* boundScroll */);
1451 relayoutTaskViews(AnimationProps.IMMEDIATE);
1452
1453 // Rebind all the task views. This will not trigger new resources to be loaded
1454 // unless they have actually changed
1455 List<TaskView> taskViews = getTaskViews();
1456 int taskViewCount = taskViews.size();
1457 for (int i = 0; i < taskViewCount; i++) {
1458 TaskView tv = taskViews.get(i);
1459 bindTaskView(tv, tv.getTask());
1460 }
1461 }
1462
Winson Chung303e1ff2014-03-07 15:06:19 -08001463 /**** ViewPoolConsumer Implementation ****/
1464
1465 @Override
1466 public TaskView createView(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -07001467 return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
Winson Chung303e1ff2014-03-07 15:06:19 -08001468 }
1469
1470 @Override
Winson05e46ca2016-02-05 15:40:29 -08001471 public void onReturnViewToPool(TaskView tv) {
Winson Chung931c51f2015-12-17 17:08:55 -05001472 final Task task = tv.getTask();
Winson Chung303e1ff2014-03-07 15:06:19 -08001473
Winson43336942016-03-07 14:52:59 -08001474 // Unbind the task from the task view
1475 unbindTaskView(tv, task);
Winson Chung303e1ff2014-03-07 15:06:19 -08001476
Winson Chung931c51f2015-12-17 17:08:55 -05001477 // Reset the view properties and view state
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001478 tv.resetViewProperties();
Winsonf24f2162016-01-05 12:11:55 -08001479 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungb0a28ea2014-10-28 15:21:35 -07001480 tv.setClipViewInStack(false);
Winson Chung931c51f2015-12-17 17:08:55 -05001481 if (mScreenPinningEnabled) {
Winsonf24f2162016-01-05 12:11:55 -08001482 tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
Winson Chung931c51f2015-12-17 17:08:55 -05001483 }
Winson3c107162016-01-22 15:53:00 -08001484
1485 // Detach the view from the hierarchy
1486 detachViewFromParent(tv);
1487 // Update the task views list after removing the task view
1488 updateTaskViewsList();
Winson Chung303e1ff2014-03-07 15:06:19 -08001489 }
1490
1491 @Override
Winson05e46ca2016-02-05 15:40:29 -08001492 public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001493 // Find the index where this task should be placed in the stack
Winson250608a2015-11-24 15:00:31 -08001494 int taskIndex = mStack.indexOfStackTask(task);
Winsona78a8f32015-12-03 10:55:01 -08001495 int insertIndex = findTaskViewInsertIndex(task, taskIndex);
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001496
Winson Chung303e1ff2014-03-07 15:06:19 -08001497 // Add/attach the view to the hierarchy
Winson Chung303e1ff2014-03-07 15:06:19 -08001498 if (isNewView) {
Winson70f0bf72016-02-01 14:05:29 -08001499 if (mInMeasureLayout) {
1500 // If we are measuring the layout, then just add the view normally as it will be
1501 // laid out during the layout pass
1502 addView(tv, insertIndex);
1503 } else {
1504 // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
1505 // pass, and we should layout the new child ourselves
1506 ViewGroup.LayoutParams params = tv.getLayoutParams();
1507 if (params == null) {
1508 params = generateDefaultLayoutParams();
1509 }
1510 addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
1511 measureTaskView(tv);
1512 layoutTaskView(tv);
1513 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001514 } else {
1515 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
1516 }
Winson Chung6ac8bd62015-01-07 16:38:35 -08001517 // Update the task views list after adding the new task view
1518 updateTaskViewsList();
Winson Chungb0a28ea2014-10-28 15:21:35 -07001519
Winson43336942016-03-07 14:52:59 -08001520 // Bind the task view to the new task
1521 bindTaskView(tv, task);
Winson3c107162016-01-22 15:53:00 -08001522
1523 // If the doze trigger has already fired, then update the state for this task view
Winsone693aaf2016-03-01 12:05:59 -08001524 if (mUIDozeTrigger.isAsleep()) {
Winson Chungbbb3d3d2016-01-30 01:09:20 +00001525 tv.setNoUserInteractionState();
1526 }
Winson3c107162016-01-22 15:53:00 -08001527
Winson Chungb0a28ea2014-10-28 15:21:35 -07001528 // Set the new state for this view, including the callbacks and view clipping
1529 tv.setCallbacks(this);
1530 tv.setTouchEnabled(true);
1531 tv.setClipViewInStack(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001532 if (mFocusedTask == task) {
Winsonf24f2162016-01-05 12:11:55 -08001533 tv.setFocusedState(true, false /* requestViewFocus */);
Winson4b9cded2016-01-26 16:26:47 -08001534 if (mStartTimerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -08001535 // The timer indicator couldn't be started before, so start it now
Winson4b9cded2016-01-26 16:26:47 -08001536 tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
1537 mStartTimerIndicatorDuration = 0;
Peter Schillerb124d562015-12-11 21:31:17 -08001538 }
Winsona0731a12015-12-02 15:10:14 -08001539 }
Winson Chung931c51f2015-12-17 17:08:55 -05001540
1541 // Restore the action button visibility if it is the front most task view
Winson35a8b042016-01-22 09:41:09 -08001542 if (mScreenPinningEnabled && tv.getTask() ==
1543 mStack.getStackFrontMostTask(false /* includeFreeform */)) {
Winson Chung931c51f2015-12-17 17:08:55 -05001544 tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
1545 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001546 }
1547
1548 @Override
1549 public boolean hasPreferredData(TaskView tv, Task preferredData) {
1550 return (tv.getTask() == preferredData);
1551 }
1552
Winson43336942016-03-07 14:52:59 -08001553 private void bindTaskView(TaskView tv, Task task) {
1554 // Rebind the task and request that this task's data be filled into the TaskView
1555 tv.onTaskBound(task);
1556
1557 // Load the task data
1558 Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
1559 }
1560
1561 private void unbindTaskView(TaskView tv, Task task) {
1562 // Report that this task's data is no longer being used
1563 Recents.getTaskLoader().unloadTaskData(task);
1564 }
1565
Winson Chung303e1ff2014-03-07 15:06:19 -08001566 /**** TaskViewCallbacks Implementation ****/
1567
1568 @Override
Winson Chung93748a12014-07-13 17:43:31 -07001569 public void onTaskViewClipStateChanged(TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001570 if (!mTaskViewsClipDirty) {
1571 mTaskViewsClipDirty = true;
1572 invalidate();
1573 }
Winson Chung93748a12014-07-13 17:43:31 -07001574 }
1575
Winson1c846142016-01-22 11:34:38 -08001576 /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
1577
1578 @Override
Winson66474132016-02-23 18:45:47 -08001579 public void onFocusStateChanged(int prevFocusState, int curFocusState) {
Winson1c846142016-01-22 11:34:38 -08001580 if (mDeferredTaskViewLayoutAnimation == null) {
1581 mUIDozeTrigger.poke();
Winsonbe8e6962016-02-01 14:27:52 -08001582 relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
Winson1c846142016-01-22 11:34:38 -08001583 }
1584 }
1585
Winson Chung012ef362014-07-31 18:36:25 -07001586 /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
1587
1588 @Override
Winson14991502016-02-15 15:40:08 -08001589 public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
Winson Chung012ef362014-07-31 18:36:25 -07001590 mUIDozeTrigger.poke();
Winson8aa99592016-01-19 15:07:07 -08001591 if (animation != null) {
1592 relayoutTaskViewsOnNextFrame(animation);
1593 }
Winson66474132016-02-23 18:45:47 -08001594 mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
Winsonc29ff002015-11-20 16:00:45 -08001595
Winson49df4202016-01-25 17:33:34 -08001596 if (mEnterAnimationComplete) {
Winson8f6ee482016-03-18 17:51:48 -07001597 if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1598 curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1599 EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
1600 } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1601 curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1602 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winson49df4202016-01-25 17:33:34 -08001603 }
Winsonc29ff002015-11-20 16:00:45 -08001604 }
Winson Chung012ef362014-07-31 18:36:25 -07001605 }
1606
Winsone6c90732015-09-24 16:06:29 -07001607 /**** EventBus Events ****/
Winson Chung9f49df92014-05-07 18:08:34 -07001608
Winsone6c90732015-09-24 16:06:29 -07001609 public final void onBusEvent(PackagesChangedEvent event) {
Winson Chung04400672014-10-17 14:53:30 -07001610 // Compute which components need to be removed
Winson55003902016-01-12 12:00:37 -08001611 ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
Winsone7f138c2015-10-22 16:15:21 -07001612 event.packageName, event.userId);
Winson Chung04400672014-10-17 14:53:30 -07001613
Winson Chung9f49df92014-05-07 18:08:34 -07001614 // For other tasks, just remove them directly if they no longer exist
Winson250608a2015-11-24 15:00:31 -08001615 ArrayList<Task> tasks = mStack.getStackTasks();
Winson Chung9f49df92014-05-07 18:08:34 -07001616 for (int i = tasks.size() - 1; i >= 0; i--) {
1617 final Task t = tasks.get(i);
Winsone7f138c2015-10-22 16:15:21 -07001618 if (removedComponents.contains(t.key.getComponent())) {
Winson0d14d4d2015-10-26 17:05:04 -07001619 final TaskView tv = getChildViewForTask(t);
Winson Chung9f49df92014-05-07 18:08:34 -07001620 if (tv != null) {
1621 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001622 tv.dismissTask();
Winson Chung9f49df92014-05-07 18:08:34 -07001623 } else {
1624 // Otherwise, remove the task from the stack immediately
Winson20684082016-03-16 17:13:34 -07001625 mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson Chung9f49df92014-05-07 18:08:34 -07001626 }
1627 }
1628 }
1629 }
Winson2536c7e2015-10-01 15:49:31 -07001630
Winson Chung48f2cda2015-12-11 13:20:12 -05001631 public final void onBusEvent(LaunchTaskEvent event) {
1632 // Cancel any doze triggers once a task is launched
1633 mUIDozeTrigger.stopDozing();
1634 }
1635
Winsonb61e6542016-02-04 14:37:18 -08001636 public final void onBusEvent(LaunchNextTaskRequestEvent event) {
1637 int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
1638 if (launchTaskIndex != -1) {
1639 launchTaskIndex = Math.max(0, launchTaskIndex - 1);
1640 } else {
1641 launchTaskIndex = mStack.getTaskCount() - 1;
1642 }
1643 if (launchTaskIndex != -1) {
1644 // Stop all animations
1645 mUIDozeTrigger.stopDozing();
1646 cancelAllTaskViewAnimations();
1647
Winson96e61342016-03-15 16:47:19 -07001648 final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
Winsonf8597b22016-03-23 18:44:26 -07001649 float curScroll = mStackScroller.getStackScroll();
1650 float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
1651 float absScrollDiff = Math.abs(targetScroll - curScroll);
1652 if (getChildViewForTask(launchTask) == null || absScrollDiff > 0.35f) {
1653 int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
1654 absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
1655 mStackScroller.animateScroll(targetScroll,
Winson96e61342016-03-15 16:47:19 -07001656 duration, new Runnable() {
1657 @Override
1658 public void run() {
1659 EventBus.getDefault().send(new LaunchTaskEvent(
1660 getChildViewForTask(launchTask), launchTask, null,
1661 INVALID_STACK_ID, false /* screenPinningRequested */));
1662 }
1663 });
1664 } else {
1665 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
1666 launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
1667 }
Winsond9342902016-02-25 10:18:33 -08001668
1669 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
1670 launchTask.key.getComponent().toString());
Winsonbc0f8cd2016-03-15 15:44:48 -07001671 } else if (mStack.getTaskCount() == 0) {
1672 // If there are no tasks, then just hide recents back to home.
1673 EventBus.getDefault().send(new HideRecentsEvent(false, true));
Winsonb61e6542016-02-04 14:37:18 -08001674 }
1675 }
1676
Winsonef064132016-01-05 12:11:31 -08001677 public final void onBusEvent(LaunchTaskStartedEvent event) {
Winsonf24f2162016-01-05 12:11:55 -08001678 mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
1679 event.getAnimationTrigger());
Winson0d14d4d2015-10-26 17:05:04 -07001680 }
Winson2536c7e2015-10-01 15:49:31 -07001681
Winsonef064132016-01-05 12:11:31 -08001682 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
1683 // Stop any scrolling
1684 mStackScroller.stopScroller();
1685 mStackScroller.stopBoundScrollAnimation();
Winson2536c7e2015-10-01 15:49:31 -07001686
Winsonef064132016-01-05 12:11:31 -08001687 // Start the task animations
Winsonf24f2162016-01-05 12:11:55 -08001688 mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001689
1690 // Dismiss the freeform workspace background
Winson50448632016-02-01 18:04:59 -08001691 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
Winsonbe8e6962016-02-01 14:27:52 -08001692 animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
1693 Interpolators.FAST_OUT_SLOW_IN));
Winson0d14d4d2015-10-26 17:05:04 -07001694 }
Winson42be4312015-10-10 11:51:08 -07001695
Winson0d14d4d2015-10-26 17:05:04 -07001696 public final void onBusEvent(DismissFocusedTaskViewEvent event) {
Winsonaaf33bc2015-12-03 12:02:38 -08001697 if (mFocusedTask != null) {
1698 TaskView tv = getChildViewForTask(mFocusedTask);
1699 if (tv != null) {
1700 tv.dismissTask();
1701 }
1702 resetFocusedTask(mFocusedTask);
Winson2536c7e2015-10-01 15:49:31 -07001703 }
1704 }
Winsone7f138c2015-10-22 16:15:21 -07001705
Winsonef064132016-01-05 12:11:31 -08001706 public final void onBusEvent(final DismissTaskViewEvent event) {
1707 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001708 mAnimationHelper.startDeleteTaskAnimation(event.task, event.taskView,
1709 event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001710 }
1711
1712 public final void onBusEvent(TaskViewDismissedEvent event) {
Winson8aa99592016-01-19 15:07:07 -08001713 removeTaskViewFromStack(event.taskView, event.task);
Winsonef064132016-01-05 12:11:31 -08001714 EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
Winson42329522016-02-05 10:39:46 -08001715
1716 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
1717 event.task.key.getComponent().toString());
Winsonef064132016-01-05 12:11:31 -08001718 }
1719
1720 public final void onBusEvent(FocusNextTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001721 // Stop any scrolling
1722 mStackScroller.stopScroller();
1723 mStackScroller.stopBoundScrollAnimation();
1724
Peter Schillerb124d562015-12-11 21:31:17 -08001725 setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
Winson4b9cded2016-01-26 16:26:47 -08001726 event.timerIndicatorDuration);
Winsonef064132016-01-05 12:11:31 -08001727 }
1728
1729 public final void onBusEvent(FocusPreviousTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001730 // Stop any scrolling
1731 mStackScroller.stopScroller();
1732 mStackScroller.stopBoundScrollAnimation();
1733
Winsonef064132016-01-05 12:11:31 -08001734 setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
1735 }
1736
Winsone7f138c2015-10-22 16:15:21 -07001737 public final void onBusEvent(UserInteractionEvent event) {
1738 // Poke the doze trigger on user interaction
1739 mUIDozeTrigger.poke();
Winson4b9cded2016-01-26 16:26:47 -08001740
1741 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
1742 if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
Winsonc5ef63f2016-01-21 14:39:23 -08001743 TaskView tv = getChildViewForTask(mFocusedTask);
1744 if (tv != null) {
1745 tv.getHeaderView().cancelFocusTimerIndicator();
1746 }
Peter Schillerb124d562015-12-11 21:31:17 -08001747 }
Winsone7f138c2015-10-22 16:15:21 -07001748 }
1749
Winsoneca4ab62015-11-04 10:50:28 -08001750 public final void onBusEvent(DragStartEvent event) {
Winson70f0bf72016-02-01 14:05:29 -08001751 // Ensure that the drag task is not animated
1752 addIgnoreTask(event.task);
1753
Winsoneca4ab62015-11-04 10:50:28 -08001754 if (event.task.isFreeformTask()) {
1755 // Animate to the front of the stack
Winsond9529612016-01-28 13:29:49 -08001756 mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
Winsoneca4ab62015-11-04 10:50:28 -08001757 }
Winsonf24f2162016-01-05 12:11:55 -08001758
1759 // Enlarge the dragged view slightly
1760 float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
1761 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
1762 mTmpTransform, null);
1763 mTmpTransform.scale = finalScale;
Winson3e874742016-01-07 10:08:17 -08001764 mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
Winsonf24f2162016-01-05 12:11:55 -08001765 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08001766 new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winsoneca4ab62015-11-04 10:50:28 -08001767 }
1768
1769 public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
1770 SystemServicesProxy ssp = Recents.getSystemServices();
1771 if (ssp.hasFreeformWorkspaceSupport()) {
1772 event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
1773 event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
1774 }
1775 }
1776
1777 public final void onBusEvent(DragDropTargetChangedEvent event) {
Winsonbe8e6962016-02-01 14:27:52 -08001778 AnimationProps animation = new AnimationProps(250, Interpolators.FAST_OUT_SLOW_IN);
Winson59924fe2016-03-17 14:13:18 -07001779 boolean ignoreTaskOverrides = false;
Winson3e874742016-01-07 10:08:17 -08001780 if (event.dropTarget instanceof TaskStack.DockState) {
1781 // Calculate the new task stack bounds that matches the window size that Recents will
1782 // have after the drop
1783 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
1784 mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
1785 getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
Winson59924fe2016-03-17 14:13:18 -07001786 mLayoutAlgorithm, getResources(), mWindowRect));
1787 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001788 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1789 updateLayoutAlgorithm(true /* boundScroll */);
Winson59924fe2016-03-17 14:13:18 -07001790 ignoreTaskOverrides = true;
Winson3e874742016-01-07 10:08:17 -08001791 } else {
Winson8aa99592016-01-19 15:07:07 -08001792 // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
1793 // task view, so add it back to the ignore set after updating the layout
Winson59924fe2016-03-17 14:13:18 -07001794 mWindowRect.set(mStableWindowRect);
Winson3e874742016-01-07 10:08:17 -08001795 mStackBounds.set(mStableStackBounds);
Winson8aa99592016-01-19 15:07:07 -08001796 removeIgnoreTask(event.task);
Winson59924fe2016-03-17 14:13:18 -07001797 mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001798 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
1799 updateLayoutAlgorithm(true /* boundScroll */);
1800 addIgnoreTask(event.task);
Winson3e874742016-01-07 10:08:17 -08001801 }
Winson59924fe2016-03-17 14:13:18 -07001802 relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides);
Winsoneca4ab62015-11-04 10:50:28 -08001803 }
1804
1805 public final void onBusEvent(final DragEndEvent event) {
Winson479f7442015-11-25 15:16:27 -08001806 // We don't handle drops on the dock regions
1807 if (event.dropTarget instanceof TaskStack.DockState) {
Winson59924fe2016-03-17 14:13:18 -07001808 // However, we do need to reset the overrides, since the last state of this task stack
1809 // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
1810 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winsoneca4ab62015-11-04 10:50:28 -08001811 return;
1812 }
1813
Winson479f7442015-11-25 15:16:27 -08001814 boolean isFreeformTask = event.task.isFreeformTask();
1815 boolean hasChangedStacks =
1816 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
1817 (isFreeformTask && event.dropTarget == mStackDropTarget);
Winson479f7442015-11-25 15:16:27 -08001818
Winson5b7dd532015-12-01 16:02:12 -08001819 if (hasChangedStacks) {
Winson479f7442015-11-25 15:16:27 -08001820 // Move the task to the right position in the stack (ie. the front of the stack if
Winson8aa99592016-01-19 15:07:07 -08001821 // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
Winson479f7442015-11-25 15:16:27 -08001822 // before we update their stack ids, otherwise, the keys will have changed.
1823 if (event.dropTarget == mFreeformWorkspaceDropTarget) {
1824 mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001825 } else if (event.dropTarget == mStackDropTarget) {
1826 mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08001827 }
Winson8aa99592016-01-19 15:07:07 -08001828 updateLayoutAlgorithm(true /* boundScroll */);
Winson479f7442015-11-25 15:16:27 -08001829
1830 // Move the task to the new stack in the system after the animation completes
Winson Chungaaeaac12015-12-16 16:49:36 -05001831 event.addPostAnimationCallback(new Runnable() {
Winson479f7442015-11-25 15:16:27 -08001832 @Override
1833 public void run() {
1834 SystemServicesProxy ssp = Recents.getSystemServices();
1835 ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
1836 }
1837 });
Winsoneca4ab62015-11-04 10:50:28 -08001838 }
Winsoneca4ab62015-11-04 10:50:28 -08001839
Winsonbb410952015-12-04 14:34:11 -08001840 // We translated the view but we need to animate it back from the current layout-space rect
1841 // to its final layout-space rect
1842 int x = (int) event.taskView.getTranslationX();
1843 int y = (int) event.taskView.getTranslationY();
1844 Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(),
1845 event.taskView.getRight(), event.taskView.getBottom());
1846 taskViewRect.offset(x, y);
1847 event.taskView.setTranslationX(0);
1848 event.taskView.setTranslationY(0);
1849 event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
1850 taskViewRect.right, taskViewRect.bottom);
1851
Winson88737542016-02-17 13:27:33 -08001852 // Animate the non-drag TaskViews back into position
Winsonf24f2162016-01-05 12:11:55 -08001853 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
1854 mTmpTransform, null);
1855 event.getAnimationTrigger().increment();
Winsonbe8e6962016-02-01 14:27:52 -08001856 relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winsonc0d70582016-01-29 10:24:39 -08001857 Interpolators.FAST_OUT_SLOW_IN));
Winson88737542016-02-17 13:27:33 -08001858
1859 // Animate the drag TaskView back into position
Winsonf24f2162016-01-05 12:11:55 -08001860 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08001861 new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
Winsonf24f2162016-01-05 12:11:55 -08001862 event.getAnimationTrigger().decrementOnAnimationEnd()));
Winson8aa99592016-01-19 15:07:07 -08001863 removeIgnoreTask(event.task);
Winsoneca4ab62015-11-04 10:50:28 -08001864 }
1865
Winson8b1871d2015-11-20 09:56:20 -08001866 public final void onBusEvent(IterateRecentsEvent event) {
Winson Chungd6b78a32015-12-15 10:22:45 -05001867 if (!mEnterAnimationComplete) {
1868 // Cancel the previous task's window transition before animating the focused state
1869 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1870 }
Winson8b1871d2015-11-20 09:56:20 -08001871 }
1872
Winsone5f1faa2015-11-20 12:26:23 -08001873 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
1874 mEnterAnimationComplete = true;
Winsonef064132016-01-05 12:11:31 -08001875
Winson4b057c62016-01-12 15:01:52 -08001876 if (mStack.getTaskCount() > 0) {
Winsonef064132016-01-05 12:11:31 -08001877 // Start the task enter animations
Winsonf24f2162016-01-05 12:11:55 -08001878 mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001879
1880 // Add a runnable to the post animation ref counter to clear all the views
1881 event.addPostAnimationCallback(new Runnable() {
1882 @Override
1883 public void run() {
1884 // Start the dozer to trigger to trigger any UI that shows after a timeout
1885 mUIDozeTrigger.startDozing();
1886
1887 // Update the focused state here -- since we only set the focused task without
1888 // requesting view focus in onFirstLayout(), actually request view focus and
1889 // animate the focused state if we are alt-tabbing now, after the window enter
1890 // animation is completed
1891 if (mFocusedTask != null) {
1892 RecentsConfiguration config = Recents.getConfiguration();
1893 RecentsActivityLaunchState launchState = config.getLaunchState();
1894 setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
1895 false /* scrollToTask */, launchState.launchedWithAltTab);
1896 }
Winson4b9cded2016-01-26 16:26:47 -08001897
1898 EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent());
Winsonef064132016-01-05 12:11:31 -08001899 }
1900 });
1901 }
Winsone5f1faa2015-11-20 12:26:23 -08001902 }
1903
Winsonb1e71d02015-11-23 12:40:23 -08001904 public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
1905 List<TaskView> taskViews = getTaskViews();
1906 int taskViewCount = taskViews.size();
1907 for (int i = 0; i < taskViewCount; i++) {
1908 TaskView tv = taskViews.get(i);
1909 Task task = tv.getTask();
Winson Chung296278a2015-12-17 12:09:02 -05001910 if (task.isFreeformTask()) {
Winsonb1e71d02015-11-23 12:40:23 -08001911 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
1912 }
1913 }
1914 }
1915
Winson88737542016-02-17 13:27:33 -08001916 public final void onBusEvent(MultiWindowStateChangedEvent event) {
Winson931845f2016-02-24 19:38:41 -08001917 if (!event.inMultiWindow) {
Winson88737542016-02-17 13:27:33 -08001918 // Scroll the stack to the front to see the undocked task
1919 mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
Winson931845f2016-02-24 19:38:41 -08001920 @Override
1921 public void run() {
Winson88737542016-02-17 13:27:33 -08001922 List<TaskView> taskViews = getTaskViews();
1923 int taskViewCount = taskViews.size();
1924 for (int i = 0; i < taskViewCount; i++) {
1925 TaskView tv = taskViews.get(i);
1926 tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
1927 tv.mIsDisabledInSafeMode);
1928 }
Winson931845f2016-02-24 19:38:41 -08001929 }
1930 });
1931 }
Winsond9529612016-01-28 13:29:49 -08001932 }
1933
Winsone693aaf2016-03-01 12:05:59 -08001934 public final void onBusEvent(ConfigurationChangedEvent event) {
Winsonf9357d92016-03-25 15:14:37 -07001935 mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winsone693aaf2016-03-01 12:05:59 -08001936 mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winson619e40c2016-03-25 16:12:35 -07001937
1938 // Notify the task views of the configuration change so they can reload their resources
1939 if (!event.fromMultiWindow) {
1940 mTmpTaskViews.clear();
1941 mTmpTaskViews.addAll(getTaskViews());
1942 mTmpTaskViews.addAll(mViewPool.getViews());
1943 int taskViewCount = mTmpTaskViews.size();
1944 for (int i = 0; i < taskViewCount; i++) {
1945 mTmpTaskViews.get(i).onConfigurationChanged();
1946 }
1947 }
1948
1949 // Trigger a new layout and scroll to the initial state
1950 mInitialState = event.fromMultiWindow
1951 ? INITIAL_STATE_UPDATE_ALL
1952 : INITIAL_STATE_UPDATE_LAYOUT_ONLY;
1953 requestLayout();
Winsone693aaf2016-03-01 12:05:59 -08001954 }
1955
Winson0d14d4d2015-10-26 17:05:04 -07001956 /**
1957 * Removes the task from the stack, and updates the focus to the next task in the stack if the
1958 * removed TaskView was focused.
1959 */
Winson8aa99592016-01-19 15:07:07 -08001960 private void removeTaskViewFromStack(TaskView tv, Task task) {
Winson0d14d4d2015-10-26 17:05:04 -07001961 // Announce for accessibility
1962 tv.announceForAccessibility(getContext().getString(
Winson8aa99592016-01-19 15:07:07 -08001963 R.string.accessibility_recents_item_dismissed, task.title));
Winson0d14d4d2015-10-26 17:05:04 -07001964
1965 // Remove the task from the stack
Winsonbe8e6962016-02-01 14:27:52 -08001966 mStack.removeTask(task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Winson20684082016-03-16 17:13:34 -07001967 Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */);
Winson0d14d4d2015-10-26 17:05:04 -07001968 }
Winsona78a8f32015-12-03 10:55:01 -08001969
1970 /**
1971 * Starts an alpha animation on the freeform workspace background.
1972 */
Winsonbe8e6962016-02-01 14:27:52 -08001973 private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
1974 AnimationProps animation) {
Winsona78a8f32015-12-03 10:55:01 -08001975 if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
1976 return;
1977 }
1978
1979 Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
1980 mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
Winson3e874742016-01-07 10:08:17 -08001981 Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
Winsonbe8e6962016-02-01 14:27:52 -08001982 mFreeformWorkspaceBackgroundAnimator.setStartDelay(
1983 animation.getDuration(AnimationProps.ALPHA));
1984 mFreeformWorkspaceBackgroundAnimator.setDuration(
1985 animation.getDuration(AnimationProps.ALPHA));
1986 mFreeformWorkspaceBackgroundAnimator.setInterpolator(
1987 animation.getInterpolator(AnimationProps.ALPHA));
Winsona78a8f32015-12-03 10:55:01 -08001988 mFreeformWorkspaceBackgroundAnimator.start();
1989 }
1990
1991 /**
Winson8aa99592016-01-19 15:07:07 -08001992 * Returns the insert index for the task in the current set of task views. If the given task
Winsona78a8f32015-12-03 10:55:01 -08001993 * is already in the task view list, then this method returns the insert index assuming it
1994 * is first removed at the previous index.
1995 *
1996 * @param task the task we are finding the index for
1997 * @param taskIndex the index of the task in the stack
1998 */
1999 private int findTaskViewInsertIndex(Task task, int taskIndex) {
2000 if (taskIndex != -1) {
2001 List<TaskView> taskViews = getTaskViews();
2002 boolean foundTaskView = false;
2003 int taskViewCount = taskViews.size();
2004 for (int i = 0; i < taskViewCount; i++) {
2005 Task tvTask = taskViews.get(i).getTask();
2006 if (tvTask == task) {
2007 foundTaskView = true;
2008 } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
2009 if (foundTaskView) {
2010 return i - 1;
2011 } else {
2012 return i;
2013 }
2014 }
2015 }
2016 }
2017 return -1;
2018 }
Winson Chungde750de2015-12-11 10:26:06 -05002019
2020 /**
Winson6ea25882016-01-13 10:59:04 -08002021 * Reads current system flags related to accessibility and screen pinning.
2022 */
2023 private void readSystemFlags() {
2024 SystemServicesProxy ssp = Recents.getSystemServices();
2025 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
2026 mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
2027 Settings.System.LOCK_TO_APP_ENABLED) != 0;
2028 }
Winson Chunga4cc9662014-07-25 12:10:38 -07002029}