blob: 8afc5b9f8bbec67140b7c652abbebd1a5c77c401 [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 Chung8e548f72014-06-24 14:40:53 -070031import android.util.AttributeSet;
Winson Chungecd9b302014-04-16 17:07:18 -070032import android.view.LayoutInflater;
Winson Chung303e1ff2014-03-07 15:06:19 -080033import android.view.View;
Winson Chung653f70c22014-05-19 14:49:42 -070034import android.view.WindowInsets;
Winson Chung303e1ff2014-03-07 15:06:19 -080035import android.widget.FrameLayout;
36import com.android.systemui.recents.Console;
37import com.android.systemui.recents.Constants;
38import com.android.systemui.recents.RecentsConfiguration;
Winson Chung9f49df92014-05-07 18:08:34 -070039import com.android.systemui.recents.RecentsPackageMonitor;
Winson Chunga10370f2014-04-02 12:25:04 -070040import com.android.systemui.recents.RecentsTaskLoader;
Winson Chung303e1ff2014-03-07 15:06:19 -080041import com.android.systemui.recents.model.SpaceNode;
42import com.android.systemui.recents.model.Task;
43import com.android.systemui.recents.model.TaskStack;
44
45import java.util.ArrayList;
Winson Chung9f49df92014-05-07 18:08:34 -070046import java.util.Set;
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 Chungc9567c02014-06-16 20:25:51 -070057 public void onTaskLaunching();
Winson Chungcdbbb7e2014-06-24 12:11:49 -070058 public void onExitToHomeAnimationTriggered();
Winson Chung47c4c692014-03-17 10:17:11 -070059 }
60
Winson Chungd42a6cf2014-06-03 16:24:04 -070061 RecentsConfiguration mConfig;
62 LayoutInflater mInflater;
63
Winson Chung303e1ff2014-03-07 15:06:19 -080064 // The space partitioning root of this container
65 SpaceNode mBSP;
Winson Chungf7bca432014-04-30 17:11:13 -070066 // Whether there are any tasks
67 boolean mHasTasks;
Winson Chungecd9b302014-04-16 17:07:18 -070068 // Search bar view
69 View mSearchBar;
Winson Chung47c4c692014-03-17 10:17:11 -070070 // Recents view callbacks
71 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080072
73 public RecentsView(Context context) {
74 super(context);
Winson Chung8e548f72014-06-24 14:40:53 -070075 }
76
77 public RecentsView(Context context, AttributeSet attrs) {
78 this(context, attrs, 0);
79 }
80
81 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
82 this(context, attrs, defStyleAttr, 0);
83 }
84
85 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
86 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -070087 mConfig = RecentsConfiguration.getInstance();
Winson Chungecd9b302014-04-16 17:07:18 -070088 mInflater = LayoutInflater.from(context);
Winson Chung303e1ff2014-03-07 15:06:19 -080089 }
90
Winson Chung47c4c692014-03-17 10:17:11 -070091 /** Sets the callbacks */
92 public void setCallbacks(RecentsViewCallbacks cb) {
93 mCb = cb;
94 }
95
Winson Chung303e1ff2014-03-07 15:06:19 -080096 /** Set/get the bsp root node */
97 public void setBSP(SpaceNode n) {
98 mBSP = n;
99
Winson Chung04dfe0d2014-03-14 14:06:29 -0700100 // Create and add all the stacks for this partition of space.
Winson Chungf7bca432014-04-30 17:11:13 -0700101 mHasTasks = false;
Winson Chung303e1ff2014-03-07 15:06:19 -0800102 removeAllViews();
103 ArrayList<TaskStack> stacks = mBSP.getStacks();
104 for (TaskStack stack : stacks) {
105 TaskStackView stackView = new TaskStackView(getContext(), stack);
106 stackView.setCallbacks(this);
107 addView(stackView);
Winson Chungf7bca432014-04-30 17:11:13 -0700108 mHasTasks |= (stack.getTaskCount() > 0);
Winson Chung303e1ff2014-03-07 15:06:19 -0800109 }
110 }
111
Winson Chung1e8d71b2014-05-16 17:05:22 -0700112 /** Launches the focused task from the first stack if possible */
113 public boolean launchFocusedTask() {
114 // Get the first stack view
115 int childCount = getChildCount();
116 for (int i = 0; i < childCount; i++) {
117 View child = getChildAt(i);
118 if (child instanceof TaskStackView) {
119 TaskStackView stackView = (TaskStackView) child;
120 TaskStack stack = stackView.mStack;
121 // Iterate the stack views and try and find the focused task
122 int taskCount = stackView.getChildCount();
123 for (int j = 0; j < taskCount; j++) {
124 TaskView tv = (TaskView) stackView.getChildAt(j);
125 Task task = tv.getTask();
126 if (tv.isFocusedTask()) {
Winson Chung10f81392014-05-20 16:21:31 -0700127 if (Console.Enabled) {
128 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
129 "Found focused Task");
130 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700131 onTaskLaunched(stackView, tv, stack, task);
132 return true;
133 }
134 }
135 }
136 }
Winson Chung10f81392014-05-20 16:21:31 -0700137 if (Console.Enabled) {
138 Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
139 "No Tasks focused");
140 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700141 return false;
142 }
143
Winson Chung303e1ff2014-03-07 15:06:19 -0800144 /** Launches the first task from the first stack if possible */
145 public boolean launchFirstTask() {
Winson Chung47c4c692014-03-17 10:17:11 -0700146 // Get the first stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800147 int childCount = getChildCount();
148 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700149 View child = getChildAt(i);
150 if (child instanceof TaskStackView) {
151 TaskStackView stackView = (TaskStackView) child;
152 TaskStack stack = stackView.mStack;
153 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -0700154
Winson Chungecd9b302014-04-16 17:07:18 -0700155 // Get the first task in the stack
156 if (!tasks.isEmpty()) {
157 Task task = tasks.get(tasks.size() - 1);
158 TaskView tv = null;
Winson Chung47c4c692014-03-17 10:17:11 -0700159
Winson Chungecd9b302014-04-16 17:07:18 -0700160 // Try and use the first child task view as the source of the launch animation
161 if (stackView.getChildCount() > 0) {
162 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
163 if (stv.getTask() == task) {
164 tv = stv;
165 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800166 }
Winson Chungecd9b302014-04-16 17:07:18 -0700167 onTaskLaunched(stackView, tv, stack, task);
168 return true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800169 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800170 }
171 }
172 return false;
173 }
174
Winson Chung24cf1522014-05-29 12:03:33 -0700175 /** Requests all task stacks to start their enter-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700176 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung24cf1522014-05-29 12:03:33 -0700177 int childCount = getChildCount();
178 for (int i = 0; i < childCount; i++) {
179 View child = getChildAt(i);
180 if (child instanceof TaskStackView) {
181 TaskStackView stackView = (TaskStackView) child;
Winson Chung969f5862014-06-16 17:08:24 -0700182 stackView.startEnterRecentsAnimation(ctx);
Winson Chung24cf1522014-05-29 12:03:33 -0700183 }
184 }
185 }
186
Winson Chungd42a6cf2014-06-03 16:24:04 -0700187 /** Requests all task stacks to start their exit-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700188 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700189 // Handle the case when there are no views by incrementing and decrementing after all
190 // animations are started.
191 ctx.postAnimationTrigger.increment();
192
193 if (Constants.DebugFlags.App.EnableHomeTransition) {
194 int childCount = getChildCount();
195 for (int i = 0; i < childCount; i++) {
196 View child = getChildAt(i);
197 if (child instanceof TaskStackView) {
198 TaskStackView stackView = (TaskStackView) child;
Winson Chung969f5862014-06-16 17:08:24 -0700199 stackView.startExitToHomeAnimation(ctx);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700200 }
201 }
202 }
203
204 // Handle the case when there are no views by incrementing and decrementing after all
205 // animations are started.
206 ctx.postAnimationTrigger.decrement();
Winson Chung969f5862014-06-16 17:08:24 -0700207
208 // Notify of the exit animation
Winson Chungcdbbb7e2014-06-24 12:11:49 -0700209 mCb.onExitToHomeAnimationTriggered();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700210 }
211
Winson Chungf7bca432014-04-30 17:11:13 -0700212 /** Adds the search bar */
213 public void setSearchBar(View searchBar) {
214 // Create the search bar (and hide it if we have no recent tasks)
Winson Chung814086d2014-05-07 15:01:14 -0700215 if (Constants.DebugFlags.App.EnableSearchLayout) {
Winson Chungf7bca432014-04-30 17:11:13 -0700216 // Remove the previous search bar if one exists
217 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
218 removeView(mSearchBar);
Winson Chungecd9b302014-04-16 17:07:18 -0700219 }
Winson Chungf7bca432014-04-30 17:11:13 -0700220 // Add the new search bar
221 if (searchBar != null) {
222 mSearchBar = searchBar;
223 mSearchBar.setVisibility(mHasTasks ? View.VISIBLE : View.GONE);
224 addView(mSearchBar);
225
Winson Chung10f81392014-05-20 16:21:31 -0700226 if (Console.Enabled) {
227 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
228 "" + (mSearchBar.getVisibility() == View.VISIBLE),
229 Console.AnsiBlue);
230 }
Winson Chungf7bca432014-04-30 17:11:13 -0700231 }
232 }
Winson Chungecd9b302014-04-16 17:07:18 -0700233 }
234
Winson Chungf7bca432014-04-30 17:11:13 -0700235 /**
236 * This is called with the full size of the window since we are handling our own insets.
237 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800238 @Override
239 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
240 int width = MeasureSpec.getSize(widthMeasureSpec);
Winson Chung67369052014-04-07 17:35:48 -0700241 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800242 int height = MeasureSpec.getSize(heightMeasureSpec);
243 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700244
Winson Chung10f81392014-05-20 16:21:31 -0700245 if (Console.Enabled) {
246 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]",
247 "width: " + width + " height: " + height, Console.AnsiGreen);
248 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
249 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
250 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800251
Winson Chungf7bca432014-04-30 17:11:13 -0700252 // Get the search bar bounds and measure the search bar layout
Winson Chungecd9b302014-04-16 17:07:18 -0700253 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700254 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700255 mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
Winson Chungf7bca432014-04-30 17:11:13 -0700256 mSearchBar.measure(
257 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
258 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
Winson Chungecd9b302014-04-16 17:07:18 -0700259 }
260
Winson Chungf7bca432014-04-30 17:11:13 -0700261 // We give the full width of the space, not including the right nav bar insets in landscape,
262 // to the stack view, since we want the tasks to render under the search bar in landscape.
263 // In addition, we give it the full height, not including the top inset or search bar space,
264 // since we want the tasks to render under the navigation buttons in portrait.
265 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700266 mConfig.getTaskStackBounds(width, height, taskStackBounds);
267 int childWidth = width - mConfig.systemInsets.right;
268 int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800269
Winson Chungf7bca432014-04-30 17:11:13 -0700270 // Measure each TaskStackView
Winson Chung303e1ff2014-03-07 15:06:19 -0800271 int childCount = getChildCount();
272 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700273 View child = getChildAt(i);
274 if (child instanceof TaskStackView && child.getVisibility() != GONE) {
Winson Chung67369052014-04-07 17:35:48 -0700275 child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
Winson Chung303e1ff2014-03-07 15:06:19 -0800276 MeasureSpec.makeMeasureSpec(childHeight, heightMode));
277 }
278 }
279
280 setMeasuredDimension(width, height);
281 }
282
Winson Chungf7bca432014-04-30 17:11:13 -0700283 /**
284 * This is called with the full size of the window since we are handling our own insets.
285 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800286 @Override
287 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung10f81392014-05-20 16:21:31 -0700288 if (Console.Enabled) {
289 Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]",
290 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
291 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
292 Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout");
293 }
Winson Chungbd912972014-03-18 14:36:35 -0700294
Winson Chungf7bca432014-04-30 17:11:13 -0700295 // Get the search bar bounds so that we lay it out
Winson Chungecd9b302014-04-16 17:07:18 -0700296 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700297 Rect searchBarSpaceBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700298 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
299 mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
300 mConfig.systemInsets.top + searchBarSpaceBounds.top,
301 mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
302 mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
Winson Chungecd9b302014-04-16 17:07:18 -0700303 }
304
Winson Chungf7bca432014-04-30 17:11:13 -0700305 // We offset the stack view by the left inset (if any), but lay it out under the search bar.
306 // In addition, we offset our stack views by the top inset and search bar height, but not
307 // the bottom insets because we want it to render under the navigation buttons.
308 Rect taskStackBounds = new Rect();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700309 mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
310 left += mConfig.systemInsets.left;
311 top += mConfig.systemInsets.top + taskStackBounds.top;
Winson Chung303e1ff2014-03-07 15:06:19 -0800312
313 // Layout each child
314 // XXX: Based on the space node for that task view
315 int childCount = getChildCount();
316 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700317 View child = getChildAt(i);
318 if (child instanceof TaskStackView && child.getVisibility() != GONE) {
Winson Chungf7bca432014-04-30 17:11:13 -0700319 TaskStackView tsv = (TaskStackView) child;
320 child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800321 }
322 }
323 }
324
Winson Chunga26fb782014-06-12 17:52:39 -0700325 /** Notifies each task view of the user interaction. */
326 public void onUserInteraction() {
327 // Get the first stack view
328 TaskStackView stackView = null;
329 int childCount = getChildCount();
330 for (int i = 0; i < childCount; i++) {
331 View child = getChildAt(i);
332 if (child instanceof TaskStackView) {
333 stackView = (TaskStackView) child;
334 stackView.onUserInteraction();
335 }
336 }
337 }
338
Winson Chung1e8d71b2014-05-16 17:05:22 -0700339 /** Focuses the next task in the first stack view */
340 public void focusNextTask(boolean forward) {
341 // Get the first stack view
342 TaskStackView stackView = null;
343 int childCount = getChildCount();
344 for (int i = 0; i < childCount; i++) {
345 View child = getChildAt(i);
346 if (child instanceof TaskStackView) {
347 stackView = (TaskStackView) child;
348 break;
349 }
350 }
351
352 if (stackView != null) {
353 stackView.focusNextTask(forward);
354 }
355 }
356
Winson Chung303e1ff2014-03-07 15:06:19 -0800357 @Override
358 protected void dispatchDraw(Canvas canvas) {
Winson Chung10f81392014-05-20 16:21:31 -0700359 if (Console.Enabled) {
360 Console.log(Constants.Log.UI.Draw, "[RecentsView|dispatchDraw]", "",
361 Console.AnsiPurple);
362 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800363 super.dispatchDraw(canvas);
364 }
365
366 @Override
Winson Chung653f70c22014-05-19 14:49:42 -0700367 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Winson Chung10f81392014-05-20 16:21:31 -0700368 if (Console.Enabled) {
369 Console.log(Constants.Log.UI.MeasureAndLayout,
370 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
371 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800372
373 // Update the configuration with the latest system insets and trigger a relayout
Winson Chungd42a6cf2014-06-03 16:24:04 -0700374 mConfig.updateSystemInsets(insets.getSystemWindowInsets());
Winson Chung303e1ff2014-03-07 15:06:19 -0800375 requestLayout();
376
Winson Chung653f70c22014-05-19 14:49:42 -0700377 return insets.consumeSystemWindowInsets(false, false, false, true);
Winson Chung303e1ff2014-03-07 15:06:19 -0800378 }
379
380 /** Unfilters any filtered stacks */
381 public boolean unfilterFilteredStacks() {
382 if (mBSP != null) {
383 // Check if there are any filtered stacks and unfilter them before we back out of Recents
384 boolean stacksUnfiltered = false;
385 ArrayList<TaskStack> stacks = mBSP.getStacks();
386 for (TaskStack stack : stacks) {
387 if (stack.hasFilteredTasks()) {
388 stack.unfilterTasks();
389 stacksUnfiltered = true;
390 }
391 }
392 return stacksUnfiltered;
393 }
394 return false;
395 }
396
Winson Chung47c4c692014-03-17 10:17:11 -0700397 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800398
399 @Override
400 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv,
401 final TaskStack stack, final Task task) {
Winson Chung47c4c692014-03-17 10:17:11 -0700402 // Notify any callbacks of the launching of a new task
403 if (mCb != null) {
Winson Chungc9567c02014-06-16 20:25:51 -0700404 mCb.onTaskLaunching();
Winson Chung47c4c692014-03-17 10:17:11 -0700405 }
406
Winson Chunge0e45bc2014-06-17 17:56:17 -0700407 // Upfront the processing of the thumbnail
408 TaskViewTransform transform;
409 View sourceView = tv;
410 int offsetX = 0;
411 int offsetY = 0;
412 int stackScroll = stackView.getStackScroll();
413 if (tv == null) {
414 // If there is no actual task view, then use the stack view as the source view
415 // and then offset to the expected transform rect, but bound this to just
416 // outside the display rect (to ensure we don't animate from too far away)
417 sourceView = stackView;
418 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
419 offsetX = transform.rect.left;
420 offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
421 } else {
422 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
423 }
424
425 // Compute the thumbnail to scale up from
426 ActivityOptions opts = null;
427 int thumbnailWidth = transform.rect.width();
428 int thumbnailHeight = transform.rect.height();
429 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
430 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
431 // Resize the thumbnail to the size of the view that we are animating from
432 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
433 Bitmap.Config.ARGB_8888);
434 Canvas c = new Canvas(b);
435 c.drawBitmap(task.thumbnail,
436 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
437 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
438 c.setBitmap(null);
439 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
440 b, offsetX, offsetY);
441 }
442
443 final ActivityOptions launchOpts = opts;
Winson Chung303e1ff2014-03-07 15:06:19 -0800444 final Runnable launchRunnable = new Runnable() {
445 @Override
446 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700447 if (Console.Enabled) {
448 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
449 Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
450 }
451
Winson Chung4be04452014-03-24 17:22:30 -0700452 if (task.isActive) {
453 // Bring an active task to the foreground
Winson Chunga10370f2014-04-02 12:25:04 -0700454 RecentsTaskLoader.getInstance().getSystemServicesProxy()
Winson Chunge0e45bc2014-06-17 17:56:17 -0700455 .moveTaskToFront(task.key.id, launchOpts);
Winson Chung4be04452014-03-24 17:22:30 -0700456 } else {
Winson Chung5393dff2014-05-08 14:25:43 -0700457 // Launch the activity anew with the desired animation
Winson Chungc6a16232014-04-01 14:04:48 -0700458 Intent i = new Intent(task.key.baseIntent);
Winson Chung4be04452014-03-24 17:22:30 -0700459 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
460 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
461 | Intent.FLAG_ACTIVITY_NEW_TASK);
462 try {
Winson Chung67369052014-04-07 17:35:48 -0700463 UserHandle taskUser = new UserHandle(task.userId);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700464 if (launchOpts != null) {
465 getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
Winson Chung4be04452014-03-24 17:22:30 -0700466 } else {
Winson Chung67369052014-04-07 17:35:48 -0700467 getContext().startActivityAsUser(i, taskUser);
Winson Chung4be04452014-03-24 17:22:30 -0700468 }
469 } catch (ActivityNotFoundException anfe) {
470 Console.logError(getContext(), "Could not start Activity");
471 }
Winson Chung5393dff2014-05-08 14:25:43 -0700472
473 // And clean up the old task
474 onTaskRemoved(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800475 }
Winson Chungbd912972014-03-18 14:36:35 -0700476
Winson Chungd42a6cf2014-06-03 16:24:04 -0700477 if (Console.Enabled) {
478 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
479 Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
480 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800481 }
482 };
483
Winson Chungd42a6cf2014-06-03 16:24:04 -0700484 if (Console.Enabled) {
485 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
486 Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
487 }
Winson Chungbd912972014-03-18 14:36:35 -0700488
Winson Chung303e1ff2014-03-07 15:06:19 -0800489 // Launch the app right away if there is no task view, otherwise, animate the icon out first
Winson Chung814086d2014-05-07 15:01:14 -0700490 if (tv == null) {
Winson Chung47c4c692014-03-17 10:17:11 -0700491 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800492 } else {
Winson Chung969f5862014-06-16 17:08:24 -0700493 stackView.animateOnLaunchingTask(tv, launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800494 }
495 }
Winson Chung9f9679d2014-04-11 16:49:09 -0700496
497 @Override
498 public void onTaskAppInfoLaunched(Task t) {
499 // Create a new task stack with the application info details activity
500 Intent baseIntent = t.key.baseIntent;
501 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
502 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
503 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
504 TaskStackBuilder.create(getContext())
505 .addNextIntentWithParentStack(intent).startActivities();
506 }
Winson Chung9f49df92014-05-07 18:08:34 -0700507
Winson Chung5393dff2014-05-08 14:25:43 -0700508 @Override
509 public void onTaskRemoved(Task t) {
510 // Remove any stored data from the loader. We currently don't bother notifying the views
511 // that the data has been unloaded because at the point we call onTaskRemoved(), the views
512 // either don't need to be updated, or have already been removed.
513 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
514 loader.deleteTaskData(t, false);
515
516 // Remove the old task from activity manager
517 int flags = t.key.baseIntent.getFlags();
518 boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
519 Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
520 RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
521 isDocument);
522 }
523
Winson Chung8e548f72014-06-24 14:40:53 -0700524 @Override
525 public void onTaskStackFilterTriggered() {
526 // Hide the search bar
527 if (mSearchBar != null) {
528 mSearchBar.animate()
529 .alpha(0f)
530 .setStartDelay(0)
531 .setInterpolator(mConfig.fastOutSlowInInterpolator)
532 .setDuration(mConfig.filteringCurrentViewsAnimDuration)
533 .withLayer()
534 .start();
535 }
536 }
537
538 @Override
539 public void onTaskStackUnfilterTriggered() {
540 // Show the search bar
541 if (mSearchBar != null) {
542 mSearchBar.animate()
543 .alpha(1f)
544 .setStartDelay(0)
545 .setInterpolator(mConfig.fastOutSlowInInterpolator)
546 .setDuration(mConfig.filteringNewViewsAnimDuration)
547 .withLayer()
548 .start();
549 }
550 }
551
Winson Chung9f49df92014-05-07 18:08:34 -0700552 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
553
554 @Override
555 public void onComponentRemoved(Set<ComponentName> cns) {
556 // Propagate this event down to each task stack view
557 int childCount = getChildCount();
558 for (int i = 0; i < childCount; i++) {
559 View child = getChildAt(i);
560 if (child instanceof TaskStackView) {
561 TaskStackView stackView = (TaskStackView) child;
562 stackView.onComponentRemoved(cns);
563 }
564 }
565 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800566}