blob: 99b012e0c9bcb8121ace22568d616070e89a4360 [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 Chungc620baf2014-03-19 17:15:29 -070021import android.content.ActivityNotFoundException;
Winson Chung9f49df92014-05-07 18:08:34 -070022import android.content.ComponentName;
Winson Chung303e1ff2014-03-07 15:06:19 -080023import android.content.Context;
24import android.content.Intent;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
Winson Chung8eaeb7d2014-06-25 15:10:59 -070027import android.graphics.Paint;
Winson Chung303e1ff2014-03-07 15:06:19 -080028import android.graphics.Rect;
Winson Chung9f9679d2014-04-11 16:49:09 -070029import android.net.Uri;
Winson Chung303e1ff2014-03-07 15:06:19 -080030import android.os.UserHandle;
Winson Chung9f9679d2014-04-11 16:49:09 -070031import android.provider.Settings;
Winson Chung8e548f72014-06-24 14:40:53 -070032import android.util.AttributeSet;
Winson Chungecd9b302014-04-16 17:07:18 -070033import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080034import android.view.View;
Winson Chung653f70c22014-05-19 14:49:42 -070035import android.view.WindowInsets;
Winson Chung303e1ff2014-03-07 15:06:19 -080036import android.widget.FrameLayout;
Winson Chung303e1ff2014-03-07 15:06:19 -080037import com.android.systemui.recents.Constants;
38import com.android.systemui.recents.RecentsConfiguration;
Winson Chung1f24c7e2014-07-11 17:06:48 -070039import com.android.systemui.recents.misc.Console;
40import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungf1fbd772014-06-24 18:06:58 -070041import com.android.systemui.recents.model.RecentsPackageMonitor;
42import com.android.systemui.recents.model.RecentsTaskLoader;
Winson Chung303e1ff2014-03-07 15:06:19 -080043import com.android.systemui.recents.model.SpaceNode;
44import com.android.systemui.recents.model.Task;
45import com.android.systemui.recents.model.TaskStack;
46
47import java.util.ArrayList;
Winson Chung9f49df92014-05-07 18:08:34 -070048import java.util.Set;
Winson Chung303e1ff2014-03-07 15:06:19 -080049
Winson Chung303e1ff2014-03-07 15:06:19 -080050/**
51 * This view is the the top level layout that contains TaskStacks (which are laid out according
52 * to their SpaceNode bounds.
53 */
Winson Chung9f49df92014-05-07 18:08:34 -070054public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
55 RecentsPackageMonitor.PackageCallbacks {
Winson Chung47c4c692014-03-17 10:17:11 -070056
57 /** The RecentsView callbacks */
58 public interface RecentsViewCallbacks {
Winson Chung7aceb9a2014-07-03 13:38:01 -070059 public void onTaskViewClicked();
60 public void onAllTaskViewsDismissed();
Winson Chungcdbbb7e2014-06-24 12:11:49 -070061 public void onExitToHomeAnimationTriggered();
Winson Chung47c4c692014-03-17 10:17:11 -070062 }
63
Winson Chungd42a6cf2014-06-03 16:24:04 -070064 RecentsConfiguration mConfig;
65 LayoutInflater mInflater;
Winson Chung8eaeb7d2014-06-25 15:10:59 -070066 Paint mDebugModePaint;
Winson Chungd42a6cf2014-06-03 16:24:04 -070067
Winson Chung303e1ff2014-03-07 15:06:19 -080068 // The space partitioning root of this container
69 SpaceNode mBSP;
Winson Chungecd9b302014-04-16 17:07:18 -070070 // Search bar view
71 View mSearchBar;
Winson Chung47c4c692014-03-17 10:17:11 -070072 // Recents view callbacks
73 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080074
75 public RecentsView(Context context) {
76 super(context);
Winson Chung8e548f72014-06-24 14:40:53 -070077 }
78
79 public RecentsView(Context context, AttributeSet attrs) {
80 this(context, attrs, 0);
81 }
82
83 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
84 this(context, attrs, defStyleAttr, 0);
85 }
86
87 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
88 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -070089 mConfig = RecentsConfiguration.getInstance();
Winson Chungecd9b302014-04-16 17:07:18 -070090 mInflater = LayoutInflater.from(context);
Winson Chung303e1ff2014-03-07 15:06:19 -080091 }
92
Winson Chung47c4c692014-03-17 10:17:11 -070093 /** Sets the callbacks */
94 public void setCallbacks(RecentsViewCallbacks cb) {
95 mCb = cb;
96 }
97
Winson Chung303e1ff2014-03-07 15:06:19 -080098 /** Set/get the bsp root node */
99 public void setBSP(SpaceNode n) {
100 mBSP = n;
101
Winson Chung7aceb9a2014-07-03 13:38:01 -0700102 // Remove all TaskStackViews (but leave the search bar)
103 int childCount = getChildCount();
104 for (int i = childCount - 1; i >= 0; i--) {
105 View v = getChildAt(i);
106 if (v != mSearchBar) {
107 removeViewAt(i);
108 }
109 }
110
Winson Chung04dfe0d2014-03-14 14:06:29 -0700111 // Create and add all the stacks for this partition of space.
Winson Chung303e1ff2014-03-07 15:06:19 -0800112 ArrayList<TaskStack> stacks = mBSP.getStacks();
113 for (TaskStack stack : stacks) {
114 TaskStackView stackView = new TaskStackView(getContext(), stack);
115 stackView.setCallbacks(this);
116 addView(stackView);
117 }
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700118
119 // Enable debug mode drawing
120 if (mConfig.debugModeEnabled) {
121 mDebugModePaint = new Paint();
122 mDebugModePaint.setColor(0xFFff0000);
123 mDebugModePaint.setStyle(Paint.Style.STROKE);
124 mDebugModePaint.setStrokeWidth(5f);
125 setWillNotDraw(false);
126 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800127 }
128
Winson Chung1e8d71b2014-05-16 17:05:22 -0700129 /** Launches the focused task from the first stack if possible */
130 public boolean launchFocusedTask() {
131 // Get the first stack view
132 int childCount = getChildCount();
133 for (int i = 0; i < childCount; i++) {
134 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700135 if (child != mSearchBar) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700136 TaskStackView stackView = (TaskStackView) child;
137 TaskStack stack = stackView.mStack;
138 // Iterate the stack views and try and find the focused task
139 int taskCount = stackView.getChildCount();
140 for (int j = 0; j < taskCount; j++) {
141 TaskView tv = (TaskView) stackView.getChildAt(j);
142 Task task = tv.getTask();
143 if (tv.isFocusedTask()) {
Winson Chung10f81392014-05-20 16:21:31 -0700144 if (Console.Enabled) {
145 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
146 "Found focused Task");
147 }
Winson Chung1f24c7e2014-07-11 17:06:48 -0700148 onTaskViewClicked(stackView, tv, stack, task, false);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700149 return true;
150 }
151 }
152 }
153 }
Winson Chung10f81392014-05-20 16:21:31 -0700154 if (Console.Enabled) {
155 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
156 "No Tasks focused");
157 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700158 return false;
159 }
160
Winson Chung303e1ff2014-03-07 15:06:19 -0800161 /** Launches the first task from the first stack if possible */
162 public boolean launchFirstTask() {
Winson Chung47c4c692014-03-17 10:17:11 -0700163 // Get the first stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800164 int childCount = getChildCount();
165 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700166 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700167 if (child != mSearchBar) {
Winson Chungecd9b302014-04-16 17:07:18 -0700168 TaskStackView stackView = (TaskStackView) child;
169 TaskStack stack = stackView.mStack;
170 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -0700171
Winson Chungecd9b302014-04-16 17:07:18 -0700172 // Get the first task in the stack
173 if (!tasks.isEmpty()) {
174 Task task = tasks.get(tasks.size() - 1);
175 TaskView tv = null;
Winson Chung47c4c692014-03-17 10:17:11 -0700176
Winson Chungecd9b302014-04-16 17:07:18 -0700177 // Try and use the first child task view as the source of the launch animation
178 if (stackView.getChildCount() > 0) {
179 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
180 if (stv.getTask() == task) {
181 tv = stv;
182 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800183 }
Winson Chung1f24c7e2014-07-11 17:06:48 -0700184 onTaskViewClicked(stackView, tv, stack, task, false);
Winson Chungecd9b302014-04-16 17:07:18 -0700185 return true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800186 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800187 }
188 }
189 return false;
190 }
191
Winson Chung24cf1522014-05-29 12:03:33 -0700192 /** Requests all task stacks to start their enter-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700193 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chungc6011de2014-06-30 18:04:55 -0700194 // Handle the case when there are no views by incrementing and decrementing after all
195 // animations are started.
196 ctx.postAnimationTrigger.increment();
197
Winson Chung24cf1522014-05-29 12:03:33 -0700198 int childCount = getChildCount();
199 for (int i = 0; i < childCount; i++) {
200 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700201 if (child != mSearchBar) {
Winson Chung24cf1522014-05-29 12:03:33 -0700202 TaskStackView stackView = (TaskStackView) child;
Winson Chung969f5862014-06-16 17:08:24 -0700203 stackView.startEnterRecentsAnimation(ctx);
Winson Chung24cf1522014-05-29 12:03:33 -0700204 }
205 }
Winson Chungc6011de2014-06-30 18:04:55 -0700206
207 // Handle the case when there are no views by incrementing and decrementing after all
208 // animations are started.
209 ctx.postAnimationTrigger.decrement();
Winson Chung24cf1522014-05-29 12:03:33 -0700210 }
211
Winson Chungd42a6cf2014-06-03 16:24:04 -0700212 /** Requests all task stacks to start their exit-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700213 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700214 // Handle the case when there are no views by incrementing and decrementing after all
215 // animations are started.
216 ctx.postAnimationTrigger.increment();
217
Winson Chungd7b2cb12014-06-26 15:08:50 -0700218 int childCount = getChildCount();
219 for (int i = 0; i < childCount; i++) {
220 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700221 if (child != mSearchBar) {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700222 TaskStackView stackView = (TaskStackView) child;
223 stackView.startExitToHomeAnimation(ctx);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700224 }
225 }
226
227 // Handle the case when there are no views by incrementing and decrementing after all
228 // animations are started.
229 ctx.postAnimationTrigger.decrement();
Winson Chung969f5862014-06-16 17:08:24 -0700230
231 // Notify of the exit animation
Winson Chungcdbbb7e2014-06-24 12:11:49 -0700232 mCb.onExitToHomeAnimationTriggered();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700233 }
234
Winson Chungf7bca432014-04-30 17:11:13 -0700235 /** Adds the search bar */
236 public void setSearchBar(View searchBar) {
237 // Create the search bar (and hide it if we have no recent tasks)
Winson Chung814086d2014-05-07 15:01:14 -0700238 if (Constants.DebugFlags.App.EnableSearchLayout) {
Winson Chungf7bca432014-04-30 17:11:13 -0700239 // Remove the previous search bar if one exists
240 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
241 removeView(mSearchBar);
Winson Chungecd9b302014-04-16 17:07:18 -0700242 }
Winson Chungf7bca432014-04-30 17:11:13 -0700243 // Add the new search bar
244 if (searchBar != null) {
245 mSearchBar = searchBar;
Winson Chungf7bca432014-04-30 17:11:13 -0700246 addView(mSearchBar);
247
Winson Chung10f81392014-05-20 16:21:31 -0700248 if (Console.Enabled) {
249 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
250 "" + (mSearchBar.getVisibility() == View.VISIBLE),
251 Console.AnsiBlue);
252 }
Winson Chungf7bca432014-04-30 17:11:13 -0700253 }
254 }
Winson Chungecd9b302014-04-16 17:07:18 -0700255 }
256
Winson Chung772b6b12014-07-03 15:54:02 -0700257 /** Returns whether there is currently a search bar */
258 public boolean hasSearchBar() {
259 return mSearchBar != null;
260 }
261
262 /** Sets the visibility of the search bar */
263 public void setSearchBarVisibility(int visibility) {
264 if (mSearchBar != null) {
265 mSearchBar.setVisibility(visibility);
266 }
267 }
268
Winson Chungf7bca432014-04-30 17:11:13 -0700269 /**
270 * This is called with the full size of the window since we are handling our own insets.
271 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800272 @Override
273 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
274 int width = MeasureSpec.getSize(widthMeasureSpec);
Winson Chung67369052014-04-07 17:35:48 -0700275 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800276 int height = MeasureSpec.getSize(heightMeasureSpec);
277 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700278
Winson Chung10f81392014-05-20 16:21:31 -0700279 if (Console.Enabled) {
280 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]",
281 "width: " + width + " height: " + height, Console.AnsiGreen);
282 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
283 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
284 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800285
Winson Chungf7bca432014-04-30 17:11:13 -0700286 // Get the search bar bounds and measure the search bar layout
Winson Chungecd9b302014-04-16 17:07:18 -0700287 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700288 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700289 mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
Winson Chungf7bca432014-04-30 17:11:13 -0700290 mSearchBar.measure(
291 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
292 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
Winson Chungecd9b302014-04-16 17:07:18 -0700293 }
294
Winson Chungf7bca432014-04-30 17:11:13 -0700295 // We give the full width of the space, not including the right nav bar insets in landscape,
296 // to the stack view, since we want the tasks to render under the search bar in landscape.
297 // In addition, we give it the full height, not including the top inset or search bar space,
298 // since we want the tasks to render under the navigation buttons in portrait.
299 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700300 mConfig.getTaskStackBounds(width, height, taskStackBounds);
301 int childWidth = width - mConfig.systemInsets.right;
302 int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800303
Winson Chungf7bca432014-04-30 17:11:13 -0700304 // Measure each TaskStackView
Winson Chung303e1ff2014-03-07 15:06:19 -0800305 int childCount = getChildCount();
306 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700307 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700308 if (child != mSearchBar && child.getVisibility() != GONE) {
Winson Chung67369052014-04-07 17:35:48 -0700309 child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
Winson Chung303e1ff2014-03-07 15:06:19 -0800310 MeasureSpec.makeMeasureSpec(childHeight, heightMode));
311 }
312 }
313
314 setMeasuredDimension(width, height);
315 }
316
Winson Chungf7bca432014-04-30 17:11:13 -0700317 /**
318 * This is called with the full size of the window since we are handling our own insets.
319 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800320 @Override
321 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung10f81392014-05-20 16:21:31 -0700322 if (Console.Enabled) {
323 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]",
324 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
325 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
326 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout");
327 }
Winson Chungbd912972014-03-18 14:36:35 -0700328
Winson Chungf7bca432014-04-30 17:11:13 -0700329 // Get the search bar bounds so that we lay it out
Winson Chungecd9b302014-04-16 17:07:18 -0700330 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700331 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700332 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
333 mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
334 mConfig.systemInsets.top + searchBarSpaceBounds.top,
335 mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
336 mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
Winson Chungecd9b302014-04-16 17:07:18 -0700337 }
338
Winson Chungf7bca432014-04-30 17:11:13 -0700339 // We offset the stack view by the left inset (if any), but lay it out under the search bar.
340 // In addition, we offset our stack views by the top inset and search bar height, but not
341 // the bottom insets because we want it to render under the navigation buttons.
342 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700343 mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
344 left += mConfig.systemInsets.left;
345 top += mConfig.systemInsets.top + taskStackBounds.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800346
347 // Layout each child
348 // XXX: Based on the space node for that task view
349 int childCount = getChildCount();
350 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700351 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700352 if (child != mSearchBar && child.getVisibility() != GONE) {
Winson Chungf7bca432014-04-30 17:11:13 -0700353 TaskStackView tsv = (TaskStackView) child;
354 child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800355 }
356 }
357 }
358
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700359 @Override
360 protected void onDraw(Canvas canvas) {
361 super.onDraw(canvas);
362 // Debug mode drawing
363 if (mConfig.debugModeEnabled) {
364 canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugModePaint);
365 }
366 }
367
368 @Override
369 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
370 if (Console.Enabled) {
371 Console.log(Constants.Log.UI.MeasureAndLayout,
372 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
373 }
374
375 // Update the configuration with the latest system insets and trigger a relayout
376 mConfig.updateSystemInsets(insets.getSystemWindowInsets());
377 requestLayout();
378
379 return insets.consumeSystemWindowInsets(false, false, false, true);
380 }
381
Winson Chunga26fb782014-06-12 17:52:39 -0700382 /** Notifies each task view of the user interaction. */
383 public void onUserInteraction() {
384 // Get the first stack view
385 TaskStackView stackView = null;
386 int childCount = getChildCount();
387 for (int i = 0; i < childCount; i++) {
388 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700389 if (child != mSearchBar) {
Winson Chunga26fb782014-06-12 17:52:39 -0700390 stackView = (TaskStackView) child;
391 stackView.onUserInteraction();
392 }
393 }
394 }
395
Winson Chung1e8d71b2014-05-16 17:05:22 -0700396 /** Focuses the next task in the first stack view */
397 public void focusNextTask(boolean forward) {
398 // Get the first stack view
399 TaskStackView stackView = null;
400 int childCount = getChildCount();
401 for (int i = 0; i < childCount; i++) {
402 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700403 if (child != mSearchBar) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700404 stackView = (TaskStackView) child;
405 break;
406 }
407 }
408
409 if (stackView != null) {
410 stackView.focusNextTask(forward);
411 }
412 }
413
Winson Chung303e1ff2014-03-07 15:06:19 -0800414 /** Unfilters any filtered stacks */
415 public boolean unfilterFilteredStacks() {
416 if (mBSP != null) {
417 // Check if there are any filtered stacks and unfilter them before we back out of Recents
418 boolean stacksUnfiltered = false;
419 ArrayList<TaskStack> stacks = mBSP.getStacks();
420 for (TaskStack stack : stacks) {
421 if (stack.hasFilteredTasks()) {
422 stack.unfilterTasks();
423 stacksUnfiltered = true;
424 }
425 }
426 return stacksUnfiltered;
427 }
428 return false;
429 }
430
Winson Chung47c4c692014-03-17 10:17:11 -0700431 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800432
433 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700434 public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
Winson Chung1f24c7e2014-07-11 17:06:48 -0700435 final TaskStack stack, final Task task, final boolean lockToTask) {
Winson Chung47c4c692014-03-17 10:17:11 -0700436 // Notify any callbacks of the launching of a new task
437 if (mCb != null) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700438 mCb.onTaskViewClicked();
Winson Chung47c4c692014-03-17 10:17:11 -0700439 }
440
Winson Chunge0e45bc2014-06-17 17:56:17 -0700441 // Upfront the processing of the thumbnail
Winson Chungffa2ec62014-07-03 15:54:42 -0700442 TaskViewTransform transform = new TaskViewTransform();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700443 View sourceView = tv;
444 int offsetX = 0;
445 int offsetY = 0;
446 int stackScroll = stackView.getStackScroll();
447 if (tv == null) {
448 // If there is no actual task view, then use the stack view as the source view
449 // and then offset to the expected transform rect, but bound this to just
450 // outside the display rect (to ensure we don't animate from too far away)
451 sourceView = stackView;
Winson Chunga433fa92014-07-08 21:50:31 -0700452 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700453 offsetX = transform.rect.left;
454 offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
455 } else {
Winson Chunga433fa92014-07-08 21:50:31 -0700456 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700457 }
458
459 // Compute the thumbnail to scale up from
Winson Chung1f24c7e2014-07-11 17:06:48 -0700460 final SystemServicesProxy ssp =
461 RecentsTaskLoader.getInstance().getSystemServicesProxy();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700462 ActivityOptions opts = null;
463 int thumbnailWidth = transform.rect.width();
464 int thumbnailHeight = transform.rect.height();
465 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
466 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
467 // Resize the thumbnail to the size of the view that we are animating from
468 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
469 Bitmap.Config.ARGB_8888);
470 Canvas c = new Canvas(b);
471 c.drawBitmap(task.thumbnail,
472 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
473 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
474 c.setBitmap(null);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700475 ActivityOptions.OnAnimationStartedListener animStartedListener = null;
476 if (lockToTask) {
477 animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
478 boolean mTriggered = false;
479 @Override
480 public void onAnimationStarted() {
481 if (!mTriggered) {
482 postDelayed(new Runnable() {
483 @Override
484 public void run() {
485 ssp.lockCurrentTask();
486 }
487 }, 350);
488 mTriggered = true;
489 }
490 }
491 };
492 }
Winson Chunge0e45bc2014-06-17 17:56:17 -0700493 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
Winson Chung1f24c7e2014-07-11 17:06:48 -0700494 b, offsetX, offsetY, animStartedListener);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700495 }
496
497 final ActivityOptions launchOpts = opts;
Winson Chung303e1ff2014-03-07 15:06:19 -0800498 final Runnable launchRunnable = new Runnable() {
499 @Override
500 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700501 if (Console.Enabled) {
502 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
503 Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
504 }
505
Winson Chung4be04452014-03-24 17:22:30 -0700506 if (task.isActive) {
507 // Bring an active task to the foreground
Winson Chunga10370f2014-04-02 12:25:04 -0700508 RecentsTaskLoader.getInstance().getSystemServicesProxy()
Winson Chunge0e45bc2014-06-17 17:56:17 -0700509 .moveTaskToFront(task.key.id, launchOpts);
Winson Chung4be04452014-03-24 17:22:30 -0700510 } else {
Winson Chung5393dff2014-05-08 14:25:43 -0700511 // Launch the activity anew with the desired animation
Winson Chungc6a16232014-04-01 14:04:48 -0700512 Intent i = new Intent(task.key.baseIntent);
Winson Chung4be04452014-03-24 17:22:30 -0700513 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
514 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
515 | Intent.FLAG_ACTIVITY_NEW_TASK);
516 try {
Winson Chung67369052014-04-07 17:35:48 -0700517 UserHandle taskUser = new UserHandle(task.userId);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700518 if (launchOpts != null) {
519 getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700520
Winson Chung4be04452014-03-24 17:22:30 -0700521 } else {
Winson Chung67369052014-04-07 17:35:48 -0700522 getContext().startActivityAsUser(i, taskUser);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700523 if (lockToTask) {
524 ssp.lockCurrentTask();
525 }
Winson Chung4be04452014-03-24 17:22:30 -0700526 }
527 } catch (ActivityNotFoundException anfe) {
528 Console.logError(getContext(), "Could not start Activity");
529 }
Winson Chung5393dff2014-05-08 14:25:43 -0700530
531 // And clean up the old task
Winson Chung7aceb9a2014-07-03 13:38:01 -0700532 onTaskViewDismissed(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800533 }
Winson Chungbd912972014-03-18 14:36:35 -0700534
Winson Chungd42a6cf2014-06-03 16:24:04 -0700535 if (Console.Enabled) {
536 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
537 Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
538 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800539 }
540 };
541
Winson Chungd42a6cf2014-06-03 16:24:04 -0700542 if (Console.Enabled) {
543 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
544 Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
545 }
Winson Chungbd912972014-03-18 14:36:35 -0700546
Winson Chung303e1ff2014-03-07 15:06:19 -0800547 // Launch the app right away if there is no task view, otherwise, animate the icon out first
Winson Chung814086d2014-05-07 15:01:14 -0700548 if (tv == null) {
Winson Chung47c4c692014-03-17 10:17:11 -0700549 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800550 } else {
Winson Chung969f5862014-06-16 17:08:24 -0700551 stackView.animateOnLaunchingTask(tv, launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800552 }
553 }
Winson Chung9f9679d2014-04-11 16:49:09 -0700554
555 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700556 public void onTaskViewAppInfoClicked(Task t) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700557 // Create a new task stack with the application info details activity
558 Intent baseIntent = t.key.baseIntent;
559 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
560 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
561 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
562 TaskStackBuilder.create(getContext())
563 .addNextIntentWithParentStack(intent).startActivities();
564 }
Winson Chung9f49df92014-05-07 18:08:34 -0700565
Winson Chung5393dff2014-05-08 14:25:43 -0700566 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700567 public void onTaskViewDismissed(Task t) {
Winson Chung5393dff2014-05-08 14:25:43 -0700568 // Remove any stored data from the loader. We currently don't bother notifying the views
Winson Chung7aceb9a2014-07-03 13:38:01 -0700569 // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views
Winson Chung5393dff2014-05-08 14:25:43 -0700570 // either don't need to be updated, or have already been removed.
571 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
572 loader.deleteTaskData(t, false);
573
574 // Remove the old task from activity manager
575 int flags = t.key.baseIntent.getFlags();
576 boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
577 Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
578 RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
579 isDocument);
580 }
581
Winson Chung8e548f72014-06-24 14:40:53 -0700582 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700583 public void onAllTaskViewsDismissed() {
584 mCb.onAllTaskViewsDismissed();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700585 }
586
587 @Override
Winson Chung8e548f72014-06-24 14:40:53 -0700588 public void onTaskStackFilterTriggered() {
589 // Hide the search bar
590 if (mSearchBar != null) {
591 mSearchBar.animate()
592 .alpha(0f)
593 .setStartDelay(0)
594 .setInterpolator(mConfig.fastOutSlowInInterpolator)
595 .setDuration(mConfig.filteringCurrentViewsAnimDuration)
596 .withLayer()
597 .start();
598 }
599 }
600
601 @Override
602 public void onTaskStackUnfilterTriggered() {
603 // Show the search bar
604 if (mSearchBar != null) {
605 mSearchBar.animate()
606 .alpha(1f)
607 .setStartDelay(0)
608 .setInterpolator(mConfig.fastOutSlowInInterpolator)
609 .setDuration(mConfig.filteringNewViewsAnimDuration)
610 .withLayer()
611 .start();
612 }
613 }
614
Winson Chung9f49df92014-05-07 18:08:34 -0700615 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
616
617 @Override
618 public void onComponentRemoved(Set<ComponentName> cns) {
619 // Propagate this event down to each task stack view
620 int childCount = getChildCount();
621 for (int i = 0; i < childCount; i++) {
622 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700623 if (child != mSearchBar) {
Winson Chung9f49df92014-05-07 18:08:34 -0700624 TaskStackView stackView = (TaskStackView) child;
625 stackView.onComponentRemoved(cns);
626 }
627 }
628 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800629}