blob: 13779757afc5c78de05561f883521d3f23c50590 [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
19import android.app.ActivityOptions;
Winson Chung9f9679d2014-04-11 16:49:09 -070020import android.app.TaskStackBuilder;
Winson Chung303e1ff2014-03-07 15:06:19 -080021import android.content.Context;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
25import android.graphics.Rect;
Winson Chung9f9679d2014-04-11 16:49:09 -070026import android.net.Uri;
Kenny Guy94a07ac2014-08-07 15:34:38 +010027import android.os.UserHandle;
Winson Chung9f9679d2014-04-11 16:49:09 -070028import android.provider.Settings;
Winson Chung8e548f72014-06-24 14:40:53 -070029import android.util.AttributeSet;
Winson Chungecd9b302014-04-16 17:07:18 -070030import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080031import android.view.View;
Winson Chungd16c5652015-01-26 16:11:07 -080032import android.view.ViewStub;
Winson Chung653f70c22014-05-19 14:49:42 -070033import android.view.WindowInsets;
Winson Chung303e1ff2014-03-07 15:06:19 -080034import android.widget.FrameLayout;
Winson Chungd16c5652015-01-26 16:11:07 -080035import com.android.systemui.R;
Winson Chung303e1ff2014-03-07 15:06:19 -080036import com.android.systemui.recents.Constants;
Winson Chungaee097c2015-04-02 18:16:02 -070037import com.android.systemui.recents.RecentsAppWidgetHostView;
Winson Chung303e1ff2014-03-07 15:06:19 -080038import com.android.systemui.recents.RecentsConfiguration;
Winson Chung1f24c7e2014-07-11 17:06:48 -070039import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungf1fbd772014-06-24 18:06:58 -070040import com.android.systemui.recents.model.RecentsPackageMonitor;
41import com.android.systemui.recents.model.RecentsTaskLoader;
Winson Chung303e1ff2014-03-07 15:06:19 -080042import com.android.systemui.recents.model.Task;
43import com.android.systemui.recents.model.TaskStack;
44
45import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -080046import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -080047
Winson Chung303e1ff2014-03-07 15:06:19 -080048/**
49 * This view is the the top level layout that contains TaskStacks (which are laid out according
50 * to their SpaceNode bounds.
51 */
Winson Chung9f49df92014-05-07 18:08:34 -070052public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
53 RecentsPackageMonitor.PackageCallbacks {
Winson Chung47c4c692014-03-17 10:17:11 -070054
55 /** The RecentsView callbacks */
56 public interface RecentsViewCallbacks {
Winson Chung7aceb9a2014-07-03 13:38:01 -070057 public void onTaskViewClicked();
Winson Chung4e96eb72014-09-17 15:16:09 +020058 public void onTaskLaunchFailed();
Winson Chung7aceb9a2014-07-03 13:38:01 -070059 public void onAllTaskViewsDismissed();
Winson Chungcdbbb7e2014-06-24 12:11:49 -070060 public void onExitToHomeAnimationTriggered();
Jason Monk18f99d92014-09-11 13:36:42 -040061 public void onScreenPinningRequest();
Winson Chungd16c5652015-01-26 16:11:07 -080062
Skuhneece738b2015-03-17 15:02:18 -070063 public void onTaskResize(Task t);
Winson Chung47c4c692014-03-17 10:17:11 -070064 }
65
Winson Chungd42a6cf2014-06-03 16:24:04 -070066 RecentsConfiguration mConfig;
67 LayoutInflater mInflater;
Winson Chungdcfa7972014-07-22 12:27:13 -070068 DebugOverlayView mDebugOverlay;
Winson Chungd16c5652015-01-26 16:11:07 -080069 RecentsViewLayoutAlgorithm mLayoutAlgorithm;
Winson Chungd42a6cf2014-06-03 16:24:04 -070070
Winson Chungdcfa7972014-07-22 12:27:13 -070071 ArrayList<TaskStack> mStacks;
Winson Chung93847da2015-02-03 10:26:22 -080072 List<TaskStackView> mTaskStackViews = new ArrayList<>();
Winson Chungaee097c2015-04-02 18:16:02 -070073 RecentsAppWidgetHostView mSearchBar;
Winson Chung47c4c692014-03-17 10:17:11 -070074 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080075
76 public RecentsView(Context context) {
77 super(context);
Winson Chung8e548f72014-06-24 14:40:53 -070078 }
79
80 public RecentsView(Context context, AttributeSet attrs) {
81 this(context, attrs, 0);
82 }
83
84 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
85 this(context, attrs, defStyleAttr, 0);
86 }
87
88 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
89 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -070090 mConfig = RecentsConfiguration.getInstance();
Winson Chungecd9b302014-04-16 17:07:18 -070091 mInflater = LayoutInflater.from(context);
Winson Chungd16c5652015-01-26 16:11:07 -080092 mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
93 }
94
Winson Chung47c4c692014-03-17 10:17:11 -070095 /** Sets the callbacks */
96 public void setCallbacks(RecentsViewCallbacks cb) {
97 mCb = cb;
98 }
99
Winson Chungdcfa7972014-07-22 12:27:13 -0700100 /** Sets the debug overlay */
101 public void setDebugOverlay(DebugOverlayView overlay) {
102 mDebugOverlay = overlay;
103 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800104
Winson Chungdcfa7972014-07-22 12:27:13 -0700105 /** Set/get the bsp root node */
106 public void setTaskStacks(ArrayList<TaskStack> stacks) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700107 int numStacks = stacks.size();
108
Winson Chungb0a28ea2014-10-28 15:21:35 -0700109 // Remove all/extra stack views
110 int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
111 if (mConfig.launchedReuseTaskStackViews) {
Winson Chung93847da2015-02-03 10:26:22 -0800112 numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700113 }
Winson Chung93847da2015-02-03 10:26:22 -0800114 for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
115 removeView(mTaskStackViews.remove(i));
Winson Chungb0a28ea2014-10-28 15:21:35 -0700116 }
117
118 // Update the stack views that we are keeping
119 for (int i = 0; i < numTaskStacksToKeep; i++) {
Winson Chung93847da2015-02-03 10:26:22 -0800120 TaskStackView tsv = mTaskStackViews.get(i);
Winson Chungbc571a92014-11-19 17:09:03 -0800121 // If onRecentsHidden is not triggered, we need to the stack view again here
122 tsv.reset();
123 tsv.setStack(stacks.get(i));
Winson Chungb0a28ea2014-10-28 15:21:35 -0700124 }
125
126 // Add remaining/recreate stack views
Winson Chungdcfa7972014-07-22 12:27:13 -0700127 mStacks = stacks;
Winson Chung93847da2015-02-03 10:26:22 -0800128 for (int i = mTaskStackViews.size(); i < numStacks; i++) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700129 TaskStack stack = stacks.get(i);
Winson Chung303e1ff2014-03-07 15:06:19 -0800130 TaskStackView stackView = new TaskStackView(getContext(), stack);
131 stackView.setCallbacks(this);
132 addView(stackView);
Winson Chung93847da2015-02-03 10:26:22 -0800133 mTaskStackViews.add(stackView);
Winson Chung303e1ff2014-03-07 15:06:19 -0800134 }
Winson Chung02d49272014-08-29 13:57:29 -0700135
Winson Chungb0a28ea2014-10-28 15:21:35 -0700136 // Enable debug mode drawing on all the stacks if necessary
137 if (mConfig.debugModeEnabled) {
Winson Chung93847da2015-02-03 10:26:22 -0800138 for (int i = mTaskStackViews.size() - 1; i >= 0; i--) {
139 TaskStackView stackView = mTaskStackViews.get(i);
Winson Chungd16c5652015-01-26 16:11:07 -0800140 stackView.setDebugOverlay(mDebugOverlay);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700141 }
142 }
143
Winson Chungb0a28ea2014-10-28 15:21:35 -0700144 // Trigger a new layout
145 requestLayout();
Winson Chung19fc1172014-07-31 18:40:03 -0700146 }
147
Winson Chungd16c5652015-01-26 16:11:07 -0800148 /** Gets the list of task views */
149 List<TaskStackView> getTaskStackViews() {
Winson Chung93847da2015-02-03 10:26:22 -0800150 return mTaskStackViews;
Winson Chungd16c5652015-01-26 16:11:07 -0800151 }
152
Skuhne8aa7d162015-03-20 13:40:53 -0700153 /** Gets the next task in the stack - or if the last - the top task */
154 public Task getNextTaskOrTopTask(Task taskToSearch) {
155 Task returnTask = null;
156 boolean found = false;
157 List<TaskStackView> stackViews = getTaskStackViews();
158 int stackCount = stackViews.size();
159 for (int i = stackCount - 1; i >= 0; --i) {
160 TaskStack stack = stackViews.get(i).getStack();
161 ArrayList<Task> taskList = stack.getTasks();
162 // Iterate the stack views and try and find the focused task
163 for (int j = taskList.size() - 1; j >= 0; --j) {
164 Task task = taskList.get(j);
165 // Return the next task in the line.
166 if (found)
167 return task;
168 // Remember the first possible task as the top task.
169 if (returnTask == null)
170 returnTask = task;
171 if (task == taskToSearch)
172 found = true;
173 }
174 }
175 return returnTask;
176 }
177
Winson Chung1e8d71b2014-05-16 17:05:22 -0700178 /** Launches the focused task from the first stack if possible */
179 public boolean launchFocusedTask() {
180 // Get the first stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800181 List<TaskStackView> stackViews = getTaskStackViews();
182 int stackCount = stackViews.size();
183 for (int i = 0; i < stackCount; i++) {
184 TaskStackView stackView = stackViews.get(i);
185 TaskStack stack = stackView.getStack();
186 // Iterate the stack views and try and find the focused task
187 List<TaskView> taskViews = stackView.getTaskViews();
188 int taskViewCount = taskViews.size();
189 for (int j = 0; j < taskViewCount; j++) {
190 TaskView tv = taskViews.get(j);
191 Task task = tv.getTask();
192 if (tv.isFocusedTask()) {
193 onTaskViewClicked(stackView, tv, stack, task, false);
194 return true;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700195 }
196 }
197 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700198 return false;
199 }
200
Skuhne8aa7d162015-03-20 13:40:53 -0700201 /** Launches a given task. */
202 public boolean launchTask(Task task) {
203 // Get the first stack view
204 List<TaskStackView> stackViews = getTaskStackViews();
205 int stackCount = stackViews.size();
206 for (int i = 0; i < stackCount; i++) {
207 TaskStackView stackView = stackViews.get(i);
208 TaskStack stack = stackView.getStack();
209 // Iterate the stack views and try and find the given task.
210 List<TaskView> taskViews = stackView.getTaskViews();
211 int taskViewCount = taskViews.size();
212 for (int j = 0; j < taskViewCount; j++) {
213 TaskView tv = taskViews.get(j);
214 if (tv.getTask() == task) {
215 onTaskViewClicked(stackView, tv, stack, task, false);
216 return true;
217 }
218 }
219 }
220 return false;
221 }
222
Winson Chungdcfa7972014-07-22 12:27:13 -0700223 /** Launches the task that Recents was launched from, if possible */
224 public boolean launchPreviousTask() {
Winson Chung47c4c692014-03-17 10:17:11 -0700225 // Get the first stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800226 List<TaskStackView> stackViews = getTaskStackViews();
227 int stackCount = stackViews.size();
228 for (int i = 0; i < stackCount; i++) {
229 TaskStackView stackView = stackViews.get(i);
230 TaskStack stack = stackView.getStack();
231 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -0700232
Winson Chungd16c5652015-01-26 16:11:07 -0800233 // Find the launch task in the stack
234 if (!tasks.isEmpty()) {
235 int taskCount = tasks.size();
236 for (int j = 0; j < taskCount; j++) {
237 if (tasks.get(j).isLaunchTarget) {
238 Task task = tasks.get(j);
239 TaskView tv = stackView.getChildViewForTask(task);
240 onTaskViewClicked(stackView, tv, stack, task, false);
241 return true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800242 }
243 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800244 }
245 }
246 return false;
247 }
248
Winson Chung24cf1522014-05-29 12:03:33 -0700249 /** Requests all task stacks to start their enter-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700250 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung740c3ac2014-11-12 16:14:38 -0800251 // We have to increment/decrement the post animation trigger in case there are no children
252 // to ensure that it runs
253 ctx.postAnimationTrigger.increment();
254
Winson Chungd16c5652015-01-26 16:11:07 -0800255 List<TaskStackView> stackViews = getTaskStackViews();
256 int stackCount = stackViews.size();
257 for (int i = 0; i < stackCount; i++) {
258 TaskStackView stackView = stackViews.get(i);
259 stackView.startEnterRecentsAnimation(ctx);
Winson Chung24cf1522014-05-29 12:03:33 -0700260 }
Winson Chung740c3ac2014-11-12 16:14:38 -0800261 ctx.postAnimationTrigger.decrement();
Winson Chung24cf1522014-05-29 12:03:33 -0700262 }
263
Winson Chungd42a6cf2014-06-03 16:24:04 -0700264 /** Requests all task stacks to start their exit-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700265 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chunga91c2932014-11-07 15:02:38 -0800266 // We have to increment/decrement the post animation trigger in case there are no children
267 // to ensure that it runs
268 ctx.postAnimationTrigger.increment();
Winson Chungd16c5652015-01-26 16:11:07 -0800269 List<TaskStackView> stackViews = getTaskStackViews();
270 int stackCount = stackViews.size();
271 for (int i = 0; i < stackCount; i++) {
272 TaskStackView stackView = stackViews.get(i);
273 stackView.startExitToHomeAnimation(ctx);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700274 }
Winson Chunga91c2932014-11-07 15:02:38 -0800275 ctx.postAnimationTrigger.decrement();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700276
Winson Chung969f5862014-06-16 17:08:24 -0700277 // Notify of the exit animation
Winson Chungcdbbb7e2014-06-24 12:11:49 -0700278 mCb.onExitToHomeAnimationTriggered();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700279 }
280
Winson Chungf7bca432014-04-30 17:11:13 -0700281 /** Adds the search bar */
Winson Chungaee097c2015-04-02 18:16:02 -0700282 public void setSearchBar(RecentsAppWidgetHostView searchBar) {
Winson Chungf7bca432014-04-30 17:11:13 -0700283 // Create the search bar (and hide it if we have no recent tasks)
Winson Chung814086d2014-05-07 15:01:14 -0700284 if (Constants.DebugFlags.App.EnableSearchLayout) {
Winson Chungf7bca432014-04-30 17:11:13 -0700285 // Remove the previous search bar if one exists
286 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
287 removeView(mSearchBar);
Winson Chungecd9b302014-04-16 17:07:18 -0700288 }
Winson Chungf7bca432014-04-30 17:11:13 -0700289 // Add the new search bar
290 if (searchBar != null) {
291 mSearchBar = searchBar;
Winson Chungf7bca432014-04-30 17:11:13 -0700292 addView(mSearchBar);
Winson Chungf7bca432014-04-30 17:11:13 -0700293 }
294 }
Winson Chungecd9b302014-04-16 17:07:18 -0700295 }
296
Winson Chung772b6b12014-07-03 15:54:02 -0700297 /** Returns whether there is currently a search bar */
Winson Chungaee097c2015-04-02 18:16:02 -0700298 public boolean hasValidSearchBar() {
299 return mSearchBar != null && !mSearchBar.isReinflateRequired();
Winson Chung772b6b12014-07-03 15:54:02 -0700300 }
301
302 /** Sets the visibility of the search bar */
303 public void setSearchBarVisibility(int visibility) {
304 if (mSearchBar != null) {
305 mSearchBar.setVisibility(visibility);
Winson Chung012ef362014-07-31 18:36:25 -0700306 // Always bring the search bar to the top
307 mSearchBar.bringToFront();
Winson Chung772b6b12014-07-03 15:54:02 -0700308 }
309 }
310
Winson Chungf7bca432014-04-30 17:11:13 -0700311 /**
312 * This is called with the full size of the window since we are handling our own insets.
313 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800314 @Override
315 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
316 int width = MeasureSpec.getSize(widthMeasureSpec);
317 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700318
Winson Chungf7bca432014-04-30 17:11:13 -0700319 // Get the search bar bounds and measure the search bar layout
Winson Chungecd9b302014-04-16 17:07:18 -0700320 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700321 Rect searchBarSpaceBounds = new Rect();
Winson Chungdcfa7972014-07-22 12:27:13 -0700322 mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
Winson Chungf7bca432014-04-30 17:11:13 -0700323 mSearchBar.measure(
324 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
325 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
Winson Chungecd9b302014-04-16 17:07:18 -0700326 }
327
Winson Chungf7bca432014-04-30 17:11:13 -0700328 Rect taskStackBounds = new Rect();
Winson Chungd16c5652015-01-26 16:11:07 -0800329 mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
Winson Chungdcfa7972014-07-22 12:27:13 -0700330 mConfig.systemInsets.right, taskStackBounds);
Winson Chung303e1ff2014-03-07 15:06:19 -0800331
Winson Chungd16c5652015-01-26 16:11:07 -0800332 // Measure each TaskStackView with the full width and height of the window since the
Winson Chungdcfa7972014-07-22 12:27:13 -0700333 // transition view is a child of that stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800334 List<TaskStackView> stackViews = getTaskStackViews();
335 List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
336 taskStackBounds);
337 int stackCount = stackViews.size();
338 for (int i = 0; i < stackCount; i++) {
339 TaskStackView stackView = stackViews.get(i);
340 if (stackView.getVisibility() != GONE) {
341 // We are going to measure the TaskStackView with the whole RecentsView dimensions,
342 // but the actual stack is going to be inset to the bounds calculated by the layout
343 // algorithm
344 stackView.setStackInsetRect(stackViewsBounds.get(i));
345 stackView.measure(widthMeasureSpec, heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800346 }
347 }
348
349 setMeasuredDimension(width, height);
350 }
351
Winson Chungf7bca432014-04-30 17:11:13 -0700352 /**
353 * This is called with the full size of the window since we are handling our own insets.
354 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800355 @Override
356 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chungf7bca432014-04-30 17:11:13 -0700357 // Get the search bar bounds so that we lay it out
Winson Chungecd9b302014-04-16 17:07:18 -0700358 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700359 Rect searchBarSpaceBounds = new Rect();
Winson Chungdcfa7972014-07-22 12:27:13 -0700360 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
361 mConfig.systemInsets.top, searchBarSpaceBounds);
362 mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
363 searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
Winson Chungecd9b302014-04-16 17:07:18 -0700364 }
365
Winson Chungdcfa7972014-07-22 12:27:13 -0700366 // Layout each TaskStackView with the full width and height of the window since the
367 // transition view is a child of that stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800368 List<TaskStackView> stackViews = getTaskStackViews();
369 int stackCount = stackViews.size();
370 for (int i = 0; i < stackCount; i++) {
371 TaskStackView stackView = stackViews.get(i);
372 if (stackView.getVisibility() != GONE) {
373 stackView.layout(left, top, left + stackView.getMeasuredWidth(),
374 top + stackView.getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800375 }
376 }
377 }
378
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700379 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700380 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700381 // Update the configuration with the latest system insets and trigger a relayout
382 mConfig.updateSystemInsets(insets.getSystemWindowInsets());
383 requestLayout();
Winson Chungdcfa7972014-07-22 12:27:13 -0700384 return insets.consumeSystemWindowInsets();
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700385 }
386
Winson Chunga26fb782014-06-12 17:52:39 -0700387 /** Notifies each task view of the user interaction. */
388 public void onUserInteraction() {
389 // Get the first stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800390 List<TaskStackView> stackViews = getTaskStackViews();
391 int stackCount = stackViews.size();
392 for (int i = 0; i < stackCount; i++) {
393 TaskStackView stackView = stackViews.get(i);
394 stackView.onUserInteraction();
Winson Chunga26fb782014-06-12 17:52:39 -0700395 }
396 }
397
Winson Chung1e8d71b2014-05-16 17:05:22 -0700398 /** Focuses the next task in the first stack view */
399 public void focusNextTask(boolean forward) {
400 // Get the first stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800401 List<TaskStackView> stackViews = getTaskStackViews();
402 if (!stackViews.isEmpty()) {
403 stackViews.get(0).focusNextTask(forward, true);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700404 }
Winson Chunga0e88b52014-08-11 19:25:42 -0700405 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700406
Winson Chunga0e88b52014-08-11 19:25:42 -0700407 /** Dismisses the focused task. */
408 public void dismissFocusedTask() {
409 // Get the first stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800410 List<TaskStackView> stackViews = getTaskStackViews();
411 if (!stackViews.isEmpty()) {
412 stackViews.get(0).dismissFocusedTask();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700413 }
414 }
415
Winson Chung303e1ff2014-03-07 15:06:19 -0800416 /** Unfilters any filtered stacks */
417 public boolean unfilterFilteredStacks() {
Winson Chungdcfa7972014-07-22 12:27:13 -0700418 if (mStacks != null) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800419 // Check if there are any filtered stacks and unfilter them before we back out of Recents
420 boolean stacksUnfiltered = false;
Winson Chungdcfa7972014-07-22 12:27:13 -0700421 int numStacks = mStacks.size();
422 for (int i = 0; i < numStacks; i++) {
423 TaskStack stack = mStacks.get(i);
Winson Chung303e1ff2014-03-07 15:06:19 -0800424 if (stack.hasFilteredTasks()) {
425 stack.unfilterTasks();
426 stacksUnfiltered = true;
427 }
428 }
429 return stacksUnfiltered;
430 }
431 return false;
432 }
433
Winson Chung47c4c692014-03-17 10:17:11 -0700434 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800435
436 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700437 public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
Winson Chung1f24c7e2014-07-11 17:06:48 -0700438 final TaskStack stack, final Task task, final boolean lockToTask) {
Winson Chung47c4c692014-03-17 10:17:11 -0700439 // Notify any callbacks of the launching of a new task
440 if (mCb != null) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700441 mCb.onTaskViewClicked();
Winson Chung47c4c692014-03-17 10:17:11 -0700442 }
443
Winson Chunge0e45bc2014-06-17 17:56:17 -0700444 // Upfront the processing of the thumbnail
Winson Chungffa2ec62014-07-03 15:54:42 -0700445 TaskViewTransform transform = new TaskViewTransform();
Jorim Jaggicb557032014-09-16 23:09:24 +0200446 View sourceView;
Winson Chunge0e45bc2014-06-17 17:56:17 -0700447 int offsetX = 0;
448 int offsetY = 0;
Winson Chung012ef362014-07-31 18:36:25 -0700449 float stackScroll = stackView.getScroller().getStackScroll();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700450 if (tv == null) {
451 // If there is no actual task view, then use the stack view as the source view
452 // and then offset to the expected transform rect, but bound this to just
453 // outside the display rect (to ensure we don't animate from too far away)
454 sourceView = stackView;
Winson Chung012ef362014-07-31 18:36:25 -0700455 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700456 offsetX = transform.rect.left;
Winson Chunge41ab842014-08-07 15:55:37 -0700457 offsetY = mConfig.displayRect.height();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700458 } else {
Jorim Jaggicb557032014-09-16 23:09:24 +0200459 sourceView = tv.mThumbnailView;
Winson Chung012ef362014-07-31 18:36:25 -0700460 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700461 }
462
463 // Compute the thumbnail to scale up from
Winson Chung1f24c7e2014-07-11 17:06:48 -0700464 final SystemServicesProxy ssp =
465 RecentsTaskLoader.getInstance().getSystemServicesProxy();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700466 ActivityOptions opts = null;
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200467 if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
468 task.thumbnail.getHeight() > 0) {
Winson Chunga4ccb862014-08-22 15:26:27 -0700469 Bitmap b;
470 if (tv != null) {
471 // Disable any focused state before we draw the header
472 if (tv.isFocusedTask()) {
473 tv.unsetFocusedTask();
474 }
475
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200476 float scale = tv.getScaleX();
477 int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
478 int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
479 b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
480 Bitmap.Config.ARGB_8888);
Winson Chunga4ccb862014-08-22 15:26:27 -0700481 if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
482 b.eraseColor(0xFFff0000);
483 } else {
484 Canvas c = new Canvas(b);
485 c.scale(tv.getScaleX(), tv.getScaleY());
486 tv.mHeaderView.draw(c);
487 c.setBitmap(null);
488 }
489 } else {
490 // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200491 b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
Winson Chunga4ccb862014-08-22 15:26:27 -0700492 }
Winson Chung1f24c7e2014-07-11 17:06:48 -0700493 ActivityOptions.OnAnimationStartedListener animStartedListener = null;
494 if (lockToTask) {
495 animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
496 boolean mTriggered = false;
497 @Override
498 public void onAnimationStarted() {
499 if (!mTriggered) {
500 postDelayed(new Runnable() {
501 @Override
502 public void run() {
Jason Monk18f99d92014-09-11 13:36:42 -0400503 mCb.onScreenPinningRequest();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700504 }
505 }, 350);
506 mTriggered = true;
507 }
508 }
509 };
510 }
Winson Chungd16c5652015-01-26 16:11:07 -0800511 if (mConfig.multiStackEnabled) {
512 opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
513 R.anim.recents_from_unknown_enter,
514 R.anim.recents_from_unknown_exit,
515 sourceView.getHandler(), animStartedListener);
516 } else {
517 opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
518 b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
519 sourceView.getHandler(), animStartedListener);
520 }
Winson Chunge0e45bc2014-06-17 17:56:17 -0700521 }
522
523 final ActivityOptions launchOpts = opts;
Winson Chung303e1ff2014-03-07 15:06:19 -0800524 final Runnable launchRunnable = new Runnable() {
525 @Override
526 public void run() {
Winson Chung4be04452014-03-24 17:22:30 -0700527 if (task.isActive) {
528 // Bring an active task to the foreground
Craig Mautnerdc00cbe2014-07-20 17:48:47 -0700529 ssp.moveTaskToFront(task.key.id, launchOpts);
Winson Chung4be04452014-03-24 17:22:30 -0700530 } else {
Winson Chung4e96eb72014-09-17 15:16:09 +0200531 if (ssp.startActivityFromRecents(getContext(), task.key.id,
532 task.activityLabel, launchOpts)) {
Craig Mautnerdc00cbe2014-07-20 17:48:47 -0700533 if (launchOpts == null && lockToTask) {
Jason Monk18f99d92014-09-11 13:36:42 -0400534 mCb.onScreenPinningRequest();
Winson Chung4be04452014-03-24 17:22:30 -0700535 }
Winson Chung4e96eb72014-09-17 15:16:09 +0200536 } else {
537 // Dismiss the task and return the user to home if we fail to
538 // launch the task
539 onTaskViewDismissed(task);
540 if (mCb != null) {
541 mCb.onTaskLaunchFailed();
542 }
Winson Chung4be04452014-03-24 17:22:30 -0700543 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800544 }
545 }
546 };
547
548 // Launch the app right away if there is no task view, otherwise, animate the icon out first
Winson Chung814086d2014-05-07 15:01:14 -0700549 if (tv == null) {
Winson Chunga91c2932014-11-07 15:02:38 -0800550 launchRunnable.run();
Winson Chung303e1ff2014-03-07 15:06:19 -0800551 } else {
Winson Chunga4ccb862014-08-22 15:26:27 -0700552 if (!task.group.isFrontMostTask(task)) {
553 // For affiliated tasks that are behind other tasks, we must animate the front cards
554 // out of view before starting the task transition
Winson Chungebfc6982014-08-26 12:25:34 -0700555 stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
Winson Chunga4ccb862014-08-22 15:26:27 -0700556 } else {
557 // Otherwise, we can start the task transition immediately
Winson Chungebfc6982014-08-26 12:25:34 -0700558 stackView.startLaunchTaskAnimation(tv, null, lockToTask);
Winson Chunga91c2932014-11-07 15:02:38 -0800559 launchRunnable.run();
Winson Chunga4ccb862014-08-22 15:26:27 -0700560 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800561 }
562 }
Winson Chung9f9679d2014-04-11 16:49:09 -0700563
564 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700565 public void onTaskViewAppInfoClicked(Task t) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700566 // Create a new task stack with the application info details activity
567 Intent baseIntent = t.key.baseIntent;
568 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
569 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
570 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
571 TaskStackBuilder.create(getContext())
Kenny Guy94a07ac2014-08-07 15:34:38 +0100572 .addNextIntentWithParentStack(intent).startActivities(null,
Winson Chunga4ccb862014-08-22 15:26:27 -0700573 new UserHandle(t.key.userId));
Winson Chung9f9679d2014-04-11 16:49:09 -0700574 }
Winson Chung9f49df92014-05-07 18:08:34 -0700575
Winson Chung5393dff2014-05-08 14:25:43 -0700576 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700577 public void onTaskViewDismissed(Task t) {
Winson Chung5393dff2014-05-08 14:25:43 -0700578 // Remove any stored data from the loader. We currently don't bother notifying the views
Winson Chung7aceb9a2014-07-03 13:38:01 -0700579 // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views
Winson Chung5393dff2014-05-08 14:25:43 -0700580 // either don't need to be updated, or have already been removed.
581 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
582 loader.deleteTaskData(t, false);
583
584 // Remove the old task from activity manager
Winson Chung2cf8b222015-01-20 11:44:05 -0800585 loader.getSystemServicesProxy().removeTask(t.key.id);
Winson Chung5393dff2014-05-08 14:25:43 -0700586 }
587
Winson Chung8e548f72014-06-24 14:40:53 -0700588 @Override
Winson Chung6ac8bd62015-01-07 16:38:35 -0800589 public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks) {
590 if (removedTasks != null) {
591 int taskCount = removedTasks.size();
592 for (int i = 0; i < taskCount; i++) {
593 onTaskViewDismissed(removedTasks.get(i));
594 }
595 }
596
Winson Chung7aceb9a2014-07-03 13:38:01 -0700597 mCb.onAllTaskViewsDismissed();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700598 }
599
Winson Chungb0a28ea2014-10-28 15:21:35 -0700600 /** Final callback after Recents is finally hidden. */
601 public void onRecentsHidden() {
602 // Notify each task stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800603 List<TaskStackView> stackViews = getTaskStackViews();
604 int stackCount = stackViews.size();
605 for (int i = 0; i < stackCount; i++) {
606 TaskStackView stackView = stackViews.get(i);
607 stackView.onRecentsHidden();
Winson Chungb0a28ea2014-10-28 15:21:35 -0700608 }
609 }
610
Winson Chungd7b2cb12014-06-26 15:08:50 -0700611 @Override
Winson Chung8e548f72014-06-24 14:40:53 -0700612 public void onTaskStackFilterTriggered() {
613 // Hide the search bar
614 if (mSearchBar != null) {
615 mSearchBar.animate()
616 .alpha(0f)
617 .setStartDelay(0)
618 .setInterpolator(mConfig.fastOutSlowInInterpolator)
619 .setDuration(mConfig.filteringCurrentViewsAnimDuration)
620 .withLayer()
621 .start();
622 }
623 }
624
625 @Override
626 public void onTaskStackUnfilterTriggered() {
627 // Show the search bar
628 if (mSearchBar != null) {
629 mSearchBar.animate()
630 .alpha(1f)
631 .setStartDelay(0)
632 .setInterpolator(mConfig.fastOutSlowInInterpolator)
633 .setDuration(mConfig.filteringNewViewsAnimDuration)
634 .withLayer()
635 .start();
636 }
637 }
638
Winson Chungd16c5652015-01-26 16:11:07 -0800639 @Override
Skuhneece738b2015-03-17 15:02:18 -0700640 public void onTaskResize(Task t) {
Winson Chungd16c5652015-01-26 16:11:07 -0800641 if (mCb != null) {
Skuhneece738b2015-03-17 15:02:18 -0700642 mCb.onTaskResize(t);
Winson Chungd16c5652015-01-26 16:11:07 -0800643 }
644 }
645
Winson Chung9f49df92014-05-07 18:08:34 -0700646 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
647
648 @Override
Winson Chung04400672014-10-17 14:53:30 -0700649 public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
Winson Chung9f49df92014-05-07 18:08:34 -0700650 // Propagate this event down to each task stack view
Winson Chungd16c5652015-01-26 16:11:07 -0800651 List<TaskStackView> stackViews = getTaskStackViews();
652 int stackCount = stackViews.size();
653 for (int i = 0; i < stackCount; i++) {
654 TaskStackView stackView = stackViews.get(i);
655 stackView.onPackagesChanged(monitor, packageName, userId);
Winson Chung9f49df92014-05-07 18:08:34 -0700656 }
657 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800658}