blob: a2c250c710f0cad764b8310351d4908988f33969 [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;
27import android.graphics.Rect;
Winson Chung9f9679d2014-04-11 16:49:09 -070028import android.net.Uri;
Winson Chung303e1ff2014-03-07 15:06:19 -080029import android.os.UserHandle;
Winson Chung9f9679d2014-04-11 16:49:09 -070030import android.provider.Settings;
Winson Chungecd9b302014-04-16 17:07:18 -070031import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080032import android.view.View;
Winson Chung653f70c22014-05-19 14:49:42 -070033import android.view.WindowInsets;
Winson Chung303e1ff2014-03-07 15:06:19 -080034import android.widget.FrameLayout;
35import com.android.systemui.recents.Console;
36import com.android.systemui.recents.Constants;
37import com.android.systemui.recents.RecentsConfiguration;
Winson Chung9f49df92014-05-07 18:08:34 -070038import com.android.systemui.recents.RecentsPackageMonitor;
Winson Chunga10370f2014-04-02 12:25:04 -070039import com.android.systemui.recents.RecentsTaskLoader;
Winson Chung303e1ff2014-03-07 15:06:19 -080040import com.android.systemui.recents.model.SpaceNode;
41import com.android.systemui.recents.model.Task;
42import com.android.systemui.recents.model.TaskStack;
43
44import java.util.ArrayList;
Winson Chung9f49df92014-05-07 18:08:34 -070045import java.util.Set;
Winson Chung303e1ff2014-03-07 15:06:19 -080046
47
48/**
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 Chung521e7dc2014-06-02 15:31:56 -070057 public void onTaskLaunching(boolean isTaskInStackBounds);
Winson Chung47c4c692014-03-17 10:17:11 -070058 }
59
Winson Chungd42a6cf2014-06-03 16:24:04 -070060 RecentsConfiguration mConfig;
61 LayoutInflater mInflater;
62
Winson Chung303e1ff2014-03-07 15:06:19 -080063 // The space partitioning root of this container
64 SpaceNode mBSP;
Winson Chungf7bca432014-04-30 17:11:13 -070065 // Whether there are any tasks
66 boolean mHasTasks;
Winson Chungecd9b302014-04-16 17:07:18 -070067 // Search bar view
68 View mSearchBar;
Winson Chung47c4c692014-03-17 10:17:11 -070069 // Recents view callbacks
70 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080071
72 public RecentsView(Context context) {
73 super(context);
Winson Chungd42a6cf2014-06-03 16:24:04 -070074 mConfig = RecentsConfiguration.getInstance();
Winson Chungecd9b302014-04-16 17:07:18 -070075 mInflater = LayoutInflater.from(context);
Winson Chung303e1ff2014-03-07 15:06:19 -080076 setWillNotDraw(false);
77 }
78
Winson Chung47c4c692014-03-17 10:17:11 -070079 /** Sets the callbacks */
80 public void setCallbacks(RecentsViewCallbacks cb) {
81 mCb = cb;
82 }
83
Winson Chung303e1ff2014-03-07 15:06:19 -080084 /** Set/get the bsp root node */
85 public void setBSP(SpaceNode n) {
86 mBSP = n;
87
Winson Chung04dfe0d2014-03-14 14:06:29 -070088 // Create and add all the stacks for this partition of space.
Winson Chungf7bca432014-04-30 17:11:13 -070089 mHasTasks = false;
Winson Chung303e1ff2014-03-07 15:06:19 -080090 removeAllViews();
91 ArrayList<TaskStack> stacks = mBSP.getStacks();
92 for (TaskStack stack : stacks) {
93 TaskStackView stackView = new TaskStackView(getContext(), stack);
94 stackView.setCallbacks(this);
95 addView(stackView);
Winson Chungf7bca432014-04-30 17:11:13 -070096 mHasTasks |= (stack.getTaskCount() > 0);
Winson Chung303e1ff2014-03-07 15:06:19 -080097 }
98 }
99
Winson Chung1e8d71b2014-05-16 17:05:22 -0700100 /** Launches the focused task from the first stack if possible */
101 public boolean launchFocusedTask() {
102 // Get the first stack view
103 int childCount = getChildCount();
104 for (int i = 0; i < childCount; i++) {
105 View child = getChildAt(i);
106 if (child instanceof TaskStackView) {
107 TaskStackView stackView = (TaskStackView) child;
108 TaskStack stack = stackView.mStack;
109 // Iterate the stack views and try and find the focused task
110 int taskCount = stackView.getChildCount();
111 for (int j = 0; j < taskCount; j++) {
112 TaskView tv = (TaskView) stackView.getChildAt(j);
113 Task task = tv.getTask();
114 if (tv.isFocusedTask()) {
Winson Chung10f81392014-05-20 16:21:31 -0700115 if (Console.Enabled) {
116 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
117 "Found focused Task");
118 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700119 onTaskLaunched(stackView, tv, stack, task);
120 return true;
121 }
122 }
123 }
124 }
Winson Chung10f81392014-05-20 16:21:31 -0700125 if (Console.Enabled) {
126 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
127 "No Tasks focused");
128 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700129 return false;
130 }
131
Winson Chung303e1ff2014-03-07 15:06:19 -0800132 /** Launches the first task from the first stack if possible */
133 public boolean launchFirstTask() {
Winson Chung47c4c692014-03-17 10:17:11 -0700134 // Get the first stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800135 int childCount = getChildCount();
136 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700137 View child = getChildAt(i);
138 if (child instanceof TaskStackView) {
139 TaskStackView stackView = (TaskStackView) child;
140 TaskStack stack = stackView.mStack;
141 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -0700142
Winson Chungecd9b302014-04-16 17:07:18 -0700143 // Get the first task in the stack
144 if (!tasks.isEmpty()) {
145 Task task = tasks.get(tasks.size() - 1);
146 TaskView tv = null;
Winson Chung47c4c692014-03-17 10:17:11 -0700147
Winson Chungecd9b302014-04-16 17:07:18 -0700148 // Try and use the first child task view as the source of the launch animation
149 if (stackView.getChildCount() > 0) {
150 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
151 if (stv.getTask() == task) {
152 tv = stv;
153 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800154 }
Winson Chungecd9b302014-04-16 17:07:18 -0700155 onTaskLaunched(stackView, tv, stack, task);
156 return true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800157 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800158 }
159 }
160 return false;
161 }
162
Winson Chung24cf1522014-05-29 12:03:33 -0700163 /** Requests all task stacks to start their enter-recents animation */
Winson Chungd42a6cf2014-06-03 16:24:04 -0700164 public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung24cf1522014-05-29 12:03:33 -0700165 int childCount = getChildCount();
166 for (int i = 0; i < childCount; i++) {
167 View child = getChildAt(i);
168 if (child instanceof TaskStackView) {
169 TaskStackView stackView = (TaskStackView) child;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700170 stackView.startOnEnterAnimation(ctx);
Winson Chung24cf1522014-05-29 12:03:33 -0700171 }
172 }
173 }
174
Winson Chungd42a6cf2014-06-03 16:24:04 -0700175 /** Requests all task stacks to start their exit-recents animation */
176 public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
177 // Handle the case when there are no views by incrementing and decrementing after all
178 // animations are started.
179 ctx.postAnimationTrigger.increment();
180
181 if (Constants.DebugFlags.App.EnableHomeTransition) {
182 int childCount = getChildCount();
183 for (int i = 0; i < childCount; i++) {
184 View child = getChildAt(i);
185 if (child instanceof TaskStackView) {
186 TaskStackView stackView = (TaskStackView) child;
187 stackView.startOnExitAnimation(ctx);
188 }
189 }
190 }
191
192 // Handle the case when there are no views by incrementing and decrementing after all
193 // animations are started.
194 ctx.postAnimationTrigger.decrement();
195 }
196
Winson Chungf7bca432014-04-30 17:11:13 -0700197 /** Adds the search bar */
198 public void setSearchBar(View searchBar) {
199 // Create the search bar (and hide it if we have no recent tasks)
Winson Chung814086d2014-05-07 15:01:14 -0700200 if (Constants.DebugFlags.App.EnableSearchLayout) {
Winson Chungf7bca432014-04-30 17:11:13 -0700201 // Remove the previous search bar if one exists
202 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
203 removeView(mSearchBar);
Winson Chungecd9b302014-04-16 17:07:18 -0700204 }
Winson Chungf7bca432014-04-30 17:11:13 -0700205 // Add the new search bar
206 if (searchBar != null) {
207 mSearchBar = searchBar;
208 mSearchBar.setVisibility(mHasTasks ? View.VISIBLE : View.GONE);
209 addView(mSearchBar);
210
Winson Chung10f81392014-05-20 16:21:31 -0700211 if (Console.Enabled) {
212 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
213 "" + (mSearchBar.getVisibility() == View.VISIBLE),
214 Console.AnsiBlue);
215 }
Winson Chungf7bca432014-04-30 17:11:13 -0700216 }
217 }
Winson Chungecd9b302014-04-16 17:07:18 -0700218 }
219
Winson Chungf7bca432014-04-30 17:11:13 -0700220 /**
221 * This is called with the full size of the window since we are handling our own insets.
222 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800223 @Override
224 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
225 int width = MeasureSpec.getSize(widthMeasureSpec);
Winson Chung67369052014-04-07 17:35:48 -0700226 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800227 int height = MeasureSpec.getSize(heightMeasureSpec);
228 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700229
Winson Chung10f81392014-05-20 16:21:31 -0700230 if (Console.Enabled) {
231 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]",
232 "width: " + width + " height: " + height, Console.AnsiGreen);
233 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
234 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
235 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800236
Winson Chungf7bca432014-04-30 17:11:13 -0700237 // Get the search bar bounds and measure the search bar layout
Winson Chungecd9b302014-04-16 17:07:18 -0700238 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700239 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700240 mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
Winson Chungf7bca432014-04-30 17:11:13 -0700241 mSearchBar.measure(
242 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
243 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
Winson Chungecd9b302014-04-16 17:07:18 -0700244 }
245
Winson Chungf7bca432014-04-30 17:11:13 -0700246 // We give the full width of the space, not including the right nav bar insets in landscape,
247 // to the stack view, since we want the tasks to render under the search bar in landscape.
248 // In addition, we give it the full height, not including the top inset or search bar space,
249 // since we want the tasks to render under the navigation buttons in portrait.
250 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700251 mConfig.getTaskStackBounds(width, height, taskStackBounds);
252 int childWidth = width - mConfig.systemInsets.right;
253 int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800254
Winson Chungf7bca432014-04-30 17:11:13 -0700255 // Measure each TaskStackView
Winson Chung303e1ff2014-03-07 15:06:19 -0800256 int childCount = getChildCount();
257 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700258 View child = getChildAt(i);
259 if (child instanceof TaskStackView && child.getVisibility() != GONE) {
Winson Chung67369052014-04-07 17:35:48 -0700260 child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
Winson Chung303e1ff2014-03-07 15:06:19 -0800261 MeasureSpec.makeMeasureSpec(childHeight, heightMode));
262 }
263 }
264
265 setMeasuredDimension(width, height);
266 }
267
Winson Chungf7bca432014-04-30 17:11:13 -0700268 /**
269 * This is called with the full size of the window since we are handling our own insets.
270 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800271 @Override
272 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung10f81392014-05-20 16:21:31 -0700273 if (Console.Enabled) {
274 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]",
275 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
276 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
277 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout");
278 }
Winson Chungbd912972014-03-18 14:36:35 -0700279
Winson Chungf7bca432014-04-30 17:11:13 -0700280 // Get the search bar bounds so that we lay it out
Winson Chungecd9b302014-04-16 17:07:18 -0700281 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700282 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700283 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
284 mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
285 mConfig.systemInsets.top + searchBarSpaceBounds.top,
286 mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
287 mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
Winson Chungecd9b302014-04-16 17:07:18 -0700288 }
289
Winson Chungf7bca432014-04-30 17:11:13 -0700290 // We offset the stack view by the left inset (if any), but lay it out under the search bar.
291 // In addition, we offset our stack views by the top inset and search bar height, but not
292 // the bottom insets because we want it to render under the navigation buttons.
293 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700294 mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
295 left += mConfig.systemInsets.left;
296 top += mConfig.systemInsets.top + taskStackBounds.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800297
298 // Layout each child
299 // XXX: Based on the space node for that task view
300 int childCount = getChildCount();
301 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700302 View child = getChildAt(i);
303 if (child instanceof TaskStackView && child.getVisibility() != GONE) {
Winson Chungf7bca432014-04-30 17:11:13 -0700304 TaskStackView tsv = (TaskStackView) child;
305 child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800306 }
307 }
308 }
309
Winson Chung1e8d71b2014-05-16 17:05:22 -0700310 /** Focuses the next task in the first stack view */
311 public void focusNextTask(boolean forward) {
312 // Get the first stack view
313 TaskStackView stackView = null;
314 int childCount = getChildCount();
315 for (int i = 0; i < childCount; i++) {
316 View child = getChildAt(i);
317 if (child instanceof TaskStackView) {
318 stackView = (TaskStackView) child;
319 break;
320 }
321 }
322
323 if (stackView != null) {
324 stackView.focusNextTask(forward);
325 }
326 }
327
Winson Chung303e1ff2014-03-07 15:06:19 -0800328 @Override
329 protected void dispatchDraw(Canvas canvas) {
Winson Chung10f81392014-05-20 16:21:31 -0700330 if (Console.Enabled) {
331 Console.log(Constants.Log.UI.Draw, "[RecentsView|dispatchDraw]", "",
332 Console.AnsiPurple);
333 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800334 super.dispatchDraw(canvas);
335 }
336
337 @Override
Winson Chung653f70c22014-05-19 14:49:42 -0700338 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Winson Chung10f81392014-05-20 16:21:31 -0700339 if (Console.Enabled) {
340 Console.log(Constants.Log.UI.MeasureAndLayout,
341 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
342 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800343
344 // Update the configuration with the latest system insets and trigger a relayout
Winson Chungd42a6cf2014-06-03 16:24:04 -0700345 mConfig.updateSystemInsets(insets.getSystemWindowInsets());
Winson Chung303e1ff2014-03-07 15:06:19 -0800346 requestLayout();
347
Winson Chung653f70c22014-05-19 14:49:42 -0700348 return insets.consumeSystemWindowInsets(false, false, false, true);
Winson Chung303e1ff2014-03-07 15:06:19 -0800349 }
350
351 /** Unfilters any filtered stacks */
352 public boolean unfilterFilteredStacks() {
353 if (mBSP != null) {
354 // Check if there are any filtered stacks and unfilter them before we back out of Recents
355 boolean stacksUnfiltered = false;
356 ArrayList<TaskStack> stacks = mBSP.getStacks();
357 for (TaskStack stack : stacks) {
358 if (stack.hasFilteredTasks()) {
359 stack.unfilterTasks();
360 stacksUnfiltered = true;
361 }
362 }
363 return stacksUnfiltered;
364 }
365 return false;
366 }
367
Winson Chung47c4c692014-03-17 10:17:11 -0700368 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800369
370 @Override
371 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv,
372 final TaskStack stack, final Task task) {
Winson Chung47c4c692014-03-17 10:17:11 -0700373 // Notify any callbacks of the launching of a new task
374 if (mCb != null) {
Winson Chung521e7dc2014-06-02 15:31:56 -0700375 boolean isTaskInStackBounds = false;
376 if (stackView != null && tv != null) {
377 isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
378 }
379 mCb.onTaskLaunching(isTaskInStackBounds);
Winson Chung47c4c692014-03-17 10:17:11 -0700380 }
381
Winson Chung303e1ff2014-03-07 15:06:19 -0800382 final Runnable launchRunnable = new Runnable() {
383 @Override
384 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700385 if (Console.Enabled) {
386 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
387 Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
388 }
389
Winson Chung303e1ff2014-03-07 15:06:19 -0800390 TaskViewTransform transform;
391 View sourceView = tv;
392 int offsetX = 0;
393 int offsetY = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700394 int stackScroll = stackView.getStackScroll();
Winson Chung303e1ff2014-03-07 15:06:19 -0800395 if (tv == null) {
Winson Chungc620baf2014-03-19 17:15:29 -0700396 // If there is no actual task view, then use the stack view as the source view
397 // and then offset to the expected transform rect, but bound this to just
398 // outside the display rect (to ensure we don't animate from too far away)
Winson Chung303e1ff2014-03-07 15:06:19 -0800399 sourceView = stackView;
Winson Chungc6a16232014-04-01 14:04:48 -0700400 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
Winson Chung303e1ff2014-03-07 15:06:19 -0800401 offsetX = transform.rect.left;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700402 offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
Winson Chung303e1ff2014-03-07 15:06:19 -0800403 } else {
Winson Chungc6a16232014-04-01 14:04:48 -0700404 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
Winson Chung303e1ff2014-03-07 15:06:19 -0800405 }
406
407 // Compute the thumbnail to scale up from
408 ActivityOptions opts = null;
409 int thumbnailWidth = transform.rect.width();
410 int thumbnailHeight = transform.rect.height();
411 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
412 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
413 // Resize the thumbnail to the size of the view that we are animating from
414 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
415 Bitmap.Config.ARGB_8888);
416 Canvas c = new Canvas(b);
417 c.drawBitmap(task.thumbnail,
418 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
419 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
420 c.setBitmap(null);
421 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
422 b, offsetX, offsetY);
423 }
424
Winson Chung4be04452014-03-24 17:22:30 -0700425 if (task.isActive) {
426 // Bring an active task to the foreground
Winson Chunga10370f2014-04-02 12:25:04 -0700427 RecentsTaskLoader.getInstance().getSystemServicesProxy()
428 .moveTaskToFront(task.key.id, opts);
Winson Chung4be04452014-03-24 17:22:30 -0700429 } else {
Winson Chung5393dff2014-05-08 14:25:43 -0700430 // Launch the activity anew with the desired animation
Winson Chungc6a16232014-04-01 14:04:48 -0700431 Intent i = new Intent(task.key.baseIntent);
Winson Chung4be04452014-03-24 17:22:30 -0700432 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
433 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
434 | Intent.FLAG_ACTIVITY_NEW_TASK);
435 try {
Winson Chung67369052014-04-07 17:35:48 -0700436 UserHandle taskUser = new UserHandle(task.userId);
Winson Chung4be04452014-03-24 17:22:30 -0700437 if (opts != null) {
Winson Chung67369052014-04-07 17:35:48 -0700438 getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
Winson Chung4be04452014-03-24 17:22:30 -0700439 } else {
Winson Chung67369052014-04-07 17:35:48 -0700440 getContext().startActivityAsUser(i, taskUser);
Winson Chung4be04452014-03-24 17:22:30 -0700441 }
442 } catch (ActivityNotFoundException anfe) {
443 Console.logError(getContext(), "Could not start Activity");
444 }
Winson Chung5393dff2014-05-08 14:25:43 -0700445
446 // And clean up the old task
447 onTaskRemoved(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800448 }
Winson Chungbd912972014-03-18 14:36:35 -0700449
Winson Chungd42a6cf2014-06-03 16:24:04 -0700450 if (Console.Enabled) {
451 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
452 Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
453 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800454 }
455 };
456
Winson Chungd42a6cf2014-06-03 16:24:04 -0700457 if (Console.Enabled) {
458 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
459 Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
460 }
Winson Chungbd912972014-03-18 14:36:35 -0700461
Winson Chung303e1ff2014-03-07 15:06:19 -0800462 // Launch the app right away if there is no task view, otherwise, animate the icon out first
Winson Chung814086d2014-05-07 15:01:14 -0700463 if (tv == null) {
Winson Chung47c4c692014-03-17 10:17:11 -0700464 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800465 } else {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700466 tv.animateOnLaunchingTask(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800467 }
468 }
Winson Chung9f9679d2014-04-11 16:49:09 -0700469
470 @Override
471 public void onTaskAppInfoLaunched(Task t) {
472 // Create a new task stack with the application info details activity
473 Intent baseIntent = t.key.baseIntent;
474 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
475 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
476 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
477 TaskStackBuilder.create(getContext())
478 .addNextIntentWithParentStack(intent).startActivities();
479 }
Winson Chung9f49df92014-05-07 18:08:34 -0700480
Winson Chung5393dff2014-05-08 14:25:43 -0700481 @Override
482 public void onTaskRemoved(Task t) {
483 // Remove any stored data from the loader. We currently don't bother notifying the views
484 // that the data has been unloaded because at the point we call onTaskRemoved(), the views
485 // either don't need to be updated, or have already been removed.
486 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
487 loader.deleteTaskData(t, false);
488
489 // Remove the old task from activity manager
490 int flags = t.key.baseIntent.getFlags();
491 boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
492 Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
493 RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
494 isDocument);
495 }
496
Winson Chung9f49df92014-05-07 18:08:34 -0700497 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
498
499 @Override
500 public void onComponentRemoved(Set<ComponentName> cns) {
501 // Propagate this event down to each task stack view
502 int childCount = getChildCount();
503 for (int i = 0; i < childCount; i++) {
504 View child = getChildAt(i);
505 if (child instanceof TaskStackView) {
506 TaskStackView stackView = (TaskStackView) child;
507 stackView.onComponentRemoved(cns);
508 }
509 }
510 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800511}