blob: 5f9a8f5cdd7c3e40fd270e3e4131852533f2ebc0 [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
Manu Cornet551e80c2017-02-10 12:54:40 -080023import android.animation.Animator;
24import android.animation.AnimatorListenerAdapter;
Winsona78a8f32015-12-03 10:55:01 -080025import android.animation.ObjectAnimator;
Winson Chungbf5dbf12014-09-16 00:58:25 +020026import android.animation.ValueAnimator;
Winson619e40c2016-03-25 16:12:35 -070027import android.annotation.IntDef;
Winson Chung9f49df92014-05-07 18:08:34 -070028import android.content.ComponentName;
Winson Chung303e1ff2014-03-07 15:06:19 -080029import android.content.Context;
Winsonfc48b072016-04-21 11:20:11 -070030import android.content.res.Configuration;
Winsonbb410952015-12-04 14:34:11 -080031import android.content.res.Resources;
Jorim Jaggi900fb482015-06-02 15:07:33 -070032import android.graphics.Canvas;
Winson Chung303e1ff2014-03-07 15:06:19 -080033import android.graphics.Rect;
Winsona78a8f32015-12-03 10:55:01 -080034import android.graphics.drawable.Drawable;
Winsonde0591a2015-12-04 17:24:35 -080035import android.graphics.drawable.GradientDrawable;
Winson Chung83ea6f72015-06-17 13:00:23 -070036import android.os.Bundle;
Winson Chung931c51f2015-12-17 17:08:55 -050037import android.provider.Settings;
Winson55003902016-01-12 12:00:37 -080038import android.util.ArrayMap;
39import android.util.ArraySet;
Winson8aa99592016-01-19 15:07:07 -080040import android.util.MutableBoolean;
Winson Chung37c8d8e2014-03-24 14:53:07 -070041import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080042import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080043import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080044import android.view.ViewDebug;
Winson70f0bf72016-02-01 14:05:29 -080045import android.view.ViewGroup;
Winson Chungee445952014-09-09 16:12:59 +020046import android.view.accessibility.AccessibilityEvent;
Winson Chung83ea6f72015-06-17 13:00:23 -070047import android.view.accessibility.AccessibilityNodeInfo;
Winson Chung303e1ff2014-03-07 15:06:19 -080048import android.widget.FrameLayout;
Winsonf0009882016-06-01 12:22:55 -070049import android.widget.ScrollView;
Winsonc0d70582016-01-29 10:24:39 -080050
Winson42329522016-02-05 10:39:46 -080051import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010052import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080053import com.android.systemui.Interpolators;
Winson Chungc6a16232014-04-01 14:04:48 -070054import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070055import com.android.systemui.recents.Recents;
Winsone6c90732015-09-24 16:06:29 -070056import com.android.systemui.recents.RecentsActivity;
Winson2536c7e2015-10-01 15:49:31 -070057import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chung303e1ff2014-03-07 15:06:19 -080058import com.android.systemui.recents.RecentsConfiguration;
Winson4b9cded2016-01-26 16:26:47 -080059import com.android.systemui.recents.RecentsDebugFlags;
Winson Chungb5026902017-05-03 12:45:13 -070060import com.android.systemui.recents.RecentsImpl;
Winsone6c90732015-09-24 16:06:29 -070061import com.android.systemui.recents.events.EventBus;
Winsone5f1faa2015-11-20 12:26:23 -080062import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
Winsone693aaf2016-03-01 12:05:59 -080063import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
Winsonef064132016-01-05 12:11:31 -080064import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
Winson4b9cded2016-01-26 16:26:47 -080065import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
Winsone5f1faa2015-11-20 12:26:23 -080066import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
Winsonbc0f8cd2016-03-15 15:44:48 -070067import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winsonc69249f2016-03-28 13:38:39 -070068import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
Winson8b1871d2015-11-20 09:56:20 -080069import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Manu Corneta96a6172017-01-19 12:40:44 -080070import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
Winsonb61e6542016-02-04 14:37:18 -080071import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Winson Chung48f2cda2015-12-11 13:20:12 -050072import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winsonef064132016-01-05 12:11:31 -080073import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
Winson88737542016-02-17 13:27:33 -080074import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
Winsone6c90732015-09-24 16:06:29 -070075import com.android.systemui.recents.events.activity.PackagesChangedEvent;
Winson Chungb5026902017-05-03 12:45:13 -070076import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
Winson8f6ee482016-03-18 17:51:48 -070077import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
Winson Chungb5026902017-05-03 12:45:13 -070078import com.android.systemui.recents.events.component.ActivityPinnedEvent;
79import com.android.systemui.recents.events.component.ExpandPipEvent;
80import com.android.systemui.recents.events.component.HidePipMenuEvent;
Jiaquan He21f495f2017-01-05 13:00:29 -080081import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
Winson397ae742015-11-20 11:27:33 -080082import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
Winsonef064132016-01-05 12:11:31 -080083import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
Winson3b6ba1a2016-03-22 15:37:54 -070084import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070085import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070086import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
Winsonef064132016-01-05 12:11:31 -080087import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winsonb1e71d02015-11-23 12:40:23 -080088import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
Winsone7f138c2015-10-22 16:15:21 -070089import com.android.systemui.recents.events.ui.UserInteractionEvent;
Winsoneca4ab62015-11-04 10:50:28 -080090import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
Winson27c28f82016-05-05 16:16:50 -070091import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +010092import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
Winsoneca4ab62015-11-04 10:50:28 -080093import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
94import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070095import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
96import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
97import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
Jiaquan Hec0e18132017-01-11 14:47:00 -080098import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
Winson Chungffa2ec62014-07-03 15:54:42 -070099import com.android.systemui.recents.misc.DozeTrigger;
Winson Chungee445952014-09-09 16:12:59 +0200100import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungbf5dbf12014-09-16 00:58:25 +0200101import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -0800102import com.android.systemui.recents.model.Task;
103import com.android.systemui.recents.model.TaskStack;
Jiaquan He5a2eb832016-12-13 14:36:37 -0800104import com.android.systemui.recents.views.grid.GridTaskView;
Manu Cornetde3cfb02017-01-25 18:31:01 +0900105import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
Jiaquan He21f495f2017-01-05 13:00:29 -0800106import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +0100107
Winsond72c3152016-04-05 15:33:35 -0700108import java.io.PrintWriter;
Winson619e40c2016-03-25 16:12:35 -0700109import java.lang.annotation.Retention;
110import java.lang.annotation.RetentionPolicy;
Winson Chung303e1ff2014-03-07 15:06:19 -0800111import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800112import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -0800113
Winson Chung303e1ff2014-03-07 15:06:19 -0800114
115/* The visual representation of a task stack view */
Winson Chung04dfe0d2014-03-14 14:06:29 -0700116public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
Winson Chung012ef362014-07-31 18:36:25 -0700117 TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
Winson1c846142016-01-22 11:34:38 -0800118 TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
Winsone6c90732015-09-24 16:06:29 -0700119 ViewPool.ViewPoolConsumer<TaskView, Task> {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700120
Winsond72c3152016-04-05 15:33:35 -0700121 private static final String TAG = "TaskStackView";
122
Winson8f6ee482016-03-18 17:51:48 -0700123 // The thresholds at which to show/hide the stack action button.
124 private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
125 private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
Winsonc29ff002015-11-20 16:00:45 -0800126
Winson8aa99592016-01-19 15:07:07 -0800127 public static final int DEFAULT_SYNC_STACK_DURATION = 200;
Winson3f32e7e2016-04-20 17:18:08 -0700128 public static final int SLOW_SYNC_STACK_DURATION = 250;
Winsonf24f2162016-01-05 12:11:55 -0800129 private static final int DRAG_SCALE_DURATION = 175;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100130 static final float DRAG_SCALE_FACTOR = 1.05f;
Winson Chung06266772015-12-11 10:24:21 -0500131
Winsonf8597b22016-03-23 18:44:26 -0700132 private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
Winson96e61342016-03-15 16:47:19 -0700133 private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
134
Winson619e40c2016-03-25 16:12:35 -0700135 // The actions to perform when resetting to initial state,
136 @Retention(RetentionPolicy.SOURCE)
137 @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
138 public @interface InitialStateAction {}
139 /** Do not update the stack and layout to the initial state. */
140 private static final int INITIAL_STATE_UPDATE_NONE = 0;
141 /** Update both the stack and layout to the initial state. */
142 private static final int INITIAL_STATE_UPDATE_ALL = 1;
143 /** Update only the layout to the initial state. */
144 private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
145
Winsonfc48b072016-04-21 11:20:11 -0700146 private LayoutInflater mInflater;
147 private TaskStack mStack = new TaskStack();
Winson231bc9c2016-02-09 12:31:00 -0800148 @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
Winson36a5a2c2015-10-29 18:04:39 -0700149 TaskStackLayoutAlgorithm mLayoutAlgorithm;
Winsonf9357d92016-03-25 15:14:37 -0700150 // The stable layout algorithm is only used to calculate the task rect with the stable bounds
Winsonfc48b072016-04-21 11:20:11 -0700151 private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
Winson231bc9c2016-02-09 12:31:00 -0800152 @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
Winsonfc48b072016-04-21 11:20:11 -0700153 private TaskStackViewScroller mStackScroller;
Winson231bc9c2016-02-09 12:31:00 -0800154 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
Winsonfc48b072016-04-21 11:20:11 -0700155 private TaskStackViewTouchHandler mTouchHandler;
156 private TaskStackAnimationHelper mAnimationHelper;
157 private GradientDrawable mFreeformWorkspaceBackground;
158 private ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
159 private ViewPool<TaskView, Task> mViewPool;
Winsonf24f2162016-01-05 12:11:55 -0800160
Winsonfc48b072016-04-21 11:20:11 -0700161 private ArrayList<TaskView> mTaskViews = new ArrayList<>();
162 private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
163 private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
164 private AnimationProps mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -0800165
Winson231bc9c2016-02-09 12:31:00 -0800166 @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
Winsonfc48b072016-04-21 11:20:11 -0700167 private DozeTrigger mUIDozeTrigger;
Winson231bc9c2016-02-09 12:31:00 -0800168 @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Winsonfc48b072016-04-21 11:20:11 -0700169 private Task mFocusedTask;
Winsonf24f2162016-01-05 12:11:55 -0800170
Winsonfc48b072016-04-21 11:20:11 -0700171 private int mTaskCornerRadiusPx;
Winson3e874742016-01-07 10:08:17 -0800172 private int mDividerSize;
Winson4b9cded2016-01-26 16:26:47 -0800173 private int mStartTimerIndicatorDuration;
Winsonf24f2162016-01-05 12:11:55 -0800174
Winson231bc9c2016-02-09 12:31:00 -0800175 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700176 private boolean mTaskViewsClipDirty = true;
Winson231bc9c2016-02-09 12:31:00 -0800177 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700178 private boolean mAwaitingFirstLayout = true;
Winson231bc9c2016-02-09 12:31:00 -0800179 @ViewDebug.ExportedProperty(category="recents")
Winsonbd53a312016-09-21 12:13:32 -0700180 private boolean mLaunchNextAfterFirstMeasure = false;
181 @ViewDebug.ExportedProperty(category="recents")
Winson619e40c2016-03-25 16:12:35 -0700182 @InitialStateAction
Winsonfc48b072016-04-21 11:20:11 -0700183 private int mInitialState = INITIAL_STATE_UPDATE_ALL;
Winson619e40c2016-03-25 16:12:35 -0700184 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700185 private boolean mInMeasureLayout = false;
Winson231bc9c2016-02-09 12:31:00 -0800186 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700187 private boolean mEnterAnimationComplete = false;
Winson231bc9c2016-02-09 12:31:00 -0800188 @ViewDebug.ExportedProperty(category="recents")
Winsonf0009882016-06-01 12:22:55 -0700189 boolean mTouchExplorationEnabled;
Winson231bc9c2016-02-09 12:31:00 -0800190 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800191 boolean mScreenPinningEnabled;
Winson3150e572015-10-23 15:07:24 -0700192
Winson3e874742016-01-07 10:08:17 -0800193 // The stable stack bounds are the full bounds that we were measured with from RecentsView
Winson231bc9c2016-02-09 12:31:00 -0800194 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800195 private Rect mStableStackBounds = new Rect();
Winson3e874742016-01-07 10:08:17 -0800196 // The current stack bounds are dynamic and may change as the user drags and drops
Winson231bc9c2016-02-09 12:31:00 -0800197 @ViewDebug.ExportedProperty(category="recents")
Winson05e46ca2016-02-05 15:40:29 -0800198 private Rect mStackBounds = new Rect();
Winson59924fe2016-03-17 14:13:18 -0700199 // The current window bounds at the point we were measured
200 @ViewDebug.ExportedProperty(category="recents")
201 private Rect mStableWindowRect = new Rect();
202 // The current window bounds are dynamic and may change as the user drags and drops
203 @ViewDebug.ExportedProperty(category="recents")
204 private Rect mWindowRect = new Rect();
Winsonfc48b072016-04-21 11:20:11 -0700205 // The current display bounds
206 @ViewDebug.ExportedProperty(category="recents")
207 private Rect mDisplayRect = new Rect();
208 // The current display orientation
209 @ViewDebug.ExportedProperty(category="recents")
210 private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
Winsond9529612016-01-28 13:29:49 -0800211
Winson05e46ca2016-02-05 15:40:29 -0800212 private Rect mTmpRect = new Rect();
213 private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
214 private List<TaskView> mTmpTaskViews = new ArrayList<>();
215 private TaskViewTransform mTmpTransform = new TaskViewTransform();
Winsonc4387022016-02-24 12:05:26 -0800216 private int[] mTmpIntPair = new int[2];
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700217 private boolean mResetToInitialStateWhenResized;
218 private int mLastWidth;
219 private int mLastHeight;
Winson Chung931c51f2015-12-17 17:08:55 -0500220
Jiaquan He21f495f2017-01-05 13:00:29 -0800221 // We keep track of the task view focused by user interaction and draw a frame around it in the
222 // grid layout.
223 private TaskViewFocusFrame mTaskViewFocusFrame;
224
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +0100225 private Task mPrefetchingTask;
226 private final float mFastFlingVelocity;
227
Winson Chungbf5dbf12014-09-16 00:58:25 +0200228 // A convenience update listener to request updating clipping of tasks
Winsoneca4ab62015-11-04 10:50:28 -0800229 private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
Winson Chungbf5dbf12014-09-16 00:58:25 +0200230 new ValueAnimator.AnimatorUpdateListener() {
Winson5b7dd532015-12-01 16:02:12 -0800231 @Override
232 public void onAnimationUpdate(ValueAnimator animation) {
Winson55003902016-01-12 12:00:37 -0800233 if (!mTaskViewsClipDirty) {
234 mTaskViewsClipDirty = true;
235 invalidate();
236 }
Winson5b7dd532015-12-01 16:02:12 -0800237 }
238 };
Winson Chungbf5dbf12014-09-16 00:58:25 +0200239
Winsoneca4ab62015-11-04 10:50:28 -0800240 // The drop targets for a task drag
241 private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
242 @Override
Winson08deff02016-08-05 13:58:31 -0700243 public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
244 boolean isCurrentTarget) {
Winson3e874742016-01-07 10:08:17 -0800245 // This drop target has a fixed bounds and should be checked last, so just fall through
246 // if it is the current target
247 if (!isCurrentTarget) {
248 return mLayoutAlgorithm.mFreeformRect.contains(x, y);
249 }
250 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800251 }
252 };
253
254 private DropTarget mStackDropTarget = new DropTarget() {
255 @Override
Winson08deff02016-08-05 13:58:31 -0700256 public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
257 boolean isCurrentTarget) {
Winson3e874742016-01-07 10:08:17 -0800258 // This drop target has a fixed bounds and should be checked last, so just fall through
259 // if it is the current target
260 if (!isCurrentTarget) {
261 return mLayoutAlgorithm.mStackRect.contains(x, y);
262 }
263 return false;
Winsoneca4ab62015-11-04 10:50:28 -0800264 }
265 };
266
Winson88737542016-02-17 13:27:33 -0800267 public TaskStackView(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800268 super(context);
Winsonde0591a2015-12-04 17:24:35 -0800269 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonbb410952015-12-04 14:34:11 -0800270 Resources res = context.getResources();
271
Winson Chungb0a28ea2014-10-28 15:21:35 -0700272 // Set the stack first
Winson88737542016-02-17 13:27:33 -0800273 mStack.setCallbacks(this);
Winson35f30502015-09-28 11:24:36 -0700274 mViewPool = new ViewPool<>(context, this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700275 mInflater = LayoutInflater.from(context);
Winson1c846142016-01-22 11:34:38 -0800276 mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
Winsonf9357d92016-03-25 15:14:37 -0700277 mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
Winson1c846142016-01-22 11:34:38 -0800278 mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
Winson35f30502015-09-28 11:24:36 -0700279 mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
Winsonf24f2162016-01-05 12:11:55 -0800280 mAnimationHelper = new TaskStackAnimationHelper(context, this);
Jiaquan He76b2e0a2017-01-19 15:57:39 -0800281 mTaskCornerRadiusPx = Recents.getConfiguration().isGridEnabled ?
282 res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
283 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +0100284 mFastFlingVelocity = res.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
Winson3e874742016-01-07 10:08:17 -0800285 mDividerSize = ssp.getDockedDividerSize(context);
Winsonfc48b072016-04-21 11:20:11 -0700286 mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
287 mDisplayRect = ssp.getDisplayRect();
Winson35f30502015-09-28 11:24:36 -0700288
Jiaquan He21f495f2017-01-05 13:00:29 -0800289 // Create a frame to draw around the focused task view
290 if (Recents.getConfiguration().isGridEnabled) {
291 mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
292 mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
293 addView(mTaskViewFocusFrame);
294 getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
295 }
296
Winson35f30502015-09-28 11:24:36 -0700297 int taskBarDismissDozeDelaySeconds = getResources().getInteger(
298 R.integer.recents_task_bar_dismiss_delay_seconds);
299 mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
Winson Chunga26fb782014-06-12 17:52:39 -0700300 @Override
301 public void run() {
302 // Show the task bar dismiss buttons
Winson Chung6ac8bd62015-01-07 16:38:35 -0800303 List<TaskView> taskViews = getTaskViews();
304 int taskViewCount = taskViews.size();
305 for (int i = 0; i < taskViewCount; i++) {
306 TaskView tv = taskViews.get(i);
Winson Chung969f5862014-06-16 17:08:24 -0700307 tv.startNoUserInteractionAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700308 }
309 }
310 });
Winson Chung83ea6f72015-06-17 13:00:23 -0700311 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Winsonedaffc52016-06-14 14:48:01 -0700312 if (ssp.hasFreeformWorkspaceSupport()) {
313 setWillNotDraw(false);
314 }
Winson36a5a2c2015-10-29 18:04:39 -0700315
Winsonde0591a2015-12-04 17:24:35 -0800316 mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
317 R.drawable.recents_freeform_workspace_bg);
Winsona78a8f32015-12-03 10:55:01 -0800318 mFreeformWorkspaceBackground.setCallback(this);
Winsonde0591a2015-12-04 17:24:35 -0800319 if (ssp.hasFreeformWorkspaceSupport()) {
Winson Chungaa4f8002015-12-17 10:27:55 -0500320 mFreeformWorkspaceBackground.setColor(
321 getContext().getColor(R.color.recents_freeform_workspace_bg_color));
Winsonde0591a2015-12-04 17:24:35 -0800322 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800323 }
324
Winsona1ededd2016-03-25 12:23:12 -0700325 @Override
326 protected void onAttachedToWindow() {
327 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
328 super.onAttachedToWindow();
329 readSystemFlags();
330 }
331
332 @Override
333 protected void onDetachedFromWindow() {
334 super.onDetachedFromWindow();
335 EventBus.getDefault().unregister(this);
336 }
337
Winson88737542016-02-17 13:27:33 -0800338 /**
Winsona1ededd2016-03-25 12:23:12 -0700339 * Called from RecentsActivity when it is relaunched.
Winson88737542016-02-17 13:27:33 -0800340 */
Winsona1ededd2016-03-25 12:23:12 -0700341 void onReload(boolean isResumingFromVisible) {
Winson88737542016-02-17 13:27:33 -0800342 if (!isResumingFromVisible) {
343 // Reset the focused task
344 resetFocusedTask(getFocusedTask());
345 }
346
347 // Reset the state of each of the task views
348 List<TaskView> taskViews = new ArrayList<>();
349 taskViews.addAll(getTaskViews());
350 taskViews.addAll(mViewPool.getViews());
351 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsona1ededd2016-03-25 12:23:12 -0700352 taskViews.get(i).onReload(isResumingFromVisible);
Winson88737542016-02-17 13:27:33 -0800353 }
354
355 // Reset the stack state
356 readSystemFlags();
357 mTaskViewsClipDirty = true;
358 mEnterAnimationComplete = false;
359 mUIDozeTrigger.stopDozing();
360 if (isResumingFromVisible) {
361 // Animate in the freeform workspace
362 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
363 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
364 Interpolators.FAST_OUT_SLOW_IN));
365 } else {
366 mStackScroller.reset();
Winsonf9357d92016-03-25 15:14:37 -0700367 mStableLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800368 mLayoutAlgorithm.reset();
Winson88737542016-02-17 13:27:33 -0800369 }
Winson88737542016-02-17 13:27:33 -0800370
Winsona1ededd2016-03-25 12:23:12 -0700371 // Since we always animate to the same place in (the initial state), always reset the stack
372 // to the initial state when resuming
373 mAwaitingFirstLayout = true;
Winsonbd53a312016-09-21 12:13:32 -0700374 mLaunchNextAfterFirstMeasure = false;
Winson619e40c2016-03-25 16:12:35 -0700375 mInitialState = INITIAL_STATE_UPDATE_ALL;
Winsona1ededd2016-03-25 12:23:12 -0700376 requestLayout();
Winsone6c90732015-09-24 16:06:29 -0700377 }
378
Winson88737542016-02-17 13:27:33 -0800379 /**
380 * Sets the stack tasks of this TaskStackView from the given TaskStack.
381 */
Winsona1ededd2016-03-25 12:23:12 -0700382 public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
Winson88737542016-02-17 13:27:33 -0800383 boolean isInitialized = mLayoutAlgorithm.isInitialized();
Winsond2a03062016-04-15 11:19:07 -0700384
Winsona1ededd2016-03-25 12:23:12 -0700385 // Only notify if we are already initialized, otherwise, everything will pick up all the
386 // new and old tasks when we next layout
Winson Chungb5026902017-05-03 12:45:13 -0700387 mStack.setTasks(getContext(), stack, allowNotifyStackChanges && isInitialized);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700388 }
389
Winson Chungd16c5652015-01-26 16:11:07 -0800390 /** Returns the task stack. */
Winsone693aaf2016-03-01 12:05:59 -0800391 public TaskStack getStack() {
Winson Chungd16c5652015-01-26 16:11:07 -0800392 return mStack;
393 }
394
Winsone693aaf2016-03-01 12:05:59 -0800395 /**
396 * Updates this TaskStackView to the initial state.
397 */
Winson67c79572016-04-13 14:02:18 -0700398 public void updateToInitialState() {
399 mStackScroller.setStackScrollToInitialState();
Winsond2a03062016-04-15 11:19:07 -0700400 mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
Winsone693aaf2016-03-01 12:05:59 -0800401 }
402
Winson Chung6ac8bd62015-01-07 16:38:35 -0800403 /** Updates the list of task views */
404 void updateTaskViewsList() {
405 mTaskViews.clear();
406 int childCount = getChildCount();
407 for (int i = 0; i < childCount; i++) {
408 View v = getChildAt(i);
409 if (v instanceof TaskView) {
410 mTaskViews.add((TaskView) v);
411 }
412 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800413 }
414
415 /** Gets the list of task views */
416 List<TaskView> getTaskViews() {
Winsonf24f2162016-01-05 12:11:55 -0800417 return mTaskViews;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800418 }
419
Winson1b585612015-11-06 09:16:26 -0800420 /**
421 * Returns the front most task view.
422 *
423 * @param stackTasksOnly if set, will return the front most task view in the stack (by default
424 * the front most task view will be freeform since they are placed above
425 * stack tasks)
426 */
427 private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
428 List<TaskView> taskViews = getTaskViews();
429 int taskViewCount = taskViews.size();
430 for (int i = taskViewCount - 1; i >= 0; i--) {
431 TaskView tv = taskViews.get(i);
432 Task task = tv.getTask();
433 if (stackTasksOnly && task.isFreeformTask()) {
434 continue;
435 }
436 return tv;
437 }
438 return null;
439 }
440
441 /**
442 * Finds the child view given a specific {@param task}.
443 */
444 public TaskView getChildViewForTask(Task t) {
445 List<TaskView> taskViews = getTaskViews();
446 int taskViewCount = taskViews.size();
447 for (int i = 0; i < taskViewCount; i++) {
448 TaskView tv = taskViews.get(i);
449 if (tv.getTask() == t) {
450 return tv;
451 }
452 }
453 return null;
454 }
455
Winson Chungd7b2cb12014-06-26 15:08:50 -0700456 /** Returns the stack algorithm for this task stack. */
Winson36a5a2c2015-10-29 18:04:39 -0700457 public TaskStackLayoutAlgorithm getStackAlgorithm() {
Winson Chung012ef362014-07-31 18:36:25 -0700458 return mLayoutAlgorithm;
Winson Chung303e1ff2014-03-07 15:06:19 -0800459 }
460
Manu Cornetde3cfb02017-01-25 18:31:01 +0900461 /** Returns the grid algorithm for this task stack. */
462 public TaskGridLayoutAlgorithm getGridAlgorithm() {
463 return mLayoutAlgorithm.mTaskGridLayoutAlgorithm;
464 }
465
Winson Chungc6a16232014-04-01 14:04:48 -0700466 /**
Winsonaa0dea72016-04-28 10:59:30 -0700467 * Returns the touch handler for this task stack.
468 */
469 public TaskStackViewTouchHandler getTouchHandler() {
470 return mTouchHandler;
471 }
472
473 /**
Winson8aa99592016-01-19 15:07:07 -0800474 * Adds a task to the ignored set.
Winson Chungc6a16232014-04-01 14:04:48 -0700475 */
Winson8aa99592016-01-19 15:07:07 -0800476 void addIgnoreTask(Task task) {
477 mIgnoreTasks.add(task.key);
478 }
479
480 /**
481 * Removes a task from the ignored set.
482 */
483 void removeIgnoreTask(Task task) {
484 mIgnoreTasks.remove(task.key);
485 }
486
487 /**
488 * Returns whether the specified {@param task} is ignored.
489 */
490 boolean isIgnoredTask(Task task) {
491 return mIgnoreTasks.contains(task.key);
492 }
493
494 /**
495 * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
496 * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
497 * visible range includes all tasks at the target stack scroll. This is useful for ensure that
498 * all views necessary for a transition or animation will be visible at the start.
499 *
500 * This call ignores freeform tasks.
501 *
502 * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
503 * match the size of {@param tasks}
504 * @param tasks The set of tasks for which to generate transforms
505 * @param curStackScroll The current stack scroll
506 * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
507 * The range of the union of the visible views at the current and
508 * target stack scrolls will be returned.
509 * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
510 * Transforms will still be calculated for the ignore tasks.
Winsonc4387022016-02-24 12:05:26 -0800511 * @return the front and back most visible task indices (there may be non visible tasks in
512 * between this range)
Winson8aa99592016-01-19 15:07:07 -0800513 */
Winsonc4387022016-02-24 12:05:26 -0800514 int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
Winson8aa99592016-01-19 15:07:07 -0800515 ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
Winsone693aaf2016-03-01 12:05:59 -0800516 ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
Winson Chungc6a16232014-04-01 14:04:48 -0700517 int taskCount = tasks.size();
Winsonc4387022016-02-24 12:05:26 -0800518 int[] visibleTaskRange = mTmpIntPair;
519 visibleTaskRange[0] = -1;
520 visibleTaskRange[1] = -1;
Winson8aa99592016-01-19 15:07:07 -0800521 boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
Winson Chungc9567c02014-06-16 20:25:51 -0700522
523 // We can reuse the task transforms where possible to reduce object allocation
Winsonc5fd3502016-01-18 15:18:37 -0800524 Utilities.matchTaskListSize(tasks, taskTransforms);
Winson Chungc9567c02014-06-16 20:25:51 -0700525
526 // Update the stack transforms
Winson4993c2f2015-11-19 10:06:06 -0800527 TaskViewTransform frontTransform = null;
Winson8aa99592016-01-19 15:07:07 -0800528 TaskViewTransform frontTransformAtTarget = null;
529 TaskViewTransform transform = null;
530 TaskViewTransform transformAtTarget = null;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700531 for (int i = taskCount - 1; i >= 0; i--) {
Winsona5e6b362015-11-02 17:17:20 -0800532 Task task = tasks.get(i);
Winson8aa99592016-01-19 15:07:07 -0800533
534 // Calculate the current and (if necessary) the target transform for the task
535 transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
Jiaquan He26f637b2016-12-27 14:44:14 -0800536 taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800537 if (useTargetStackScroll && !transform.visible) {
538 // If we have a target stack scroll and the task is not currently visible, then we
539 // just update the transform at the new scroll
540 // TODO: Optimize this
Jiaquan Hec0a1acd2016-12-13 13:46:46 -0800541 transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
Jiaquan He26f637b2016-12-27 14:44:14 -0800542 new TaskViewTransform(), frontTransformAtTarget);
Winson8aa99592016-01-19 15:07:07 -0800543 if (transformAtTarget.visible) {
544 transform.copyFrom(transformAtTarget);
545 }
Winson3e874742016-01-07 10:08:17 -0800546 }
547
Winson8aa99592016-01-19 15:07:07 -0800548 // For ignore tasks, only calculate the stack transform and skip the calculation of the
549 // visible stack indices
550 if (ignoreTasksSet.contains(task.key)) {
551 continue;
552 }
Winsonf24f2162016-01-05 12:11:55 -0800553
554 // For freeform tasks, only calculate the stack transform and skip the calculation of
555 // the visible stack indices
Winsona5e6b362015-11-02 17:17:20 -0800556 if (task.isFreeformTask()) {
557 continue;
558 }
559
Winson4993c2f2015-11-19 10:06:06 -0800560 frontTransform = transform;
Winson8aa99592016-01-19 15:07:07 -0800561 frontTransformAtTarget = transformAtTarget;
Winsonc4387022016-02-24 12:05:26 -0800562 if (transform.visible) {
563 if (visibleTaskRange[0] < 0) {
564 visibleTaskRange[0] = i;
565 }
566 visibleTaskRange[1] = i;
567 }
Winson Chungc6a16232014-04-01 14:04:48 -0700568 }
Winsonc4387022016-02-24 12:05:26 -0800569 return visibleTaskRange;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700570 }
571
Winsonf24f2162016-01-05 12:11:55 -0800572 /**
Winson8aa99592016-01-19 15:07:07 -0800573 * Binds the visible {@link TaskView}s at the given target scroll.
Winsonf24f2162016-01-05 12:11:55 -0800574 */
Winson8aa99592016-01-19 15:07:07 -0800575 void bindVisibleTaskViews(float targetStackScroll) {
Winsond2a03062016-04-15 11:19:07 -0700576 bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800577 }
578
579 /**
580 * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
581 * current {@link TaskStack}. This call does not continue on to update their position to the
582 * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
583 * be added/removed from the view hierarchy and placed in the correct Z order and initial
584 * position (if not currently on screen).
585 *
586 * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
587 * includes those visible at the current stack scroll, and all at the
588 * target stack scroll.
Winsone693aaf2016-03-01 12:05:59 -0800589 * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
590 * tasks at their non-overridden task progress
Winson8aa99592016-01-19 15:07:07 -0800591 */
Winsond2a03062016-04-15 11:19:07 -0700592 void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800593 // Get all the task transforms
Winsonc4387022016-02-24 12:05:26 -0800594 ArrayList<Task> tasks = mStack.getStackTasks();
595 int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
Winsond2a03062016-04-15 11:19:07 -0700596 mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
Winsone693aaf2016-03-01 12:05:59 -0800597 ignoreTaskOverrides);
Winsonf24f2162016-01-05 12:11:55 -0800598
599 // Return all the invisible children to the pool
Winson55003902016-01-12 12:00:37 -0800600 mTmpTaskViewMap.clear();
Winson8aa99592016-01-19 15:07:07 -0800601 List<TaskView> taskViews = getTaskViews();
602 int lastFocusedTaskIndex = -1;
603 int taskViewCount = taskViews.size();
Winsonf24f2162016-01-05 12:11:55 -0800604 for (int i = taskViewCount - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800605 TaskView tv = taskViews.get(i);
606 Task task = tv.getTask();
Winsonf24f2162016-01-05 12:11:55 -0800607
Winson3e874742016-01-07 10:08:17 -0800608 // Skip ignored tasks
Winsond2a03062016-04-15 11:19:07 -0700609 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800610 continue;
611 }
612
Winson1d5ff7e2016-03-04 11:21:09 -0800613 // It is possible for the set of lingering TaskViews to differ from the stack if the
614 // stack was updated before the relayout. If the task view is no longer in the stack,
615 // then just return it back to the view pool.
616 int taskIndex = mStack.indexOfStackTask(task);
617 TaskViewTransform transform = null;
618 if (taskIndex != -1) {
619 transform = mCurrentTaskTransforms.get(taskIndex);
620 }
621
622 if (task.isFreeformTask() || (transform != null && transform.visible)) {
Winson55003902016-01-12 12:00:37 -0800623 mTmpTaskViewMap.put(task.key, tv);
Winsonf24f2162016-01-05 12:11:55 -0800624 } else {
Winsonf0009882016-06-01 12:22:55 -0700625 if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
Winsonf24f2162016-01-05 12:11:55 -0800626 lastFocusedTaskIndex = taskIndex;
627 resetFocusedTask(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800628 }
Winsonf24f2162016-01-05 12:11:55 -0800629 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -0800630 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800631 }
Winsonf24f2162016-01-05 12:11:55 -0800632
633 // Pick up all the newly visible children
Winsonc4387022016-02-24 12:05:26 -0800634 for (int i = tasks.size() - 1; i >= 0; i--) {
Winson8aa99592016-01-19 15:07:07 -0800635 Task task = tasks.get(i);
636 TaskViewTransform transform = mCurrentTaskTransforms.get(i);
Winsonf24f2162016-01-05 12:11:55 -0800637
Winson3e874742016-01-07 10:08:17 -0800638 // Skip ignored tasks
Winsond2a03062016-04-15 11:19:07 -0700639 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800640 continue;
641 }
642
Winsonf24f2162016-01-05 12:11:55 -0800643 // Skip the invisible non-freeform stack tasks
Winson05e46ca2016-02-05 15:40:29 -0800644 if (!task.isFreeformTask() && !transform.visible) {
Winsonf24f2162016-01-05 12:11:55 -0800645 continue;
646 }
647
Winson55003902016-01-12 12:00:37 -0800648 TaskView tv = mTmpTaskViewMap.get(task.key);
Winsonf24f2162016-01-05 12:11:55 -0800649 if (tv == null) {
650 tv = mViewPool.pickUpViewFromPool(task, task);
651 if (task.isFreeformTask()) {
Winsonc69249f2016-03-28 13:38:39 -0700652 updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800653 } else {
Winson68088812016-02-12 16:06:04 -0800654 if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
Winsonc69249f2016-03-28 13:38:39 -0700655 updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
656 AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800657 } else {
Winsonc69249f2016-03-28 13:38:39 -0700658 updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
659 AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -0800660 }
661 }
662 } else {
663 // Reattach it in the right z order
664 final int taskIndex = mStack.indexOfStackTask(task);
665 final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
666 if (insertIndex != getTaskViews().indexOf(tv)){
667 detachViewFromParent(tv);
668 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
669 updateTaskViewsList();
670 }
671 }
672 }
673
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +0100674 updatePrefetchingTask(tasks, visibleTaskRange[0], visibleTaskRange[1]);
675
Winsonf24f2162016-01-05 12:11:55 -0800676 // Update the focus if the previous focused task was returned to the view pool
677 if (lastFocusedTaskIndex != -1) {
Winsonf0009882016-06-01 12:22:55 -0700678 int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
679 ? visibleTaskRange[1]
680 : visibleTaskRange[0];
681 setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
682 true /* requestViewFocus */);
683 TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
684 if (focusedTaskView != null) {
685 focusedTaskView.requestAccessibilityFocus();
Winsonf24f2162016-01-05 12:11:55 -0800686 }
687 }
688 }
689
690 /**
Winson27c28f82016-05-05 16:16:50 -0700691 * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
Winsonf24f2162016-01-05 12:11:55 -0800692 */
Winson67c79572016-04-13 14:02:18 -0700693 public void relayoutTaskViews(AnimationProps animation) {
Winson27c28f82016-05-05 16:16:50 -0700694 relayoutTaskViews(animation, null /* animationOverrides */,
695 false /* ignoreTaskOverrides */);
Winson59924fe2016-03-17 14:13:18 -0700696 }
697
698 /**
Winson8aa99592016-01-19 15:07:07 -0800699 * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
700 * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
701 * animations that are current running on those task views, and will ensure that the children
Winson27c28f82016-05-05 16:16:50 -0700702 * {@link TaskView}s will match the set of visible tasks in the stack. If a {@link Task} has
703 * an animation provided in {@param animationOverrides}, that will be used instead.
Winson8aa99592016-01-19 15:07:07 -0800704 */
Winson27c28f82016-05-05 16:16:50 -0700705 private void relayoutTaskViews(AnimationProps animation,
706 ArrayMap<Task, AnimationProps> animationOverrides,
707 boolean ignoreTaskOverrides) {
Winsonf24f2162016-01-05 12:11:55 -0800708 // If we had a deferred animation, cancel that
Winsoneca47ef2016-04-21 16:48:50 -0700709 cancelDeferredTaskViewLayoutAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800710
Winson8aa99592016-01-19 15:07:07 -0800711 // Synchronize the current set of TaskViews
Winsond2a03062016-04-15 11:19:07 -0700712 bindVisibleTaskViews(mStackScroller.getStackScroll(),
Winson59924fe2016-03-17 14:13:18 -0700713 ignoreTaskOverrides /* ignoreTaskOverrides */);
Winsonf24f2162016-01-05 12:11:55 -0800714
715 // Animate them to their final transforms with the given animation
716 List<TaskView> taskViews = getTaskViews();
717 int taskViewCount = taskViews.size();
718 for (int i = 0; i < taskViewCount; i++) {
Winson43336942016-03-07 14:52:59 -0800719 TaskView tv = taskViews.get(i);
Winson27c28f82016-05-05 16:16:50 -0700720 Task task = tv.getTask();
Winsonf24f2162016-01-05 12:11:55 -0800721
Winson27c28f82016-05-05 16:16:50 -0700722 if (mIgnoreTasks.contains(task.key)) {
Winson3e874742016-01-07 10:08:17 -0800723 continue;
724 }
725
Winson448c0ad2016-10-19 09:53:59 -0700726 int taskIndex = mStack.indexOfStackTask(task);
727 TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
Winson27c28f82016-05-05 16:16:50 -0700728 if (animationOverrides != null && animationOverrides.containsKey(task)) {
729 animation = animationOverrides.get(task);
730 }
731
Winsonf24f2162016-01-05 12:11:55 -0800732 updateTaskViewToTransform(tv, transform, animation);
733 }
734 }
735
736 /**
737 * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
738 */
Winsonbe8e6962016-02-01 14:27:52 -0800739 void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
Winson8aa99592016-01-19 15:07:07 -0800740 mDeferredTaskViewLayoutAnimation = animation;
Winson1c846142016-01-22 11:34:38 -0800741 invalidate();
Winsonf24f2162016-01-05 12:11:55 -0800742 }
743
744 /**
745 * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
Winsonbe8e6962016-02-01 14:27:52 -0800746 * given set of {@link AnimationProps} properties.
Winsonf24f2162016-01-05 12:11:55 -0800747 */
748 public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
Winsonbe8e6962016-02-01 14:27:52 -0800749 AnimationProps animation) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500750 if (taskView.isAnimatingTo(transform)) {
751 return;
752 }
753 taskView.cancelTransformAnimation();
Winsonf24f2162016-01-05 12:11:55 -0800754 taskView.updateViewPropertiesToTaskTransform(transform, animation,
755 mRequestUpdateClippingListener);
756 }
757
758 /**
Winson8aa99592016-01-19 15:07:07 -0800759 * Returns the current task transforms of all tasks, falling back to the stack layout if there
760 * is no {@link TaskView} for the task.
Winsonf24f2162016-01-05 12:11:55 -0800761 */
Winson8aa99592016-01-19 15:07:07 -0800762 public void getCurrentTaskTransforms(ArrayList<Task> tasks,
763 ArrayList<TaskViewTransform> transformsOut) {
764 Utilities.matchTaskListSize(tasks, transformsOut);
Winson66474132016-02-23 18:45:47 -0800765 int focusState = mLayoutAlgorithm.getFocusState();
Winson8aa99592016-01-19 15:07:07 -0800766 for (int i = tasks.size() - 1; i >= 0; i--) {
767 Task task = tasks.get(i);
768 TaskViewTransform transform = transformsOut.get(i);
769 TaskView tv = getChildViewForTask(task);
770 if (tv != null) {
771 transform.fillIn(tv);
772 } else {
773 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
Winsone693aaf2016-03-01 12:05:59 -0800774 focusState, transform, null, true /* forceUpdate */,
Jiaquan He26f637b2016-12-27 14:44:14 -0800775 false /* ignoreTaskOverrides */);
Winson8aa99592016-01-19 15:07:07 -0800776 }
777 transform.visible = true;
778 }
779 }
780
781 /**
782 * Returns the task transforms for all the tasks in the stack if the stack was at the given
Winson14991502016-02-15 15:40:08 -0800783 * {@param stackScroll} and {@param focusState}.
Winson8aa99592016-01-19 15:07:07 -0800784 */
Winson66474132016-02-23 18:45:47 -0800785 public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
Winsond2a03062016-04-15 11:19:07 -0700786 boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
Winson8aa99592016-01-19 15:07:07 -0800787 Utilities.matchTaskListSize(tasks, transformsOut);
788 for (int i = tasks.size() - 1; i >= 0; i--) {
789 Task task = tasks.get(i);
790 TaskViewTransform transform = transformsOut.get(i);
Winson14991502016-02-15 15:40:08 -0800791 mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
Jiaquan He26f637b2016-12-27 14:44:14 -0800792 true /* forceUpdate */, ignoreTaskOverrides);
Winson8aa99592016-01-19 15:07:07 -0800793 transform.visible = true;
794 }
795 }
796
797 /**
Winson05e46ca2016-02-05 15:40:29 -0800798 * Cancels the next deferred task view layout.
799 */
800 void cancelDeferredTaskViewLayoutAnimation() {
801 mDeferredTaskViewLayoutAnimation = null;
802 }
803
804 /**
Winson8aa99592016-01-19 15:07:07 -0800805 * Cancels all {@link TaskView} animations.
Winson8aa99592016-01-19 15:07:07 -0800806 */
807 void cancelAllTaskViewAnimations() {
Winsonf24f2162016-01-05 12:11:55 -0800808 List<TaskView> taskViews = getTaskViews();
Winson3e874742016-01-07 10:08:17 -0800809 for (int i = taskViews.size() - 1; i >= 0; i--) {
Winsonf24f2162016-01-05 12:11:55 -0800810 final TaskView tv = taskViews.get(i);
Winsoneca47ef2016-04-21 16:48:50 -0700811 if (!mIgnoreTasks.contains(tv.getTask().key)) {
Winson8aa99592016-01-19 15:07:07 -0800812 tv.cancelTransformAnimation();
813 }
Winsonf24f2162016-01-05 12:11:55 -0800814 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800815 }
816
Winson3150e572015-10-23 15:07:24 -0700817 /**
818 * Updates the clip for each of the task views from back to front.
819 */
Winsonf24f2162016-01-05 12:11:55 -0800820 private void clipTaskViews() {
Jiaquan He5a2eb832016-12-13 14:36:37 -0800821 // We never clip task views in grid layout
822 if (Recents.getConfiguration().isGridEnabled) {
823 return;
824 }
825
Winson Chung93748a12014-07-13 17:43:31 -0700826 // Update the clip on each task child
Winson Chung6ac8bd62015-01-07 16:38:35 -0800827 List<TaskView> taskViews = getTaskViews();
Winson3150e572015-10-23 15:07:24 -0700828 TaskView tmpTv = null;
Winson8aa99592016-01-19 15:07:07 -0800829 TaskView prevVisibleTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800830 int taskViewCount = taskViews.size();
Winson3150e572015-10-23 15:07:24 -0700831 for (int i = 0; i < taskViewCount; i++) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800832 TaskView tv = taskViews.get(i);
Winson3150e572015-10-23 15:07:24 -0700833 TaskView frontTv = null;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800834 int clipBottom = 0;
Winson8aa99592016-01-19 15:07:07 -0800835
Winson05e46ca2016-02-05 15:40:29 -0800836 if (isIgnoredTask(tv.getTask())) {
Winson8aa99592016-01-19 15:07:07 -0800837 // For each of the ignore tasks, update the translationZ of its TaskView to be
838 // between the translationZ of the tasks immediately underneath it
839 if (prevVisibleTv != null) {
840 tv.setTranslationZ(Math.max(tv.getTranslationZ(),
841 prevVisibleTv.getTranslationZ() + 0.1f));
842 }
843 }
844
Winson3150e572015-10-23 15:07:24 -0700845 if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800846 // Find the next view to clip against
Winson3150e572015-10-23 15:07:24 -0700847 for (int j = i + 1; j < taskViewCount; j++) {
848 tmpTv = taskViews.get(j);
Winsonef064132016-01-05 12:11:31 -0800849
Winson3150e572015-10-23 15:07:24 -0700850 if (tmpTv.shouldClipViewInStack()) {
851 frontTv = tmpTv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800852 break;
Winson Chung93748a12014-07-13 17:43:31 -0700853 }
854 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800855
856 // Clip against the next view, this is just an approximation since we are
857 // stacked and we can make assumptions about the visibility of the this
858 // task relative to the ones in front of it.
Winson3150e572015-10-23 15:07:24 -0700859 if (frontTv != null) {
Winsonbb410952015-12-04 14:34:11 -0800860 float taskBottom = tv.getBottom();
861 float frontTaskTop = frontTv.getTop();
Winson3150e572015-10-23 15:07:24 -0700862 if (frontTaskTop < taskBottom) {
863 // Map the stack view space coordinate (the rects) to view space
Winsonbb410952015-12-04 14:34:11 -0800864 clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
Winson3150e572015-10-23 15:07:24 -0700865 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800866 }
Winson Chung93748a12014-07-13 17:43:31 -0700867 }
Winsonf24f2162016-01-05 12:11:55 -0800868 tv.getViewBounds().setClipBottom(clipBottom);
Winsone693aaf2016-03-01 12:05:59 -0800869 tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
Winson8aa99592016-01-19 15:07:07 -0800870 prevVisibleTv = tv;
Winson Chung6ac8bd62015-01-07 16:38:35 -0800871 }
Winsonf24f2162016-01-05 12:11:55 -0800872 mTaskViewsClipDirty = false;
Winson Chung93748a12014-07-13 17:43:31 -0700873 }
874
Jorim Jaggicdef5912017-04-03 17:24:19 +0200875 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
876 updateLayoutAlgorithm(boundScrollToNewMinMax, Recents.getConfiguration().getLaunchState());
877 }
878
Winson3e874742016-01-07 10:08:17 -0800879 /**
Winson8aa99592016-01-19 15:07:07 -0800880 * Updates the layout algorithm min and max virtual scroll bounds.
Winson8aa99592016-01-19 15:07:07 -0800881 */
Jorim Jaggicdef5912017-04-03 17:24:19 +0200882 public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
883 RecentsActivityLaunchState launchState) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800884 // Compute the min and max scroll values
Jorim Jaggicdef5912017-04-03 17:24:19 +0200885 mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState);
Winson Chung303e1ff2014-03-07 15:06:19 -0800886
Winson8aa99592016-01-19 15:07:07 -0800887 // Update the freeform workspace background
Winsona5e6b362015-11-02 17:17:20 -0800888 SystemServicesProxy ssp = Recents.getSystemServices();
889 if (ssp.hasFreeformWorkspaceSupport()) {
890 mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
Winsona5e6b362015-11-02 17:17:20 -0800891 mFreeformWorkspaceBackground.setBounds(mTmpRect);
892 }
893
Winson Chung303e1ff2014-03-07 15:06:19 -0800894 if (boundScrollToNewMinMax) {
Winson Chung012ef362014-07-31 18:36:25 -0700895 mStackScroller.boundScroll();
Winson Chung303e1ff2014-03-07 15:06:19 -0800896 }
897 }
898
Winson27c28f82016-05-05 16:16:50 -0700899 /**
900 * Updates the stack layout to its stable places.
901 */
902 private void updateLayoutToStableBounds() {
903 mWindowRect.set(mStableWindowRect);
904 mStackBounds.set(mStableStackBounds);
905 mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
906 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
907 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
908 updateLayoutAlgorithm(true /* boundScroll */);
909 }
910
Winson Chung012ef362014-07-31 18:36:25 -0700911 /** Returns the scroller. */
912 public TaskStackViewScroller getScroller() {
913 return mStackScroller;
914 }
915
Winson0d14d4d2015-10-26 17:05:04 -0700916 /**
917 * Sets the focused task to the provided (bounded taskIndex).
Winsone5f1faa2015-11-20 12:26:23 -0800918 *
919 * @return whether or not the stack will scroll as a part of this focus change
Winson0d14d4d2015-10-26 17:05:04 -0700920 */
Jiaquan He21f495f2017-01-05 13:00:29 -0800921 public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
Winsonaaf33bc2015-12-03 12:02:38 -0800922 final boolean requestViewFocus) {
Winson4b9cded2016-01-26 16:26:47 -0800923 return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
Peter Schillerb124d562015-12-11 21:31:17 -0800924 }
925
926 /**
Winson05e46ca2016-02-05 15:40:29 -0800927 * Sets the focused task to the provided (bounded focusTaskIndex).
Peter Schillerb124d562015-12-11 21:31:17 -0800928 *
929 * @return whether or not the stack will scroll as a part of this focus change
930 */
Jiaquan He21f495f2017-01-05 13:00:29 -0800931 public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
Winson05e46ca2016-02-05 15:40:29 -0800932 boolean requestViewFocus, int timerIndicatorDuration) {
Winson0d14d4d2015-10-26 17:05:04 -0700933 // Find the next task to focus
Winson4b057c62016-01-12 15:01:52 -0800934 int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
Winson68088812016-02-12 16:06:04 -0800935 Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
Winson0d14d4d2015-10-26 17:05:04 -0700936 final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
Winson250608a2015-11-24 15:00:31 -0800937 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700938
Winson0d14d4d2015-10-26 17:05:04 -0700939 // Reset the last focused task state if changed
Winsonaaf33bc2015-12-03 12:02:38 -0800940 if (mFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800941 // Cancel the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800942 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800943 final TaskView tv = getChildViewForTask(mFocusedTask);
944 if (tv != null) {
945 tv.getHeaderView().cancelFocusTimerIndicator();
946 }
947 }
Winsonb433c5b2016-01-20 17:11:29 -0800948
949 resetFocusedTask(mFocusedTask);
Winson0d14d4d2015-10-26 17:05:04 -0700950 }
951
Winsone5f1faa2015-11-20 12:26:23 -0800952 boolean willScroll = false;
Winsonaaf33bc2015-12-03 12:02:38 -0800953 mFocusedTask = newFocusedTask;
Peter Schillerb124d562015-12-11 21:31:17 -0800954
Winsonaaf33bc2015-12-03 12:02:38 -0800955 if (newFocusedTask != null) {
Peter Schillerb124d562015-12-11 21:31:17 -0800956 // Start the timer indicator, if applicable
Winson4b9cded2016-01-26 16:26:47 -0800957 if (timerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -0800958 final TaskView tv = getChildViewForTask(mFocusedTask);
959 if (tv != null) {
Winson4b9cded2016-01-26 16:26:47 -0800960 tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
Peter Schillerb124d562015-12-11 21:31:17 -0800961 } else {
962 // The view is null; set a flag for later
Winson4b9cded2016-01-26 16:26:47 -0800963 mStartTimerIndicatorDuration = timerIndicatorDuration;
Peter Schillerb124d562015-12-11 21:31:17 -0800964 }
965 }
966
Winson0d14d4d2015-10-26 17:05:04 -0700967 if (scrollToTask) {
Winson1c846142016-01-22 11:34:38 -0800968 // Cancel any running enter animations at this point when we scroll or change focus
969 if (!mEnterAnimationComplete) {
970 cancelAllTaskViewAnimations();
971 }
972
Winsone693aaf2016-03-01 12:05:59 -0800973 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winson05e46ca2016-02-05 15:40:29 -0800974 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
975 requestViewFocus);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700976 } else {
Winson05e46ca2016-02-05 15:40:29 -0800977 // Focus the task view
978 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
979 if (newFocusedTaskView != null) {
980 newFocusedTaskView.setFocusedState(true, requestViewFocus);
981 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700982 }
Jiaquan He21f495f2017-01-05 13:00:29 -0800983 // Any time a task view gets the focus, we move the focus frame around it.
984 if (mTaskViewFocusFrame != null) {
985 mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
986 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700987 }
Winsone5f1faa2015-11-20 12:26:23 -0800988 return willScroll;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700989 }
990
Winson Chungd213a1e2014-10-02 11:18:30 -0700991 /**
Winson0d14d4d2015-10-26 17:05:04 -0700992 * Sets the focused task relative to the currently focused task.
993 *
Winsone5f1faa2015-11-20 12:26:23 -0800994 * @param forward whether to go to the next task in the stack (along the curve) or the previous
Winson1b585612015-11-06 09:16:26 -0800995 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
996 * if the currently focused task is not a stack task, will set the focus
997 * to the first visible stack task
Winson0d14d4d2015-10-26 17:05:04 -0700998 * @param animated determines whether to actually draw the highlight along with the change in
999 * focus.
Winson Chungd213a1e2014-10-02 11:18:30 -07001000 */
Winson1b585612015-11-06 09:16:26 -08001001 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
Winsonf0009882016-06-01 12:22:55 -07001002 setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
Peter Schillerb124d562015-12-11 21:31:17 -08001003 }
1004
1005 /**
1006 * Sets the focused task relative to the currently focused task.
1007 *
1008 * @param forward whether to go to the next task in the stack (along the curve) or the previous
1009 * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
1010 * if the currently focused task is not a stack task, will set the focus
1011 * to the first visible stack task
1012 * @param animated determines whether to actually draw the highlight along with the change in
1013 * focus.
1014 * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
1015 * happens.
Winson4b9cded2016-01-26 16:26:47 -08001016 * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
Peter Schillerb124d562015-12-11 21:31:17 -08001017 */
1018 public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
Winsonf0009882016-06-01 12:22:55 -07001019 boolean cancelWindowAnimations, int timerIndicatorDuration) {
1020 Task focusedTask = getFocusedTask();
1021 int newIndex = mStack.indexOfStackTask(focusedTask);
1022 if (focusedTask != null) {
Winson1b585612015-11-06 09:16:26 -08001023 if (stackTasksOnly) {
Winson250608a2015-11-24 15:00:31 -08001024 List<Task> tasks = mStack.getStackTasks();
Winsonf0009882016-06-01 12:22:55 -07001025 if (focusedTask.isFreeformTask()) {
Winson1b585612015-11-06 09:16:26 -08001026 // Try and focus the front most stack task
1027 TaskView tv = getFrontMostTaskView(stackTasksOnly);
1028 if (tv != null) {
Winson250608a2015-11-24 15:00:31 -08001029 newIndex = mStack.indexOfStackTask(tv.getTask());
Winson1b585612015-11-06 09:16:26 -08001030 }
1031 } else {
1032 // Try the next task if it is a stack task
Winsonaaf33bc2015-12-03 12:02:38 -08001033 int tmpNewIndex = newIndex + (forward ? -1 : 1);
Winson1b585612015-11-06 09:16:26 -08001034 if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
1035 Task t = tasks.get(tmpNewIndex);
1036 if (!t.isFreeformTask()) {
1037 newIndex = tmpNewIndex;
1038 }
1039 }
1040 }
1041 } else {
Winson8b1871d2015-11-20 09:56:20 -08001042 // No restrictions, lets just move to the new task (looping forward/backwards if
1043 // necessary)
Winson4b057c62016-01-12 15:01:52 -08001044 int taskCount = mStack.getTaskCount();
Winsonaaf33bc2015-12-03 12:02:38 -08001045 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
Winson1b585612015-11-06 09:16:26 -08001046 }
1047 } else {
Winson23b0d3f2016-02-15 17:43:01 -08001048 // We don't have a focused task
1049 float stackScroll = mStackScroller.getStackScroll();
1050 ArrayList<Task> tasks = mStack.getStackTasks();
1051 int taskCount = tasks.size();
Jiaquan He21f495f2017-01-05 13:00:29 -08001052 if (useGridLayout()) {
1053 // For the grid layout, we directly set focus to the most recently used task
1054 // no matter we're moving forwards or backwards.
1055 newIndex = taskCount - 1;
Winson23b0d3f2016-02-15 17:43:01 -08001056 } else {
Jiaquan He21f495f2017-01-05 13:00:29 -08001057 // For the grid layout we pick a proper task to focus, according to the current
1058 // stack scroll.
1059 if (forward) {
1060 // Walk backwards and focus the next task smaller than the current stack scroll
1061 for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
1062 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1063 if (Float.compare(taskP, stackScroll) <= 0) {
1064 break;
1065 }
1066 }
1067 } else {
1068 // Walk forwards and focus the next task larger than the current stack scroll
1069 for (newIndex = 0; newIndex < taskCount; newIndex++) {
1070 float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
1071 if (Float.compare(taskP, stackScroll) >= 0) {
1072 break;
1073 }
Winson23b0d3f2016-02-15 17:43:01 -08001074 }
1075 }
Winson1b585612015-11-06 09:16:26 -08001076 }
1077 }
1078 if (newIndex != -1) {
Winsonf24f2162016-01-05 12:11:55 -08001079 boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
Winson4b9cded2016-01-26 16:26:47 -08001080 true /* requestViewFocus */, timerIndicatorDuration);
Winsone5f1faa2015-11-20 12:26:23 -08001081 if (willScroll && cancelWindowAnimations) {
1082 // As we iterate to the next/previous task, cancel any current/lagging window
1083 // transition animations
1084 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
1085 }
Winson1b585612015-11-06 09:16:26 -08001086 }
Winson Chunga0e88b52014-08-11 19:25:42 -07001087 }
1088
Winson0d14d4d2015-10-26 17:05:04 -07001089 /**
1090 * Resets the focused task.
1091 */
Jiaquan He21f495f2017-01-05 13:00:29 -08001092 public void resetFocusedTask(Task task) {
Winsona0731a12015-12-02 15:10:14 -08001093 if (task != null) {
1094 TaskView tv = getChildViewForTask(task);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001095 if (tv != null) {
Winsonf24f2162016-01-05 12:11:55 -08001096 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungfc33cdf2014-12-03 13:16:48 -08001097 }
Winson Chungb0a28ea2014-10-28 15:21:35 -07001098 }
Jiaquan He21f495f2017-01-05 13:00:29 -08001099 if (mTaskViewFocusFrame != null) {
1100 mTaskViewFocusFrame.moveGridTaskViewFocus(null);
1101 }
Winsonaaf33bc2015-12-03 12:02:38 -08001102 mFocusedTask = null;
Winson Chungb0a28ea2014-10-28 15:21:35 -07001103 }
1104
Winson142af422015-11-09 10:39:57 -08001105 /**
1106 * Returns the focused task.
1107 */
Jiaquan He21f495f2017-01-05 13:00:29 -08001108 public Task getFocusedTask() {
Winsonaaf33bc2015-12-03 12:02:38 -08001109 return mFocusedTask;
Winson142af422015-11-09 10:39:57 -08001110 }
1111
Winsonf0009882016-06-01 12:22:55 -07001112 /**
1113 * Returns the accessibility focused task.
1114 */
1115 Task getAccessibilityFocusedTask() {
1116 List<TaskView> taskViews = getTaskViews();
1117 int taskViewCount = taskViews.size();
1118 for (int i = 0; i < taskViewCount; i++) {
1119 TaskView tv = taskViews.get(i);
1120 if (Utilities.isDescendentAccessibilityFocused(tv)) {
1121 return tv.getTask();
1122 }
1123 }
1124 TaskView frontTv = getFrontMostTaskView(true /* stackTasksOnly */);
1125 if (frontTv != null) {
1126 return frontTv.getTask();
1127 }
1128 return null;
1129 }
1130
Winson Chung303e1ff2014-03-07 15:06:19 -08001131 @Override
Winson Chungee445952014-09-09 16:12:59 +02001132 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1133 super.onInitializeAccessibilityEvent(event);
Winson Chung6ac8bd62015-01-07 16:38:35 -08001134 List<TaskView> taskViews = getTaskViews();
1135 int taskViewCount = taskViews.size();
1136 if (taskViewCount > 0) {
1137 TaskView backMostTask = taskViews.get(0);
1138 TaskView frontMostTask = taskViews.get(taskViewCount - 1);
Winson250608a2015-11-24 15:00:31 -08001139 event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
1140 event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
Winson Chung296278a2015-12-17 12:09:02 -05001141 event.setContentDescription(frontMostTask.getTask().title);
Winson Chungee445952014-09-09 16:12:59 +02001142 }
Winson4b057c62016-01-12 15:01:52 -08001143 event.setItemCount(mStack.getTaskCount());
Winson59924fe2016-03-17 14:13:18 -07001144
1145 int stackHeight = mLayoutAlgorithm.mStackRect.height();
1146 event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
1147 event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
Winson Chungee445952014-09-09 16:12:59 +02001148 }
1149
1150 @Override
Winson Chung83ea6f72015-06-17 13:00:23 -07001151 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1152 super.onInitializeAccessibilityNodeInfo(info);
1153 List<TaskView> taskViews = getTaskViews();
1154 int taskViewCount = taskViews.size();
Winsonf0009882016-06-01 12:22:55 -07001155 if (taskViewCount > 1) {
1156 // Find the accessibility focused task
1157 Task focusedTask = getAccessibilityFocusedTask();
Winson Chung83ea6f72015-06-17 13:00:23 -07001158 info.setScrollable(true);
Winsonf0009882016-06-01 12:22:55 -07001159 int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);
Winsonaaf33bc2015-12-03 12:02:38 -08001160 if (focusedTaskIndex > 0) {
Winson Chung83ea6f72015-06-17 13:00:23 -07001161 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1162 }
Winsonf0009882016-06-01 12:22:55 -07001163 if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
1164 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1165 }
Winson Chung83ea6f72015-06-17 13:00:23 -07001166 }
1167 }
1168
1169 @Override
1170 public CharSequence getAccessibilityClassName() {
Winsonf0009882016-06-01 12:22:55 -07001171 return ScrollView.class.getName();
Winson Chung83ea6f72015-06-17 13:00:23 -07001172 }
1173
1174 @Override
1175 public boolean performAccessibilityAction(int action, Bundle arguments) {
1176 if (super.performAccessibilityAction(action, arguments)) {
1177 return true;
1178 }
Winsonf0009882016-06-01 12:22:55 -07001179 Task focusedTask = getAccessibilityFocusedTask();
1180 int taskIndex = mStack.indexOfStackTask(focusedTask);
1181 if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
1182 switch (action) {
1183 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
1184 setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
1185 0);
1186 return true;
1187 }
1188 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
1189 setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
1190 0);
1191 return true;
1192 }
Winson Chung83ea6f72015-06-17 13:00:23 -07001193 }
1194 }
1195 return false;
1196 }
1197
1198 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -08001199 public boolean onInterceptTouchEvent(MotionEvent ev) {
1200 return mTouchHandler.onInterceptTouchEvent(ev);
1201 }
1202
1203 @Override
1204 public boolean onTouchEvent(MotionEvent ev) {
1205 return mTouchHandler.onTouchEvent(ev);
1206 }
1207
1208 @Override
Winson Chungd213a1e2014-10-02 11:18:30 -07001209 public boolean onGenericMotionEvent(MotionEvent ev) {
1210 return mTouchHandler.onGenericMotionEvent(ev);
1211 }
1212
1213 @Override
Winson Chungd7b2cb12014-06-26 15:08:50 -07001214 public void computeScroll() {
Winsonf24f2162016-01-05 12:11:55 -08001215 if (mStackScroller.computeScroll()) {
1216 // Notify accessibility
1217 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +01001218 Recents.getTaskLoader().getHighResThumbnailLoader().setFlingingFast(
1219 mStackScroller.getScrollVelocity() > mFastFlingVelocity);
Winsonf24f2162016-01-05 12:11:55 -08001220 }
Winson8aa99592016-01-19 15:07:07 -08001221 if (mDeferredTaskViewLayoutAnimation != null) {
1222 relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
Winsonf24f2162016-01-05 12:11:55 -08001223 mTaskViewsClipDirty = true;
Winson8aa99592016-01-19 15:07:07 -08001224 mDeferredTaskViewLayoutAnimation = null;
Winsonf24f2162016-01-05 12:11:55 -08001225 }
1226 if (mTaskViewsClipDirty) {
1227 clipTaskViews();
1228 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001229 }
1230
Winson3e874742016-01-07 10:08:17 -08001231 /**
Winson8aa99592016-01-19 15:07:07 -08001232 * Computes the maximum number of visible tasks and thumbnails. Requires that
Winsoneca4ab62015-11-04 10:50:28 -08001233 * updateLayoutForStack() is called first.
Winson Chunga91c2932014-11-07 15:02:38 -08001234 */
Winson36a5a2c2015-10-29 18:04:39 -07001235 public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
Winson250608a2015-11-24 15:00:31 -08001236 return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
Winson Chunga91c2932014-11-07 15:02:38 -08001237 }
1238
Winson3e874742016-01-07 10:08:17 -08001239 /**
Winson59924fe2016-03-17 14:13:18 -07001240 * Updates the system insets.
Winson3e874742016-01-07 10:08:17 -08001241 */
Winson59924fe2016-03-17 14:13:18 -07001242 public void setSystemInsets(Rect systemInsets) {
Winson67c79572016-04-13 14:02:18 -07001243 boolean changed = false;
1244 changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
1245 changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
1246 if (changed) {
Winson49df4202016-01-25 17:33:34 -08001247 requestLayout();
1248 }
Winson147ecaf2015-09-16 16:49:55 -07001249 }
1250
Winson Chunga91c2932014-11-07 15:02:38 -08001251 /**
Winson Chunga4ccb862014-08-22 15:26:27 -07001252 * This is called with the full window width and height to allow stack view children to
Winson Chungdcfa7972014-07-22 12:27:13 -07001253 * perform the full screen transition down.
Winson Chungf7bca432014-04-30 17:11:13 -07001254 */
Winson Chung303e1ff2014-03-07 15:06:19 -08001255 @Override
1256 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson70f0bf72016-02-01 14:05:29 -08001257 mInMeasureLayout = true;
Winson Chung303e1ff2014-03-07 15:06:19 -08001258 int width = MeasureSpec.getSize(widthMeasureSpec);
1259 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -08001260
Winson59924fe2016-03-17 14:13:18 -07001261 // Update the stable stack bounds, but only update the current stack bounds if the stable
1262 // bounds have changed. This is because we may get spurious measures while dragging where
1263 // our current stack bounds reflect the target drop region.
Winsonfc48b072016-04-21 11:20:11 -07001264 mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
Winsoncbb625b2016-07-06 15:24:15 -07001265 mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
1266 mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
Winson59924fe2016-03-17 14:13:18 -07001267 if (!mTmpRect.equals(mStableStackBounds)) {
1268 mStableStackBounds.set(mTmpRect);
1269 mStackBounds.set(mTmpRect);
1270 mStableWindowRect.set(0, 0, width, height);
1271 mWindowRect.set(0, 0, width, height);
1272 }
1273
Winson8aa99592016-01-19 15:07:07 -08001274 // Compute the rects in the stack algorithm
Winsonfc48b072016-04-21 11:20:11 -07001275 mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds,
Winsonf9357d92016-03-25 15:14:37 -07001276 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winsonfc48b072016-04-21 11:20:11 -07001277 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08001278 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
Winson67c79572016-04-13 14:02:18 -07001279 updateLayoutAlgorithm(false /* boundScroll */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001280
Winsonf24f2162016-01-05 12:11:55 -08001281 // If this is the first layout, then scroll to the front of the stack, then update the
1282 // TaskViews with the stack so that we can lay them out
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001283 boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
1284 && mResetToInitialStateWhenResized;
1285 if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE
1286 || resetToInitialState) {
1287 if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
Winson67c79572016-04-13 14:02:18 -07001288 updateToInitialState();
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001289 mResetToInitialStateWhenResized = false;
Winson67c79572016-04-13 14:02:18 -07001290 }
Winson670ea712016-04-12 17:02:26 -07001291 if (!mAwaitingFirstLayout) {
1292 mInitialState = INITIAL_STATE_UPDATE_NONE;
1293 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001294 }
Winsonbd53a312016-09-21 12:13:32 -07001295 // If we got the launch-next event before the first layout pass, then re-send it after the
1296 // initial state has been updated
1297 if (mLaunchNextAfterFirstMeasure) {
1298 mLaunchNextAfterFirstMeasure = false;
1299 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
1300 }
Winsone693aaf2016-03-01 12:05:59 -08001301
Winson8aa99592016-01-19 15:07:07 -08001302 // Rebind all the views, including the ignore ones
Winsond2a03062016-04-15 11:19:07 -07001303 bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
Winson Chung303e1ff2014-03-07 15:06:19 -08001304
Winson Chungdcfa7972014-07-22 12:27:13 -07001305 // Measure each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001306 mTmpTaskViews.clear();
1307 mTmpTaskViews.addAll(getTaskViews());
1308 mTmpTaskViews.addAll(mViewPool.getViews());
1309 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001310 for (int i = 0; i < taskViewCount; i++) {
Winson70f0bf72016-02-01 14:05:29 -08001311 measureTaskView(mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001312 }
Jiaquan He21f495f2017-01-05 13:00:29 -08001313 if (mTaskViewFocusFrame != null) {
1314 mTaskViewFocusFrame.measure();
1315 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001316
1317 setMeasuredDimension(width, height);
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001318 mLastWidth = width;
1319 mLastHeight = height;
Winson70f0bf72016-02-01 14:05:29 -08001320 mInMeasureLayout = false;
1321 }
1322
1323 /**
1324 * Measures a TaskView.
1325 */
1326 private void measureTaskView(TaskView tv) {
Winson67c79572016-04-13 14:02:18 -07001327 Rect padding = new Rect();
Winson70f0bf72016-02-01 14:05:29 -08001328 if (tv.getBackground() != null) {
Winson67c79572016-04-13 14:02:18 -07001329 tv.getBackground().getPadding(padding);
Winson70f0bf72016-02-01 14:05:29 -08001330 }
Jiaquan He8f34df42016-12-27 15:38:44 -08001331 mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
1332 mTmpRect.union(mLayoutAlgorithm.getTaskRect());
Winson70f0bf72016-02-01 14:05:29 -08001333 tv.measure(
Winson67c79572016-04-13 14:02:18 -07001334 MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
Winson70f0bf72016-02-01 14:05:29 -08001335 MeasureSpec.EXACTLY),
Winson67c79572016-04-13 14:02:18 -07001336 MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
Winson70f0bf72016-02-01 14:05:29 -08001337 MeasureSpec.EXACTLY));
Winson Chung303e1ff2014-03-07 15:06:19 -08001338 }
1339
1340 @Override
1341 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson36a5a2c2015-10-29 18:04:39 -07001342 // Layout each of the TaskViews
Winsona2236f12015-11-13 16:10:01 -08001343 mTmpTaskViews.clear();
1344 mTmpTaskViews.addAll(getTaskViews());
1345 mTmpTaskViews.addAll(mViewPool.getViews());
1346 int taskViewCount = mTmpTaskViews.size();
Winson Chung6ac8bd62015-01-07 16:38:35 -08001347 for (int i = 0; i < taskViewCount; i++) {
Winsonc69249f2016-03-28 13:38:39 -07001348 layoutTaskView(changed, mTmpTaskViews.get(i));
Winson Chung303e1ff2014-03-07 15:06:19 -08001349 }
Jiaquan He21f495f2017-01-05 13:00:29 -08001350 if (mTaskViewFocusFrame != null) {
1351 mTaskViewFocusFrame.layout();
1352 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001353
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001354 if (changed) {
Winsona2236f12015-11-13 16:10:01 -08001355 if (mStackScroller.isScrollOutOfBounds()) {
1356 mStackScroller.boundScroll();
1357 }
Winsonf24f2162016-01-05 12:11:55 -08001358 }
Winsonc69249f2016-03-28 13:38:39 -07001359
Winson8aa99592016-01-19 15:07:07 -08001360 // Relayout all of the task views including the ignored ones
Winson67c79572016-04-13 14:02:18 -07001361 relayoutTaskViews(AnimationProps.IMMEDIATE);
Winsonf24f2162016-01-05 12:11:55 -08001362 clipTaskViews();
1363
1364 if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
1365 mAwaitingFirstLayout = false;
Winson670ea712016-04-12 17:02:26 -07001366 mInitialState = INITIAL_STATE_UPDATE_NONE;
Winsonf24f2162016-01-05 12:11:55 -08001367 onFirstLayout();
Jorim Jaggi7af8ea82015-11-09 15:28:34 +01001368 }
Winson Chungdcfa7972014-07-22 12:27:13 -07001369 }
Winson Chung24cf1522014-05-29 12:03:33 -07001370
Winson70f0bf72016-02-01 14:05:29 -08001371 /**
1372 * Lays out a TaskView.
1373 */
Winsonc69249f2016-03-28 13:38:39 -07001374 private void layoutTaskView(boolean changed, TaskView tv) {
1375 if (changed) {
Winson67c79572016-04-13 14:02:18 -07001376 Rect padding = new Rect();
Winsonc69249f2016-03-28 13:38:39 -07001377 if (tv.getBackground() != null) {
Winson67c79572016-04-13 14:02:18 -07001378 tv.getBackground().getPadding(padding);
Winsonc69249f2016-03-28 13:38:39 -07001379 }
Jiaquan He8f34df42016-12-27 15:38:44 -08001380 mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
1381 mTmpRect.union(mLayoutAlgorithm.getTaskRect());
Winsonc69249f2016-03-28 13:38:39 -07001382 tv.cancelTransformAnimation();
Winson67c79572016-04-13 14:02:18 -07001383 tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
1384 mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
Winson70f0bf72016-02-01 14:05:29 -08001385 } else {
Winsonc69249f2016-03-28 13:38:39 -07001386 // If the layout has not changed, then just lay it out again in-place
1387 tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
Winson70f0bf72016-02-01 14:05:29 -08001388 }
Winson70f0bf72016-02-01 14:05:29 -08001389 }
1390
Winson Chungdcfa7972014-07-22 12:27:13 -07001391 /** Handler for the first layout. */
1392 void onFirstLayout() {
Winsonf24f2162016-01-05 12:11:55 -08001393 // Setup the view for the enter animation
1394 mAnimationHelper.prepareForEnterAnimation();
Winson Chung083baf92014-07-11 10:32:42 -07001395
Winsona78a8f32015-12-03 10:55:01 -08001396 // Animate in the freeform workspace
Winsonbe8e6962016-02-01 14:27:52 -08001397 int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
1398 animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
1399 Interpolators.FAST_OUT_SLOW_IN));
Winsona78a8f32015-12-03 10:55:01 -08001400
Winson0d14d4d2015-10-26 17:05:04 -07001401 // Set the task focused state without requesting view focus, and leave the focus animations
1402 // until after the enter-animation
Winson5da43472015-11-04 17:39:55 -08001403 RecentsConfiguration config = Recents.getConfiguration();
1404 RecentsActivityLaunchState launchState = config.getLaunchState();
Jiaquan He21f495f2017-01-05 13:00:29 -08001405
1406 // We set the initial focused task view iff the following conditions are satisfied:
1407 // 1. Recents is showing task views in stack layout.
1408 // 2. Recents is launched with ALT + TAB.
1409 boolean setFocusOnFirstLayout = !useGridLayout() ||
1410 Recents.getConfiguration().getLaunchState().launchedWithAltTab;
1411 if (setFocusOnFirstLayout) {
1412 int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
1413 useGridLayout());
1414 if (focusedTaskIndex != -1) {
1415 setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
1416 false /* requestViewFocus */);
1417 }
Winson0983e022015-10-13 17:21:42 -07001418 }
Manu Cornet5db30f02016-12-19 17:02:59 -08001419 updateStackActionButtonVisibility();
Winson Chung24cf1522014-05-29 12:03:33 -07001420 }
1421
Winson671e8f92016-01-12 13:16:56 -08001422 public boolean isTouchPointInView(float x, float y, TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001423 mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
1424 mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
1425 return mTmpRect.contains((int) x, (int) y);
1426 }
1427
1428 /**
1429 * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
1430 * calculating the scroll position before and after a layout change.
1431 */
1432 public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
1433 for (int i = tasks.size() - 1; i >= 0; i--) {
1434 Task task = tasks.get(i);
1435
1436 // Ignore deleting tasks
Winson05e46ca2016-02-05 15:40:29 -08001437 if (isIgnoredTask(task)) {
Winson8aa99592016-01-19 15:07:07 -08001438 if (i == tasks.size() - 1) {
1439 isFrontMostTask.value = true;
1440 }
1441 continue;
1442 }
1443 return task;
1444 }
1445 return null;
Winson Chung303e1ff2014-03-07 15:06:19 -08001446 }
1447
Jorim Jaggi900fb482015-06-02 15:07:33 -07001448 @Override
Winsonbe8e6962016-02-01 14:27:52 -08001449 protected void onDraw(Canvas canvas) {
1450 super.onDraw(canvas);
1451
Winson36a5a2c2015-10-29 18:04:39 -07001452 // Draw the freeform workspace background
Winson805578d2015-11-23 14:47:37 -08001453 SystemServicesProxy ssp = Recents.getSystemServices();
1454 if (ssp.hasFreeformWorkspaceSupport()) {
1455 if (mFreeformWorkspaceBackground.getAlpha() > 0) {
1456 mFreeformWorkspaceBackground.draw(canvas);
1457 }
Winson36a5a2c2015-10-29 18:04:39 -07001458 }
Jorim Jaggi900fb482015-06-02 15:07:33 -07001459 }
1460
Winsona78a8f32015-12-03 10:55:01 -08001461 @Override
1462 protected boolean verifyDrawable(Drawable who) {
1463 if (who == mFreeformWorkspaceBackground) {
1464 return true;
1465 }
1466 return super.verifyDrawable(who);
1467 }
1468
Winsona5e6b362015-11-02 17:17:20 -08001469 /**
1470 * Launches the freeform tasks.
1471 */
1472 public boolean launchFreeformTasks() {
Winsonf24f2162016-01-05 12:11:55 -08001473 ArrayList<Task> tasks = mStack.getFreeformTasks();
1474 if (!tasks.isEmpty()) {
1475 Task frontTask = tasks.get(tasks.size() - 1);
1476 if (frontTask != null && frontTask.isFreeformTask()) {
1477 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
1478 frontTask, null, INVALID_STACK_ID, false));
1479 return true;
1480 }
Winsona5e6b362015-11-02 17:17:20 -08001481 }
1482 return false;
1483 }
1484
Winson Chung303e1ff2014-03-07 15:06:19 -08001485 /**** TaskStackCallbacks Implementation ****/
1486
1487 @Override
Winson Chung06266772015-12-11 10:24:21 -05001488 public void onStackTaskAdded(TaskStack stack, Task newTask) {
1489 // Update the min/max scroll and animate other task views into their new positions
Winson8aa99592016-01-19 15:07:07 -08001490 updateLayoutAlgorithm(true /* boundScroll */);
Winson Chung06266772015-12-11 10:24:21 -05001491
1492 // Animate all the tasks into place
Winson409d99a2016-05-10 15:03:46 -07001493 relayoutTaskViews(mAwaitingFirstLayout
1494 ? AnimationProps.IMMEDIATE
1495 : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winson Chung06266772015-12-11 10:24:21 -05001496 }
1497
Winson8aa99592016-01-19 15:07:07 -08001498 /**
1499 * We expect that the {@link TaskView} associated with the removed task is already hidden.
1500 */
Winson Chung06266772015-12-11 10:24:21 -05001501 @Override
Winson6c8217a2016-05-25 10:53:53 -07001502 public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
Winson Chungb5026902017-05-03 12:45:13 -07001503 AnimationProps animation, boolean fromDockGesture, boolean dismissRecentsIfAllRemoved) {
Winsonaaf33bc2015-12-03 12:02:38 -08001504 if (mFocusedTask == removedTask) {
Winsona0731a12015-12-02 15:10:14 -08001505 resetFocusedTask(removedTask);
1506 }
1507
Winson8aa99592016-01-19 15:07:07 -08001508 // Remove the view associated with this task, we can't rely on updateTransforms
1509 // to work here because the task is no longer in the list
1510 TaskView tv = getChildViewForTask(removedTask);
1511 if (tv != null) {
1512 mViewPool.returnViewToPool(tv);
Winson Chung303e1ff2014-03-07 15:06:19 -08001513 }
1514
Winson8aa99592016-01-19 15:07:07 -08001515 // Remove the task from the ignored set
1516 removeIgnoreTask(removedTask);
1517
1518 // If requested, relayout with the given animation
1519 if (animation != null) {
1520 updateLayoutAlgorithm(true /* boundScroll */);
1521 relayoutTaskViews(animation);
1522 }
Winsonf24f2162016-01-05 12:11:55 -08001523
Winson Chung931c51f2015-12-17 17:08:55 -05001524 // Update the new front most task's action button
1525 if (mScreenPinningEnabled && newFrontMostTask != null) {
Winson Chung1f24c7e2014-07-11 17:06:48 -07001526 TaskView frontTv = getChildViewForTask(newFrontMostTask);
1527 if (frontTv != null) {
Winson Chung931c51f2015-12-17 17:08:55 -05001528 frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
Winson Chung1f24c7e2014-07-11 17:06:48 -07001529 }
1530 }
1531
Winson397ae742015-11-20 11:27:33 -08001532 // If there are no remaining tasks, then just close recents
Winson4b057c62016-01-12 15:01:52 -08001533 if (mStack.getTaskCount() == 0) {
Winson Chungb5026902017-05-03 12:45:13 -07001534 if (dismissRecentsIfAllRemoved) {
1535 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
1536 ? R.string.recents_empty_message
1537 : R.string.recents_empty_message_dismissed_all));
1538 } else {
1539 EventBus.getDefault().send(new ShowEmptyViewEvent());
1540 }
Winson Chung9f49df92014-05-07 18:08:34 -07001541 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001542 }
1543
Winsona1ededd2016-03-25 12:23:12 -07001544 @Override
Winson3b6ba1a2016-03-22 15:37:54 -07001545 public void onStackTasksRemoved(TaskStack stack) {
1546 // Reset the focused task
1547 resetFocusedTask(getFocusedTask());
1548
1549 // Return all the views to the pool
1550 List<TaskView> taskViews = new ArrayList<>();
1551 taskViews.addAll(getTaskViews());
1552 for (int i = taskViews.size() - 1; i >= 0; i--) {
1553 mViewPool.returnViewToPool(taskViews.get(i));
1554 }
1555
1556 // Remove all the ignore tasks
1557 mIgnoreTasks.clear();
1558
1559 // If there are no remaining tasks, then just close recents
1560 EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
1561 R.string.recents_empty_message_dismissed_all));
1562 }
1563
1564 @Override
Winsona1ededd2016-03-25 12:23:12 -07001565 public void onStackTasksUpdated(TaskStack stack) {
Jorim Jaggicdef5912017-04-03 17:24:19 +02001566 if (mAwaitingFirstLayout) {
1567 return;
1568 }
1569
Winsona1ededd2016-03-25 12:23:12 -07001570 // Update the layout and immediately layout
1571 updateLayoutAlgorithm(false /* boundScroll */);
1572 relayoutTaskViews(AnimationProps.IMMEDIATE);
1573
1574 // Rebind all the task views. This will not trigger new resources to be loaded
1575 // unless they have actually changed
1576 List<TaskView> taskViews = getTaskViews();
1577 int taskViewCount = taskViews.size();
1578 for (int i = 0; i < taskViewCount; i++) {
1579 TaskView tv = taskViews.get(i);
1580 bindTaskView(tv, tv.getTask());
1581 }
1582 }
1583
Winson Chung303e1ff2014-03-07 15:06:19 -08001584 /**** ViewPoolConsumer Implementation ****/
1585
1586 @Override
1587 public TaskView createView(Context context) {
Jiaquan He5a2eb832016-12-13 14:36:37 -08001588 if (Recents.getConfiguration().isGridEnabled) {
1589 return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
1590 } else {
1591 return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
1592 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001593 }
1594
1595 @Override
Winson05e46ca2016-02-05 15:40:29 -08001596 public void onReturnViewToPool(TaskView tv) {
Winson Chung931c51f2015-12-17 17:08:55 -05001597 final Task task = tv.getTask();
Winson Chung303e1ff2014-03-07 15:06:19 -08001598
Winson43336942016-03-07 14:52:59 -08001599 // Unbind the task from the task view
1600 unbindTaskView(tv, task);
Winson Chung303e1ff2014-03-07 15:06:19 -08001601
Winson Chung931c51f2015-12-17 17:08:55 -05001602 // Reset the view properties and view state
Winsonf0009882016-06-01 12:22:55 -07001603 tv.clearAccessibilityFocus();
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001604 tv.resetViewProperties();
Winsonf24f2162016-01-05 12:11:55 -08001605 tv.setFocusedState(false, false /* requestViewFocus */);
Winson Chungb0a28ea2014-10-28 15:21:35 -07001606 tv.setClipViewInStack(false);
Winson Chung931c51f2015-12-17 17:08:55 -05001607 if (mScreenPinningEnabled) {
Winsonf24f2162016-01-05 12:11:55 -08001608 tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
Winson Chung931c51f2015-12-17 17:08:55 -05001609 }
Winson3c107162016-01-22 15:53:00 -08001610
1611 // Detach the view from the hierarchy
1612 detachViewFromParent(tv);
1613 // Update the task views list after removing the task view
1614 updateTaskViewsList();
Winson Chung303e1ff2014-03-07 15:06:19 -08001615 }
1616
1617 @Override
Winson05e46ca2016-02-05 15:40:29 -08001618 public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001619 // Find the index where this task should be placed in the stack
Winson250608a2015-11-24 15:00:31 -08001620 int taskIndex = mStack.indexOfStackTask(task);
Winsona78a8f32015-12-03 10:55:01 -08001621 int insertIndex = findTaskViewInsertIndex(task, taskIndex);
Winson Chung8eaeb7d2014-06-25 15:10:59 -07001622
Winson Chung303e1ff2014-03-07 15:06:19 -08001623 // Add/attach the view to the hierarchy
Winson Chung303e1ff2014-03-07 15:06:19 -08001624 if (isNewView) {
Winson70f0bf72016-02-01 14:05:29 -08001625 if (mInMeasureLayout) {
1626 // If we are measuring the layout, then just add the view normally as it will be
1627 // laid out during the layout pass
1628 addView(tv, insertIndex);
1629 } else {
1630 // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
1631 // pass, and we should layout the new child ourselves
1632 ViewGroup.LayoutParams params = tv.getLayoutParams();
1633 if (params == null) {
1634 params = generateDefaultLayoutParams();
1635 }
1636 addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
1637 measureTaskView(tv);
Winsonc69249f2016-03-28 13:38:39 -07001638 layoutTaskView(true /* changed */, tv);
Winson70f0bf72016-02-01 14:05:29 -08001639 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001640 } else {
1641 attachViewToParent(tv, insertIndex, tv.getLayoutParams());
1642 }
Winson Chung6ac8bd62015-01-07 16:38:35 -08001643 // Update the task views list after adding the new task view
1644 updateTaskViewsList();
Winson Chungb0a28ea2014-10-28 15:21:35 -07001645
Winson43336942016-03-07 14:52:59 -08001646 // Bind the task view to the new task
1647 bindTaskView(tv, task);
Winson3c107162016-01-22 15:53:00 -08001648
Winson Chungb0a28ea2014-10-28 15:21:35 -07001649 // Set the new state for this view, including the callbacks and view clipping
1650 tv.setCallbacks(this);
1651 tv.setTouchEnabled(true);
1652 tv.setClipViewInStack(true);
Winsonaaf33bc2015-12-03 12:02:38 -08001653 if (mFocusedTask == task) {
Winsonf24f2162016-01-05 12:11:55 -08001654 tv.setFocusedState(true, false /* requestViewFocus */);
Winson4b9cded2016-01-26 16:26:47 -08001655 if (mStartTimerIndicatorDuration > 0) {
Peter Schillerb124d562015-12-11 21:31:17 -08001656 // The timer indicator couldn't be started before, so start it now
Winson4b9cded2016-01-26 16:26:47 -08001657 tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
1658 mStartTimerIndicatorDuration = 0;
Peter Schillerb124d562015-12-11 21:31:17 -08001659 }
Winsona0731a12015-12-02 15:10:14 -08001660 }
Winson Chung931c51f2015-12-17 17:08:55 -05001661
1662 // Restore the action button visibility if it is the front most task view
Winson35a8b042016-01-22 09:41:09 -08001663 if (mScreenPinningEnabled && tv.getTask() ==
1664 mStack.getStackFrontMostTask(false /* includeFreeform */)) {
Winson Chung931c51f2015-12-17 17:08:55 -05001665 tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
1666 }
Winson Chung303e1ff2014-03-07 15:06:19 -08001667 }
1668
1669 @Override
1670 public boolean hasPreferredData(TaskView tv, Task preferredData) {
1671 return (tv.getTask() == preferredData);
1672 }
1673
Winson43336942016-03-07 14:52:59 -08001674 private void bindTaskView(TaskView tv, Task task) {
1675 // Rebind the task and request that this task's data be filled into the TaskView
Winsonfc48b072016-04-21 11:20:11 -07001676 tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
Winson43336942016-03-07 14:52:59 -08001677
Winson93771102016-07-11 19:49:26 -07001678 // If the doze trigger has already fired, then update the state for this task view
1679 if (mUIDozeTrigger.isAsleep() ||
Manu Cornet8ff78542016-12-20 17:17:51 -08001680 Recents.getSystemServices().hasFreeformWorkspaceSupport() ||
1681 useGridLayout()) {
Winson93771102016-07-11 19:49:26 -07001682 tv.setNoUserInteractionState();
1683 }
1684
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +01001685 if (task == mPrefetchingTask) {
1686 task.notifyTaskDataLoaded(task.thumbnail, task.icon);
1687 } else {
1688 // Load the task data
1689 Recents.getTaskLoader().loadTaskData(task);
1690 }
1691 Recents.getTaskLoader().getHighResThumbnailLoader().onTaskVisible(task);
Winson43336942016-03-07 14:52:59 -08001692 }
1693
1694 private void unbindTaskView(TaskView tv, Task task) {
Jorim Jaggi6f9dbcb2017-03-17 17:22:47 +01001695 if (task != mPrefetchingTask) {
1696 // Report that this task's data is no longer being used
1697 Recents.getTaskLoader().unloadTaskData(task);
1698 }
1699 Recents.getTaskLoader().getHighResThumbnailLoader().onTaskInvisible(task);
1700 }
1701
1702 private void updatePrefetchingTask(ArrayList<Task> tasks, int frontIndex, int backIndex) {
1703 Task t = null;
1704 boolean somethingVisible = frontIndex != -1 && backIndex != -1;
1705 if (somethingVisible && frontIndex < tasks.size() - 1) {
1706 t = tasks.get(frontIndex + 1);
1707 }
1708 if (mPrefetchingTask != t) {
1709 if (mPrefetchingTask != null) {
1710 int index = tasks.indexOf(mPrefetchingTask);
1711 if (index < backIndex || index > frontIndex) {
1712 Recents.getTaskLoader().unloadTaskData(mPrefetchingTask);
1713 }
1714 }
1715 mPrefetchingTask = t;
1716 if (t != null) {
1717 Recents.getTaskLoader().loadTaskData(t);
1718 }
1719 }
Winson43336942016-03-07 14:52:59 -08001720 }
1721
Jorim Jaggi2149be12017-04-25 15:08:29 +02001722 private void clearPrefetchingTask() {
1723 if (mPrefetchingTask != null) {
1724 Recents.getTaskLoader().unloadTaskData(mPrefetchingTask);
1725 }
1726 mPrefetchingTask = null;
1727 }
1728
Winson Chung303e1ff2014-03-07 15:06:19 -08001729 /**** TaskViewCallbacks Implementation ****/
1730
1731 @Override
Winson Chung93748a12014-07-13 17:43:31 -07001732 public void onTaskViewClipStateChanged(TaskView tv) {
Winson8aa99592016-01-19 15:07:07 -08001733 if (!mTaskViewsClipDirty) {
1734 mTaskViewsClipDirty = true;
1735 invalidate();
1736 }
Winson Chung93748a12014-07-13 17:43:31 -07001737 }
1738
Winson1c846142016-01-22 11:34:38 -08001739 /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
1740
1741 @Override
Winson66474132016-02-23 18:45:47 -08001742 public void onFocusStateChanged(int prevFocusState, int curFocusState) {
Winson1c846142016-01-22 11:34:38 -08001743 if (mDeferredTaskViewLayoutAnimation == null) {
1744 mUIDozeTrigger.poke();
Winsonbe8e6962016-02-01 14:27:52 -08001745 relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
Winson1c846142016-01-22 11:34:38 -08001746 }
1747 }
1748
Winson Chung012ef362014-07-31 18:36:25 -07001749 /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
1750
1751 @Override
Winson14991502016-02-15 15:40:08 -08001752 public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
Winson Chung012ef362014-07-31 18:36:25 -07001753 mUIDozeTrigger.poke();
Winson8aa99592016-01-19 15:07:07 -08001754 if (animation != null) {
1755 relayoutTaskViewsOnNextFrame(animation);
1756 }
Winsonc29ff002015-11-20 16:00:45 -08001757
Manu Cornet5db30f02016-12-19 17:02:59 -08001758 // In grid layout, the stack action button always remains visible.
1759 if (mEnterAnimationComplete && !useGridLayout()) {
Winson8f6ee482016-03-18 17:51:48 -07001760 if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
Winson3b6ba1a2016-03-22 15:37:54 -07001761 curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1762 mStack.getTaskCount() > 0) {
Winson8f6ee482016-03-18 17:51:48 -07001763 EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
1764 } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
1765 curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
1766 EventBus.getDefault().send(new HideStackActionButtonEvent());
Winson49df4202016-01-25 17:33:34 -08001767 }
Winsonc29ff002015-11-20 16:00:45 -08001768 }
Winson Chung012ef362014-07-31 18:36:25 -07001769 }
1770
Winsone6c90732015-09-24 16:06:29 -07001771 /**** EventBus Events ****/
Winson Chung9f49df92014-05-07 18:08:34 -07001772
Winsone6c90732015-09-24 16:06:29 -07001773 public final void onBusEvent(PackagesChangedEvent event) {
Winson Chung04400672014-10-17 14:53:30 -07001774 // Compute which components need to be removed
Winson55003902016-01-12 12:00:37 -08001775 ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
Winsone7f138c2015-10-22 16:15:21 -07001776 event.packageName, event.userId);
Winson Chung04400672014-10-17 14:53:30 -07001777
Winson Chung9f49df92014-05-07 18:08:34 -07001778 // For other tasks, just remove them directly if they no longer exist
Winson250608a2015-11-24 15:00:31 -08001779 ArrayList<Task> tasks = mStack.getStackTasks();
Winson Chung9f49df92014-05-07 18:08:34 -07001780 for (int i = tasks.size() - 1; i >= 0; i--) {
1781 final Task t = tasks.get(i);
Winsone7f138c2015-10-22 16:15:21 -07001782 if (removedComponents.contains(t.key.getComponent())) {
Winson0d14d4d2015-10-26 17:05:04 -07001783 final TaskView tv = getChildViewForTask(t);
Winson Chung9f49df92014-05-07 18:08:34 -07001784 if (tv != null) {
1785 // For visible children, defer removing the task until after the animation
Winsonf24f2162016-01-05 12:11:55 -08001786 tv.dismissTask();
Winson Chung9f49df92014-05-07 18:08:34 -07001787 } else {
1788 // Otherwise, remove the task from the stack immediately
Winson20684082016-03-16 17:13:34 -07001789 mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson Chung9f49df92014-05-07 18:08:34 -07001790 }
1791 }
1792 }
1793 }
Winson2536c7e2015-10-01 15:49:31 -07001794
Winson Chung48f2cda2015-12-11 13:20:12 -05001795 public final void onBusEvent(LaunchTaskEvent event) {
1796 // Cancel any doze triggers once a task is launched
1797 mUIDozeTrigger.stopDozing();
1798 }
1799
Manu Corneta96a6172017-01-19 12:40:44 -08001800 public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
1801 if (mStack.getTaskCount() > 0) {
1802 Task mostRecentTask = mStack.getStackFrontMostTask(true /* includeFreefromTasks */);
1803 launchTask(mostRecentTask);
1804 }
1805 }
1806
Winsonb61e6542016-02-04 14:37:18 -08001807 public final void onBusEvent(LaunchNextTaskRequestEvent event) {
Winsonbd53a312016-09-21 12:13:32 -07001808 if (mAwaitingFirstLayout) {
1809 mLaunchNextAfterFirstMeasure = true;
1810 return;
1811 }
1812
Winson Chungb5026902017-05-03 12:45:13 -07001813 if (mStack.getTaskCount() == 0) {
1814 if (RecentsImpl.getLastPipTime() != -1) {
1815 EventBus.getDefault().send(new ExpandPipEvent());
1816 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
1817 "pip");
1818 } else {
1819 // If there are no tasks, then just hide recents back to home.
1820 EventBus.getDefault().send(new HideRecentsEvent(false, true));
1821 }
1822 return;
1823 }
1824
1825 if (!Recents.getConfiguration().getLaunchState().launchedFromPipApp
1826 && mStack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime())) {
1827 // If the launch task is in the pinned stack, then expand the PiP now
1828 EventBus.getDefault().send(new ExpandPipEvent());
1829 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, "pip");
1830 } else {
1831 final Task launchTask = mStack.getNextLaunchTarget();
1832 if (launchTask != null) {
1833 // Defer launching the task until the PiP menu has been dismissed (if it is
1834 // showing at all)
1835 HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
1836 hideMenuEvent.addPostAnimationCallback(() -> {
1837 launchTask(launchTask);
1838 });
1839 EventBus.getDefault().send(hideMenuEvent);
1840 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
1841 launchTask.key.getComponent().toString());
1842 }
Winsonb61e6542016-02-04 14:37:18 -08001843 }
1844 }
1845
Winsonef064132016-01-05 12:11:31 -08001846 public final void onBusEvent(LaunchTaskStartedEvent event) {
Winsonf24f2162016-01-05 12:11:55 -08001847 mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
1848 event.getAnimationTrigger());
Winson0d14d4d2015-10-26 17:05:04 -07001849 }
Winson2536c7e2015-10-01 15:49:31 -07001850
Winsonef064132016-01-05 12:11:31 -08001851 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
1852 // Stop any scrolling
Winson61560f02016-05-02 16:36:20 -07001853 mTouchHandler.cancelNonDismissTaskAnimations();
Winsonef064132016-01-05 12:11:31 -08001854 mStackScroller.stopScroller();
1855 mStackScroller.stopBoundScrollAnimation();
Winson60df2462016-04-23 16:06:50 -07001856 cancelDeferredTaskViewLayoutAnimation();
Winson2536c7e2015-10-01 15:49:31 -07001857
Winsonef064132016-01-05 12:11:31 -08001858 // Start the task animations
Winsonf24f2162016-01-05 12:11:55 -08001859 mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08001860
1861 // Dismiss the freeform workspace background
Winson50448632016-02-01 18:04:59 -08001862 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
Winsonbe8e6962016-02-01 14:27:52 -08001863 animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
1864 Interpolators.FAST_OUT_SLOW_IN));
Jiaquan He21f495f2017-01-05 13:00:29 -08001865
1866 // Dismiss the grid task view focus frame
1867 if (mTaskViewFocusFrame != null) {
1868 mTaskViewFocusFrame.moveGridTaskViewFocus(null);
1869 }
Winson0d14d4d2015-10-26 17:05:04 -07001870 }
Winson42be4312015-10-10 11:51:08 -07001871
Winson0d14d4d2015-10-26 17:05:04 -07001872 public final void onBusEvent(DismissFocusedTaskViewEvent event) {
Winsonaaf33bc2015-12-03 12:02:38 -08001873 if (mFocusedTask != null) {
Jiaquan He21f495f2017-01-05 13:00:29 -08001874 if (mTaskViewFocusFrame != null) {
1875 mTaskViewFocusFrame.moveGridTaskViewFocus(null);
1876 }
Winsonaaf33bc2015-12-03 12:02:38 -08001877 TaskView tv = getChildViewForTask(mFocusedTask);
1878 if (tv != null) {
1879 tv.dismissTask();
1880 }
1881 resetFocusedTask(mFocusedTask);
Winson2536c7e2015-10-01 15:49:31 -07001882 }
1883 }
Winsone7f138c2015-10-22 16:15:21 -07001884
Winson3b6ba1a2016-03-22 15:37:54 -07001885 public final void onBusEvent(DismissTaskViewEvent event) {
Winsonef064132016-01-05 12:11:31 -08001886 // For visible children, defer removing the task until after the animation
Manu Cornet5df76672017-01-11 15:14:14 -08001887 mAnimationHelper.startDeleteTaskAnimation(
1888 event.taskView, useGridLayout(), event.getAnimationTrigger());
Winson3b6ba1a2016-03-22 15:37:54 -07001889 }
1890
1891 public final void onBusEvent(final DismissAllTaskViewsEvent event) {
1892 // Keep track of the tasks which will have their data removed
1893 ArrayList<Task> tasks = new ArrayList<>(mStack.getStackTasks());
Manu Cornet5df76672017-01-11 15:14:14 -08001894 mAnimationHelper.startDeleteAllTasksAnimation(
1895 getTaskViews(), useGridLayout(), event.getAnimationTrigger());
Winson3b6ba1a2016-03-22 15:37:54 -07001896 event.addPostAnimationCallback(new Runnable() {
1897 @Override
1898 public void run() {
1899 // Announce for accessibility
1900 announceForAccessibility(getContext().getString(
1901 R.string.accessibility_recents_all_items_dismissed));
1902
1903 // Remove all tasks and delete the task data for all tasks
Winson Chungb5026902017-05-03 12:45:13 -07001904 mStack.removeAllTasks(true /* notifyStackChanges */);
Winson3b6ba1a2016-03-22 15:37:54 -07001905 for (int i = tasks.size() - 1; i >= 0; i--) {
1906 EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
1907 }
1908
1909 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
1910 }
1911 });
1912
Winsonef064132016-01-05 12:11:31 -08001913 }
1914
1915 public final void onBusEvent(TaskViewDismissedEvent event) {
Winson3b6ba1a2016-03-22 15:37:54 -07001916 // Announce for accessibility
1917 announceForAccessibility(getContext().getString(
1918 R.string.accessibility_recents_item_dismissed, event.task.title));
1919
Manu Cornet551e80c2017-02-10 12:54:40 -08001920 if (useGridLayout() && event.animation != null) {
1921 event.animation.setListener(new AnimatorListenerAdapter() {
1922 public void onAnimationEnd(Animator animator) {
1923 if (mTaskViewFocusFrame != null) {
1924 // Resize the grid layout task view focus frame
1925 mTaskViewFocusFrame.resize();
1926 }
1927 }
1928 });
1929 }
1930
Winson3b6ba1a2016-03-22 15:37:54 -07001931 // Remove the task from the stack
Winson61560f02016-05-02 16:36:20 -07001932 mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
Winsonef064132016-01-05 12:11:31 -08001933 EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
Winson42329522016-02-05 10:39:46 -08001934
1935 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
1936 event.task.key.getComponent().toString());
Winsonef064132016-01-05 12:11:31 -08001937 }
1938
1939 public final void onBusEvent(FocusNextTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001940 // Stop any scrolling
1941 mStackScroller.stopScroller();
1942 mStackScroller.stopBoundScrollAnimation();
1943
Peter Schillerb124d562015-12-11 21:31:17 -08001944 setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
Winson4b9cded2016-01-26 16:26:47 -08001945 event.timerIndicatorDuration);
Winsonef064132016-01-05 12:11:31 -08001946 }
1947
1948 public final void onBusEvent(FocusPreviousTaskViewEvent event) {
Winson66474132016-02-23 18:45:47 -08001949 // Stop any scrolling
1950 mStackScroller.stopScroller();
1951 mStackScroller.stopBoundScrollAnimation();
1952
Winsonef064132016-01-05 12:11:31 -08001953 setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
1954 }
1955
Jiaquan Hec0e18132017-01-11 14:47:00 -08001956 public final void onBusEvent(NavigateTaskViewEvent event) {
1957 if (useGridLayout()) {
1958 final int taskCount = mStack.getTaskCount();
1959 final int currentIndex = mStack.indexOfStackTask(getFocusedTask());
1960 final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
1961 currentIndex, event.direction);
1962 setFocusedTask(nextIndex, false, true);
1963 } else {
1964 switch (event.direction) {
1965 case UP:
1966 EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
1967 break;
1968 case DOWN:
1969 EventBus.getDefault().send(
1970 new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
1971 break;
1972 }
1973 }
1974 }
1975
Winsone7f138c2015-10-22 16:15:21 -07001976 public final void onBusEvent(UserInteractionEvent event) {
1977 // Poke the doze trigger on user interaction
1978 mUIDozeTrigger.poke();
Winson4b9cded2016-01-26 16:26:47 -08001979
1980 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
1981 if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
Winsonc5ef63f2016-01-21 14:39:23 -08001982 TaskView tv = getChildViewForTask(mFocusedTask);
1983 if (tv != null) {
1984 tv.getHeaderView().cancelFocusTimerIndicator();
1985 }
Peter Schillerb124d562015-12-11 21:31:17 -08001986 }
Winsone7f138c2015-10-22 16:15:21 -07001987 }
1988
Winsoneca4ab62015-11-04 10:50:28 -08001989 public final void onBusEvent(DragStartEvent event) {
Winson70f0bf72016-02-01 14:05:29 -08001990 // Ensure that the drag task is not animated
1991 addIgnoreTask(event.task);
1992
Winsoneca4ab62015-11-04 10:50:28 -08001993 if (event.task.isFreeformTask()) {
1994 // Animate to the front of the stack
Winsond9529612016-01-28 13:29:49 -08001995 mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
Winsoneca4ab62015-11-04 10:50:28 -08001996 }
Winsonf24f2162016-01-05 12:11:55 -08001997
1998 // Enlarge the dragged view slightly
1999 float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
2000 mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
Jiaquan He26f637b2016-12-27 14:44:14 -08002001 mTmpTransform, null);
Winsonf24f2162016-01-05 12:11:55 -08002002 mTmpTransform.scale = finalScale;
Winson3e874742016-01-07 10:08:17 -08002003 mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
Winson0a461f02016-04-19 10:51:12 -07002004 mTmpTransform.dimAlpha = 0f;
Winsonf24f2162016-01-05 12:11:55 -08002005 updateTaskViewToTransform(event.taskView, mTmpTransform,
Winsonbe8e6962016-02-01 14:27:52 -08002006 new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
Winsoneca4ab62015-11-04 10:50:28 -08002007 }
2008
2009 public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
2010 SystemServicesProxy ssp = Recents.getSystemServices();
2011 if (ssp.hasFreeformWorkspaceSupport()) {
2012 event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
2013 event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
2014 }
2015 }
2016
2017 public final void onBusEvent(DragDropTargetChangedEvent event) {
Winson3f32e7e2016-04-20 17:18:08 -07002018 AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
2019 Interpolators.FAST_OUT_SLOW_IN);
Winson59924fe2016-03-17 14:13:18 -07002020 boolean ignoreTaskOverrides = false;
Winson3e874742016-01-07 10:08:17 -08002021 if (event.dropTarget instanceof TaskStack.DockState) {
2022 // Calculate the new task stack bounds that matches the window size that Recents will
2023 // have after the drop
2024 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
Winson670ea712016-04-12 17:02:26 -07002025 Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
2026 // When docked, the nav bar insets are consumed and the activity is measured without
2027 // insets. However, the window bounds include the insets, so we need to subtract them
2028 // here to make them identical.
2029 int height = getMeasuredHeight();
2030 height -= systemInsets.bottom;
2031 systemInsets.bottom = 0;
Winsonfc48b072016-04-21 11:20:11 -07002032 mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
Winson67c79572016-04-13 14:02:18 -07002033 height, mDividerSize, systemInsets,
Winson59924fe2016-03-17 14:13:18 -07002034 mLayoutAlgorithm, getResources(), mWindowRect));
Winson670ea712016-04-12 17:02:26 -07002035 mLayoutAlgorithm.setSystemInsets(systemInsets);
Winsonfc48b072016-04-21 11:20:11 -07002036 mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
Winson8aa99592016-01-19 15:07:07 -08002037 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
2038 updateLayoutAlgorithm(true /* boundScroll */);
Winson59924fe2016-03-17 14:13:18 -07002039 ignoreTaskOverrides = true;
Winson3e874742016-01-07 10:08:17 -08002040 } else {
Winson8aa99592016-01-19 15:07:07 -08002041 // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
2042 // task view, so add it back to the ignore set after updating the layout
Winson8aa99592016-01-19 15:07:07 -08002043 removeIgnoreTask(event.task);
Winson27c28f82016-05-05 16:16:50 -07002044 updateLayoutToStableBounds();
Winson8aa99592016-01-19 15:07:07 -08002045 addIgnoreTask(event.task);
Winson3e874742016-01-07 10:08:17 -08002046 }
Winson27c28f82016-05-05 16:16:50 -07002047 relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
Winsoneca4ab62015-11-04 10:50:28 -08002048 }
2049
2050 public final void onBusEvent(final DragEndEvent event) {
Winson479f7442015-11-25 15:16:27 -08002051 // We don't handle drops on the dock regions
2052 if (event.dropTarget instanceof TaskStack.DockState) {
Winson59924fe2016-03-17 14:13:18 -07002053 // However, we do need to reset the overrides, since the last state of this task stack
2054 // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
2055 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
Winsoneca4ab62015-11-04 10:50:28 -08002056 return;
2057 }
2058
Winson479f7442015-11-25 15:16:27 -08002059 boolean isFreeformTask = event.task.isFreeformTask();
2060 boolean hasChangedStacks =
2061 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
2062 (isFreeformTask && event.dropTarget == mStackDropTarget);
Winson479f7442015-11-25 15:16:27 -08002063
Winson5b7dd532015-12-01 16:02:12 -08002064 if (hasChangedStacks) {
Winson479f7442015-11-25 15:16:27 -08002065 // Move the task to the right position in the stack (ie. the front of the stack if
Winson8aa99592016-01-19 15:07:07 -08002066 // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
Winson479f7442015-11-25 15:16:27 -08002067 // before we update their stack ids, otherwise, the keys will have changed.
2068 if (event.dropTarget == mFreeformWorkspaceDropTarget) {
2069 mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08002070 } else if (event.dropTarget == mStackDropTarget) {
2071 mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
Winson479f7442015-11-25 15:16:27 -08002072 }
Winson8aa99592016-01-19 15:07:07 -08002073 updateLayoutAlgorithm(true /* boundScroll */);
Winson479f7442015-11-25 15:16:27 -08002074
2075 // Move the task to the new stack in the system after the animation completes
Winson Chungaaeaac12015-12-16 16:49:36 -05002076 event.addPostAnimationCallback(new Runnable() {
Winson479f7442015-11-25 15:16:27 -08002077 @Override
2078 public void run() {
2079 SystemServicesProxy ssp = Recents.getSystemServices();
2080 ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
2081 }
2082 });
Winsoneca4ab62015-11-04 10:50:28 -08002083 }
Winsoneca4ab62015-11-04 10:50:28 -08002084
Winson27c28f82016-05-05 16:16:50 -07002085 // Restore the task, so that relayout will apply to it below
Winson8aa99592016-01-19 15:07:07 -08002086 removeIgnoreTask(event.task);
Winson27c28f82016-05-05 16:16:50 -07002087
2088 // Convert the dragging task view back to its final layout-space rect
2089 Utilities.setViewFrameFromTranslation(event.taskView);
2090
2091 // Animate all the tasks into place
2092 ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
2093 animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
2094 Interpolators.FAST_OUT_SLOW_IN,
2095 event.getAnimationTrigger().decrementOnAnimationEnd()));
2096 relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
2097 Interpolators.FAST_OUT_SLOW_IN));
2098 event.getAnimationTrigger().increment();
2099 }
2100
2101 public final void onBusEvent(final DragEndCancelledEvent event) {
2102 // Restore the pre-drag task stack bounds, including the dragging task view
2103 removeIgnoreTask(event.task);
2104 updateLayoutToStableBounds();
2105
2106 // Convert the dragging task view back to its final layout-space rect
2107 Utilities.setViewFrameFromTranslation(event.taskView);
2108
2109 // Animate all the tasks into place
2110 ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
2111 animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
2112 Interpolators.FAST_OUT_SLOW_IN,
2113 event.getAnimationTrigger().decrementOnAnimationEnd()));
2114 relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
2115 Interpolators.FAST_OUT_SLOW_IN));
2116 event.getAnimationTrigger().increment();
Winsoneca4ab62015-11-04 10:50:28 -08002117 }
2118
Winson8b1871d2015-11-20 09:56:20 -08002119 public final void onBusEvent(IterateRecentsEvent event) {
Winson Chungd6b78a32015-12-15 10:22:45 -05002120 if (!mEnterAnimationComplete) {
2121 // Cancel the previous task's window transition before animating the focused state
2122 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
2123 }
Winson8b1871d2015-11-20 09:56:20 -08002124 }
2125
Winsone5f1faa2015-11-20 12:26:23 -08002126 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
2127 mEnterAnimationComplete = true;
Winsonef064132016-01-05 12:11:31 -08002128
Winson4b057c62016-01-12 15:01:52 -08002129 if (mStack.getTaskCount() > 0) {
Winsonef064132016-01-05 12:11:31 -08002130 // Start the task enter animations
Winsonf24f2162016-01-05 12:11:55 -08002131 mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
Winsonef064132016-01-05 12:11:31 -08002132
2133 // Add a runnable to the post animation ref counter to clear all the views
2134 event.addPostAnimationCallback(new Runnable() {
2135 @Override
2136 public void run() {
2137 // Start the dozer to trigger to trigger any UI that shows after a timeout
Winson2ed7c472016-06-14 17:35:53 -07002138 if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
2139 mUIDozeTrigger.startDozing();
2140 }
Winsonef064132016-01-05 12:11:31 -08002141
2142 // Update the focused state here -- since we only set the focused task without
2143 // requesting view focus in onFirstLayout(), actually request view focus and
2144 // animate the focused state if we are alt-tabbing now, after the window enter
2145 // animation is completed
2146 if (mFocusedTask != null) {
2147 RecentsConfiguration config = Recents.getConfiguration();
2148 RecentsActivityLaunchState launchState = config.getLaunchState();
2149 setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
2150 false /* scrollToTask */, launchState.launchedWithAltTab);
Winsonf0009882016-06-01 12:22:55 -07002151 TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
2152 if (mTouchExplorationEnabled && focusedTaskView != null) {
2153 focusedTaskView.requestAccessibilityFocus();
2154 }
Winsonef064132016-01-05 12:11:31 -08002155 }
Winson4b9cded2016-01-26 16:26:47 -08002156
2157 EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent());
Winsonef064132016-01-05 12:11:31 -08002158 }
2159 });
2160 }
Winsone5f1faa2015-11-20 12:26:23 -08002161 }
2162
Winsonb1e71d02015-11-23 12:40:23 -08002163 public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
2164 List<TaskView> taskViews = getTaskViews();
2165 int taskViewCount = taskViews.size();
2166 for (int i = 0; i < taskViewCount; i++) {
2167 TaskView tv = taskViews.get(i);
2168 Task task = tv.getTask();
Winson Chung296278a2015-12-17 12:09:02 -05002169 if (task.isFreeformTask()) {
Winsonb1e71d02015-11-23 12:40:23 -08002170 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
2171 }
2172 }
2173 }
2174
Winsond2a03062016-04-15 11:19:07 -07002175 public final void onBusEvent(final MultiWindowStateChangedEvent event) {
Winson95ee8732016-04-26 14:46:58 -07002176 if (event.inMultiWindow || !event.showDeferredAnimation) {
Winsond2a03062016-04-15 11:19:07 -07002177 setTasks(event.stack, true /* allowNotifyStackChanges */);
2178 } else {
2179 // Reset the launch state before handling the multiwindow change
2180 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
2181 launchState.reset();
2182
Winson67c79572016-04-13 14:02:18 -07002183 // Defer until the next frame to ensure that we have received all the system insets, and
2184 // initial layout updates
Winsond2a03062016-04-15 11:19:07 -07002185 event.getAnimationTrigger().increment();
Winson67c79572016-04-13 14:02:18 -07002186 post(new Runnable() {
Winson931845f2016-02-24 19:38:41 -08002187 @Override
2188 public void run() {
Winson67c79572016-04-13 14:02:18 -07002189 // Scroll the stack to the front to see the undocked task
Winsond2a03062016-04-15 11:19:07 -07002190 mAnimationHelper.startNewStackScrollAnimation(event.stack,
2191 event.getAnimationTrigger());
2192 event.getAnimationTrigger().decrement();
Winson931845f2016-02-24 19:38:41 -08002193 }
2194 });
2195 }
Winsond9529612016-01-28 13:29:49 -08002196 }
2197
Winsone693aaf2016-03-01 12:05:59 -08002198 public final void onBusEvent(ConfigurationChangedEvent event) {
Winsonfc48b072016-04-21 11:20:11 -07002199 if (event.fromDeviceOrientationChange) {
2200 mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
2201 mDisplayRect = Recents.getSystemServices().getDisplayRect();
Winson399d21e2016-05-18 13:26:29 -07002202
2203 // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
2204 // wrong bounds in the new layout
2205 mStackScroller.stopScroller();
Winsonfc48b072016-04-21 11:20:11 -07002206 }
Winson670ea712016-04-12 17:02:26 -07002207 reloadOnConfigurationChange();
Winson619e40c2016-03-25 16:12:35 -07002208
2209 // Notify the task views of the configuration change so they can reload their resources
2210 if (!event.fromMultiWindow) {
2211 mTmpTaskViews.clear();
2212 mTmpTaskViews.addAll(getTaskViews());
2213 mTmpTaskViews.addAll(mViewPool.getViews());
2214 int taskViewCount = mTmpTaskViews.size();
2215 for (int i = 0; i < taskViewCount; i++) {
2216 mTmpTaskViews.get(i).onConfigurationChanged();
2217 }
2218 }
2219
Manu Cornet5db30f02016-12-19 17:02:59 -08002220 // Update the Clear All button in case we're switching in or out of grid layout.
2221 updateStackActionButtonVisibility();
2222
Winson44849b82016-03-29 10:45:45 -07002223 // Trigger a new layout and update to the initial state if necessary
Winson67c79572016-04-13 14:02:18 -07002224 if (event.fromMultiWindow) {
Winson44849b82016-03-29 10:45:45 -07002225 mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
Winson670ea712016-04-12 17:02:26 -07002226 requestLayout();
Winson67c79572016-04-13 14:02:18 -07002227 } else if (event.fromDeviceOrientationChange) {
2228 mInitialState = INITIAL_STATE_UPDATE_ALL;
2229 requestLayout();
Winson44849b82016-03-29 10:45:45 -07002230 }
Winson670ea712016-04-12 17:02:26 -07002231 }
2232
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07002233 public final void onBusEvent(RecentsGrowingEvent event) {
2234 mResetToInitialStateWhenResized = true;
2235 }
2236
Jiaquan He21f495f2017-01-05 13:00:29 -08002237 public final void onBusEvent(RecentsVisibilityChangedEvent event) {
2238 if (!event.visible && mTaskViewFocusFrame != null) {
2239 mTaskViewFocusFrame.moveGridTaskViewFocus(null);
2240 }
Jorim Jaggi2149be12017-04-25 15:08:29 +02002241 if (!event.visible) {
2242 List<TaskView> taskViews = new ArrayList<>(getTaskViews());
2243 for (int i = 0; i < taskViews.size(); i++) {
2244 mViewPool.returnViewToPool(taskViews.get(i));
2245 }
2246 clearPrefetchingTask();
2247 }
Jiaquan He21f495f2017-01-05 13:00:29 -08002248 }
2249
Winson Chungb5026902017-05-03 12:45:13 -07002250 public final void onBusEvent(ActivityPinnedEvent event) {
2251 // If an activity enters PiP while Recents is open, remove the stack task associated with
2252 // the new PiP task
2253 Task removeTask = mStack.findTaskWithId(event.taskId);
2254 if (removeTask != null) {
2255 // In this case, we remove the task, but if the last task is removed, don't dismiss
2256 // Recents to home
2257 mStack.removeTask(removeTask, AnimationProps.IMMEDIATE, false /* fromDockGesture */,
2258 false /* dismissRecentsIfAllRemoved */);
2259 }
2260 updateLayoutAlgorithm(false /* boundScroll */);
2261 updateToInitialState();
2262 }
2263
Winson670ea712016-04-12 17:02:26 -07002264 public void reloadOnConfigurationChange() {
2265 mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
2266 mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
Winsone693aaf2016-03-01 12:05:59 -08002267 }
2268
Winson0d14d4d2015-10-26 17:05:04 -07002269 /**
Winsona78a8f32015-12-03 10:55:01 -08002270 * Starts an alpha animation on the freeform workspace background.
2271 */
Winsonbe8e6962016-02-01 14:27:52 -08002272 private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
2273 AnimationProps animation) {
Winsona78a8f32015-12-03 10:55:01 -08002274 if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
2275 return;
2276 }
2277
2278 Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
2279 mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
Winson3e874742016-01-07 10:08:17 -08002280 Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
Winsonbe8e6962016-02-01 14:27:52 -08002281 mFreeformWorkspaceBackgroundAnimator.setStartDelay(
2282 animation.getDuration(AnimationProps.ALPHA));
2283 mFreeformWorkspaceBackgroundAnimator.setDuration(
2284 animation.getDuration(AnimationProps.ALPHA));
2285 mFreeformWorkspaceBackgroundAnimator.setInterpolator(
2286 animation.getInterpolator(AnimationProps.ALPHA));
Winsona78a8f32015-12-03 10:55:01 -08002287 mFreeformWorkspaceBackgroundAnimator.start();
2288 }
2289
2290 /**
Winson8aa99592016-01-19 15:07:07 -08002291 * Returns the insert index for the task in the current set of task views. If the given task
Winsona78a8f32015-12-03 10:55:01 -08002292 * is already in the task view list, then this method returns the insert index assuming it
2293 * is first removed at the previous index.
2294 *
2295 * @param task the task we are finding the index for
2296 * @param taskIndex the index of the task in the stack
2297 */
2298 private int findTaskViewInsertIndex(Task task, int taskIndex) {
2299 if (taskIndex != -1) {
2300 List<TaskView> taskViews = getTaskViews();
2301 boolean foundTaskView = false;
2302 int taskViewCount = taskViews.size();
2303 for (int i = 0; i < taskViewCount; i++) {
2304 Task tvTask = taskViews.get(i).getTask();
2305 if (tvTask == task) {
2306 foundTaskView = true;
2307 } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
2308 if (foundTaskView) {
2309 return i - 1;
2310 } else {
2311 return i;
2312 }
2313 }
2314 }
2315 }
2316 return -1;
2317 }
Winson Chungde750de2015-12-11 10:26:06 -05002318
Manu Corneta96a6172017-01-19 12:40:44 -08002319 private void launchTask(Task task) {
2320 // Stop all animations
2321 cancelAllTaskViewAnimations();
2322
2323 float curScroll = mStackScroller.getStackScroll();
2324 float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(task);
2325 float absScrollDiff = Math.abs(targetScroll - curScroll);
2326 if (getChildViewForTask(task) == null || absScrollDiff > 0.35f) {
2327 int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
2328 absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
2329 mStackScroller.animateScroll(targetScroll,
2330 duration, new Runnable() {
2331 @Override
2332 public void run() {
2333 EventBus.getDefault().send(new LaunchTaskEvent(
2334 getChildViewForTask(task), task, null,
2335 INVALID_STACK_ID, false /* screenPinningRequested */));
2336 }
2337 });
2338 } else {
2339 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task),
2340 task, null, INVALID_STACK_ID, false /* screenPinningRequested */));
2341 }
2342 }
2343
Winson Chungde750de2015-12-11 10:26:06 -05002344 /**
Jiaquan Hec0a1acd2016-12-13 13:46:46 -08002345 * Check whether we should use the grid layout.
Jiaquan Hec0a1acd2016-12-13 13:46:46 -08002346 */
2347 public boolean useGridLayout() {
Manu Cornetc0432c62017-01-13 09:21:26 -08002348 return mLayoutAlgorithm.useGridLayout();
Jiaquan Hec0a1acd2016-12-13 13:46:46 -08002349 }
2350
2351 /**
Winson6ea25882016-01-13 10:59:04 -08002352 * Reads current system flags related to accessibility and screen pinning.
2353 */
2354 private void readSystemFlags() {
2355 SystemServicesProxy ssp = Recents.getSystemServices();
2356 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
2357 mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
2358 Settings.System.LOCK_TO_APP_ENABLED) != 0;
2359 }
Winsond72c3152016-04-05 15:33:35 -07002360
Manu Cornet5db30f02016-12-19 17:02:59 -08002361 private void updateStackActionButtonVisibility() {
2362 // Always show the button in grid layout.
2363 if (useGridLayout() ||
2364 (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
2365 mStack.getTaskCount() > 0)) {
2366 EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
2367 } else {
2368 EventBus.getDefault().send(new HideStackActionButtonEvent());
2369 }
2370 }
2371
Winsond72c3152016-04-05 15:33:35 -07002372 public void dump(String prefix, PrintWriter writer) {
2373 String innerPrefix = prefix + " ";
2374 String id = Integer.toHexString(System.identityHashCode(this));
2375
2376 writer.print(prefix); writer.print(TAG);
2377 writer.print(" hasDefRelayout=");
2378 writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
2379 writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
2380 writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
2381 writer.print(" initialState="); writer.print(mInitialState);
2382 writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
2383 writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
2384 writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
2385 writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
2386 writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
2387 writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
2388 writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
2389 writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
2390 writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
2391 writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
Winsonfc48b072016-04-21 11:20:11 -07002392 writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
2393 writer.print(" orientation="); writer.print(mDisplayOrientation);
Winsond72c3152016-04-05 15:33:35 -07002394 writer.print(" [0x"); writer.print(id); writer.print("]");
2395 writer.println();
2396
2397 if (mFocusedTask != null) {
2398 writer.print(innerPrefix);
2399 writer.print("Focused task: ");
Winson67c79572016-04-13 14:02:18 -07002400 mFocusedTask.dump("", writer);
Winsond72c3152016-04-05 15:33:35 -07002401 }
2402
Winson Chungeb2206c2017-01-03 12:37:08 -08002403 int numTaskViews = mTaskViews.size();
2404 for (int i = 0; i < numTaskViews; i++) {
2405 mTaskViews.get(i).dump(innerPrefix, writer);
2406 }
2407
Winsond72c3152016-04-05 15:33:35 -07002408 mLayoutAlgorithm.dump(innerPrefix, writer);
2409 mStackScroller.dump(innerPrefix, writer);
2410 }
Winson Chunga4cc9662014-07-25 12:10:38 -07002411}