blob: 1a32b81c767e7b37d4c2e7992acaaa9f3f0db199 [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 Chung9f9679d2014-04-11 16:49:09 -070029import android.provider.Settings;
Winson Chung8e548f72014-06-24 14:40:53 -070030import android.util.AttributeSet;
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;
Winson Chung303e1ff2014-03-07 15:06:19 -080035import com.android.systemui.recents.Constants;
36import com.android.systemui.recents.RecentsConfiguration;
Winson Chung1f24c7e2014-07-11 17:06:48 -070037import com.android.systemui.recents.misc.Console;
38import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chungff88d7b2014-07-17 12:30:07 -070039import com.android.systemui.recents.misc.Utilities;
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 Chung82c8c5e2014-07-14 17:31:41 -070046import java.util.HashSet;
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();
58 public void onAllTaskViewsDismissed();
Winson Chungcdbbb7e2014-06-24 12:11:49 -070059 public void onExitToHomeAnimationTriggered();
Winson Chung47c4c692014-03-17 10:17:11 -070060 }
61
Winson Chungd42a6cf2014-06-03 16:24:04 -070062 RecentsConfiguration mConfig;
63 LayoutInflater mInflater;
Winson Chungdcfa7972014-07-22 12:27:13 -070064 DebugOverlayView mDebugOverlay;
Winson Chungd42a6cf2014-06-03 16:24:04 -070065
Winson Chungdcfa7972014-07-22 12:27:13 -070066 ArrayList<TaskStack> mStacks;
Winson Chungecd9b302014-04-16 17:07:18 -070067 View mSearchBar;
Winson Chung47c4c692014-03-17 10:17:11 -070068 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080069
70 public RecentsView(Context context) {
71 super(context);
Winson Chung8e548f72014-06-24 14:40:53 -070072 }
73
74 public RecentsView(Context context, AttributeSet attrs) {
75 this(context, attrs, 0);
76 }
77
78 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
79 this(context, attrs, defStyleAttr, 0);
80 }
81
82 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
83 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -070084 mConfig = RecentsConfiguration.getInstance();
Winson Chungecd9b302014-04-16 17:07:18 -070085 mInflater = LayoutInflater.from(context);
Winson Chung303e1ff2014-03-07 15:06:19 -080086 }
87
Winson Chung47c4c692014-03-17 10:17:11 -070088 /** Sets the callbacks */
89 public void setCallbacks(RecentsViewCallbacks cb) {
90 mCb = cb;
91 }
92
Winson Chungdcfa7972014-07-22 12:27:13 -070093 /** Sets the debug overlay */
94 public void setDebugOverlay(DebugOverlayView overlay) {
95 mDebugOverlay = overlay;
96 }
Winson Chung303e1ff2014-03-07 15:06:19 -080097
Winson Chungdcfa7972014-07-22 12:27:13 -070098 /** Set/get the bsp root node */
99 public void setTaskStacks(ArrayList<TaskStack> stacks) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700100 // Remove all TaskStackViews (but leave the search bar)
101 int childCount = getChildCount();
102 for (int i = childCount - 1; i >= 0; i--) {
103 View v = getChildAt(i);
104 if (v != mSearchBar) {
105 removeViewAt(i);
106 }
107 }
108
Winson Chung04dfe0d2014-03-14 14:06:29 -0700109 // Create and add all the stacks for this partition of space.
Winson Chungdcfa7972014-07-22 12:27:13 -0700110 mStacks = stacks;
111 int numStacks = mStacks.size();
112 for (int i = 0; i < numStacks; i++) {
113 TaskStack stack = mStacks.get(i);
Winson Chung303e1ff2014-03-07 15:06:19 -0800114 TaskStackView stackView = new TaskStackView(getContext(), stack);
115 stackView.setCallbacks(this);
Winson Chungdcfa7972014-07-22 12:27:13 -0700116 // Enable debug mode drawing
117 if (mConfig.debugModeEnabled) {
118 stackView.setDebugOverlay(mDebugOverlay);
119 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800120 addView(stackView);
121 }
122 }
123
Winson Chung19fc1172014-07-31 18:40:03 -0700124 /** Removes all the task stack views from this recents view. */
125 public void removeAllTaskStacks() {
126 int childCount = getChildCount();
127 for (int i = childCount - 1; i >= 0; i--) {
128 View child = getChildAt(i);
129 if (child != mSearchBar) {
130 removeViewAt(i);
131 }
132 }
133 }
134
Winson Chung1e8d71b2014-05-16 17:05:22 -0700135 /** Launches the focused task from the first stack if possible */
136 public boolean launchFocusedTask() {
137 // Get the first stack view
138 int childCount = getChildCount();
139 for (int i = 0; i < childCount; i++) {
140 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700141 if (child != mSearchBar) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700142 TaskStackView stackView = (TaskStackView) child;
143 TaskStack stack = stackView.mStack;
144 // Iterate the stack views and try and find the focused task
145 int taskCount = stackView.getChildCount();
146 for (int j = 0; j < taskCount; j++) {
147 TaskView tv = (TaskView) stackView.getChildAt(j);
148 Task task = tv.getTask();
149 if (tv.isFocusedTask()) {
Winson Chung1f24c7e2014-07-11 17:06:48 -0700150 onTaskViewClicked(stackView, tv, stack, task, false);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700151 return true;
152 }
153 }
154 }
155 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700156 return false;
157 }
158
Winson Chungdcfa7972014-07-22 12:27:13 -0700159 /** Launches the task that Recents was launched from, if possible */
160 public boolean launchPreviousTask() {
Winson Chung47c4c692014-03-17 10:17:11 -0700161 // Get the first stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800162 int childCount = getChildCount();
163 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700164 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700165 if (child != mSearchBar) {
Winson Chungecd9b302014-04-16 17:07:18 -0700166 TaskStackView stackView = (TaskStackView) child;
167 TaskStack stack = stackView.mStack;
168 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -0700169
Winson Chungdcfa7972014-07-22 12:27:13 -0700170 // Find the launch task in the stack
Winson Chungecd9b302014-04-16 17:07:18 -0700171 if (!tasks.isEmpty()) {
Winson Chungdcfa7972014-07-22 12:27:13 -0700172 int taskCount = tasks.size();
173 for (int j = 0; j < taskCount; j++) {
174 if (tasks.get(j).isLaunchTarget) {
175 Task task = tasks.get(j);
176 TaskView tv = stackView.getChildViewForTask(task);
177 onTaskViewClicked(stackView, tv, stack, task, false);
178 return true;
Winson Chungecd9b302014-04-16 17:07:18 -0700179 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800180 }
181 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800182 }
183 }
184 return false;
185 }
186
Winson Chung24cf1522014-05-29 12:03:33 -0700187 /** Requests all task stacks to start their enter-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700188 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung24cf1522014-05-29 12:03:33 -0700189 int childCount = getChildCount();
190 for (int i = 0; i < childCount; i++) {
191 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700192 if (child != mSearchBar) {
Winson Chung24cf1522014-05-29 12:03:33 -0700193 TaskStackView stackView = (TaskStackView) child;
Winson Chung969f5862014-06-16 17:08:24 -0700194 stackView.startEnterRecentsAnimation(ctx);
Winson Chung24cf1522014-05-29 12:03:33 -0700195 }
196 }
197 }
198
Winson Chungd42a6cf2014-06-03 16:24:04 -0700199 /** Requests all task stacks to start their exit-recents animation */
Winson Chung969f5862014-06-16 17:08:24 -0700200 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700201 int childCount = getChildCount();
202 for (int i = 0; i < childCount; i++) {
203 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700204 if (child != mSearchBar) {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700205 TaskStackView stackView = (TaskStackView) child;
206 stackView.startExitToHomeAnimation(ctx);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700207 }
208 }
209
Winson Chung969f5862014-06-16 17:08:24 -0700210 // Notify of the exit animation
Winson Chungcdbbb7e2014-06-24 12:11:49 -0700211 mCb.onExitToHomeAnimationTriggered();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700212 }
213
Winson Chungf7bca432014-04-30 17:11:13 -0700214 /** Adds the search bar */
215 public void setSearchBar(View searchBar) {
216 // Create the search bar (and hide it if we have no recent tasks)
Winson Chung814086d2014-05-07 15:01:14 -0700217 if (Constants.DebugFlags.App.EnableSearchLayout) {
Winson Chungf7bca432014-04-30 17:11:13 -0700218 // Remove the previous search bar if one exists
219 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
220 removeView(mSearchBar);
Winson Chungecd9b302014-04-16 17:07:18 -0700221 }
Winson Chungf7bca432014-04-30 17:11:13 -0700222 // Add the new search bar
223 if (searchBar != null) {
224 mSearchBar = searchBar;
Winson Chungf7bca432014-04-30 17:11:13 -0700225 addView(mSearchBar);
Winson Chungf7bca432014-04-30 17:11:13 -0700226 }
227 }
Winson Chungecd9b302014-04-16 17:07:18 -0700228 }
229
Winson Chung772b6b12014-07-03 15:54:02 -0700230 /** Returns whether there is currently a search bar */
231 public boolean hasSearchBar() {
232 return mSearchBar != null;
233 }
234
235 /** Sets the visibility of the search bar */
236 public void setSearchBarVisibility(int visibility) {
237 if (mSearchBar != null) {
238 mSearchBar.setVisibility(visibility);
Winson Chung012ef362014-07-31 18:36:25 -0700239 // Always bring the search bar to the top
240 mSearchBar.bringToFront();
Winson Chung772b6b12014-07-03 15:54:02 -0700241 }
242 }
243
Winson Chungf7bca432014-04-30 17:11:13 -0700244 /**
245 * This is called with the full size of the window since we are handling our own insets.
246 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800247 @Override
248 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
249 int width = MeasureSpec.getSize(widthMeasureSpec);
250 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700251
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 Chungdcfa7972014-07-22 12:27:13 -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 Rect taskStackBounds = new Rect();
Winson Chungdcfa7972014-07-22 12:27:13 -0700262 mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
263 mConfig.systemInsets.right, taskStackBounds);
Winson Chung303e1ff2014-03-07 15:06:19 -0800264
Winson Chungdcfa7972014-07-22 12:27:13 -0700265 // Measure each TaskStackView with the full width and height of the window since the
266 // transition view is a child of that stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800267 int childCount = getChildCount();
268 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700269 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700270 if (child != mSearchBar && child.getVisibility() != GONE) {
Winson Chungdcfa7972014-07-22 12:27:13 -0700271 TaskStackView tsv = (TaskStackView) child;
272 // Set the insets to be the top/left inset + search bounds
273 tsv.setStackInsetRect(taskStackBounds);
274 tsv.measure(widthMeasureSpec, heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800275 }
276 }
277
278 setMeasuredDimension(width, height);
279 }
280
Winson Chungf7bca432014-04-30 17:11:13 -0700281 /**
282 * This is called with the full size of the window since we are handling our own insets.
283 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800284 @Override
285 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chungf7bca432014-04-30 17:11:13 -0700286 // Get the search bar bounds so that we lay it out
Winson Chungecd9b302014-04-16 17:07:18 -0700287 if (mSearchBar != null) {
Winson Chungf7bca432014-04-30 17:11:13 -0700288 Rect searchBarSpaceBounds = new Rect();
Winson Chungdcfa7972014-07-22 12:27:13 -0700289 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
290 mConfig.systemInsets.top, searchBarSpaceBounds);
291 mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
292 searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
Winson Chungecd9b302014-04-16 17:07:18 -0700293 }
294
Winson Chungdcfa7972014-07-22 12:27:13 -0700295 // Layout each TaskStackView with the full width and height of the window since the
296 // transition view is a child of that stack view
Winson Chung303e1ff2014-03-07 15:06:19 -0800297 int childCount = getChildCount();
298 for (int i = 0; i < childCount; i++) {
Winson Chungecd9b302014-04-16 17:07:18 -0700299 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700300 if (child != mSearchBar && child.getVisibility() != GONE) {
Winson Chungdcfa7972014-07-22 12:27:13 -0700301 child.layout(left, top, left + child.getMeasuredWidth(),
302 top + child.getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800303 }
304 }
305 }
306
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700307 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700308 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700309 // Update the configuration with the latest system insets and trigger a relayout
310 mConfig.updateSystemInsets(insets.getSystemWindowInsets());
311 requestLayout();
Winson Chungdcfa7972014-07-22 12:27:13 -0700312 return insets.consumeSystemWindowInsets();
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700313 }
314
Winson Chunga26fb782014-06-12 17:52:39 -0700315 /** Notifies each task view of the user interaction. */
316 public void onUserInteraction() {
317 // Get the first stack view
318 TaskStackView stackView = null;
319 int childCount = getChildCount();
320 for (int i = 0; i < childCount; i++) {
321 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700322 if (child != mSearchBar) {
Winson Chunga26fb782014-06-12 17:52:39 -0700323 stackView = (TaskStackView) child;
324 stackView.onUserInteraction();
325 }
326 }
327 }
328
Winson Chung1e8d71b2014-05-16 17:05:22 -0700329 /** Focuses the next task in the first stack view */
330 public void focusNextTask(boolean forward) {
331 // Get the first stack view
332 TaskStackView stackView = null;
333 int childCount = getChildCount();
334 for (int i = 0; i < childCount; i++) {
335 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700336 if (child != mSearchBar) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700337 stackView = (TaskStackView) child;
338 break;
339 }
340 }
341
342 if (stackView != null) {
343 stackView.focusNextTask(forward);
344 }
345 }
346
Winson Chung303e1ff2014-03-07 15:06:19 -0800347 /** Unfilters any filtered stacks */
348 public boolean unfilterFilteredStacks() {
Winson Chungdcfa7972014-07-22 12:27:13 -0700349 if (mStacks != null) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800350 // Check if there are any filtered stacks and unfilter them before we back out of Recents
351 boolean stacksUnfiltered = false;
Winson Chungdcfa7972014-07-22 12:27:13 -0700352 int numStacks = mStacks.size();
353 for (int i = 0; i < numStacks; i++) {
354 TaskStack stack = mStacks.get(i);
Winson Chung303e1ff2014-03-07 15:06:19 -0800355 if (stack.hasFilteredTasks()) {
356 stack.unfilterTasks();
357 stacksUnfiltered = true;
358 }
359 }
360 return stacksUnfiltered;
361 }
362 return false;
363 }
364
Winson Chung47c4c692014-03-17 10:17:11 -0700365 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800366
367 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700368 public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
Winson Chung1f24c7e2014-07-11 17:06:48 -0700369 final TaskStack stack, final Task task, final boolean lockToTask) {
Winson Chung47c4c692014-03-17 10:17:11 -0700370 // Notify any callbacks of the launching of a new task
371 if (mCb != null) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700372 mCb.onTaskViewClicked();
Winson Chung47c4c692014-03-17 10:17:11 -0700373 }
374
Winson Chunge0e45bc2014-06-17 17:56:17 -0700375 // Upfront the processing of the thumbnail
Winson Chungffa2ec62014-07-03 15:54:42 -0700376 TaskViewTransform transform = new TaskViewTransform();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700377 View sourceView = tv;
378 int offsetX = 0;
379 int offsetY = 0;
Winson Chung012ef362014-07-31 18:36:25 -0700380 float stackScroll = stackView.getScroller().getStackScroll();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700381 if (tv == null) {
382 // If there is no actual task view, then use the stack view as the source view
383 // and then offset to the expected transform rect, but bound this to just
384 // outside the display rect (to ensure we don't animate from too far away)
385 sourceView = stackView;
Winson Chung012ef362014-07-31 18:36:25 -0700386 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700387 offsetX = transform.rect.left;
388 offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
389 } else {
Winson Chung012ef362014-07-31 18:36:25 -0700390 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700391 }
392
393 // Compute the thumbnail to scale up from
Winson Chung1f24c7e2014-07-11 17:06:48 -0700394 final SystemServicesProxy ssp =
395 RecentsTaskLoader.getInstance().getSystemServicesProxy();
Winson Chunge0e45bc2014-06-17 17:56:17 -0700396 ActivityOptions opts = null;
397 int thumbnailWidth = transform.rect.width();
398 int thumbnailHeight = transform.rect.height();
399 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
400 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
401 // Resize the thumbnail to the size of the view that we are animating from
402 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
403 Bitmap.Config.ARGB_8888);
404 Canvas c = new Canvas(b);
405 c.drawBitmap(task.thumbnail,
406 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
407 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
408 c.setBitmap(null);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700409 ActivityOptions.OnAnimationStartedListener animStartedListener = null;
410 if (lockToTask) {
411 animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
412 boolean mTriggered = false;
413 @Override
414 public void onAnimationStarted() {
415 if (!mTriggered) {
416 postDelayed(new Runnable() {
417 @Override
418 public void run() {
419 ssp.lockCurrentTask();
420 }
421 }, 350);
422 mTriggered = true;
423 }
424 }
425 };
426 }
Winson Chunge0e45bc2014-06-17 17:56:17 -0700427 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
Winson Chung1f24c7e2014-07-11 17:06:48 -0700428 b, offsetX, offsetY, animStartedListener);
Winson Chunge0e45bc2014-06-17 17:56:17 -0700429 }
430
431 final ActivityOptions launchOpts = opts;
Winson Chung303e1ff2014-03-07 15:06:19 -0800432 final Runnable launchRunnable = new Runnable() {
433 @Override
434 public void run() {
Winson Chung4be04452014-03-24 17:22:30 -0700435 if (task.isActive) {
436 // Bring an active task to the foreground
Craig Mautnerdc00cbe2014-07-20 17:48:47 -0700437 ssp.moveTaskToFront(task.key.id, launchOpts);
Winson Chung4be04452014-03-24 17:22:30 -0700438 } else {
Winson Chung5393dff2014-05-08 14:25:43 -0700439 // Launch the activity anew with the desired animation
Winson Chungc6a16232014-04-01 14:04:48 -0700440 Intent i = new Intent(task.key.baseIntent);
Winson Chungdc9b7e72014-07-16 11:25:22 -0700441 i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
442 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Winson Chungff88d7b2014-07-17 12:30:07 -0700443 if (!Utilities.isDocument(i)) {
Winson Chungdc9b7e72014-07-16 11:25:22 -0700444 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
445 }
Winson Chung4be04452014-03-24 17:22:30 -0700446 try {
Craig Mautnerdc00cbe2014-07-20 17:48:47 -0700447 ssp.startActivityFromRecents(task.key.id, launchOpts);
448 if (launchOpts == null && lockToTask) {
449 ssp.lockCurrentTask();
Winson Chung4be04452014-03-24 17:22:30 -0700450 }
451 } catch (ActivityNotFoundException anfe) {
452 Console.logError(getContext(), "Could not start Activity");
453 }
Winson Chung5393dff2014-05-08 14:25:43 -0700454
455 // And clean up the old task
Winson Chung7aceb9a2014-07-03 13:38:01 -0700456 onTaskViewDismissed(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800457 }
458 }
459 };
460
461 // Launch the app right away if there is no task view, otherwise, animate the icon out first
Winson Chung814086d2014-05-07 15:01:14 -0700462 if (tv == null) {
Winson Chung47c4c692014-03-17 10:17:11 -0700463 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800464 } else {
Winson Chung1907cd42014-07-23 18:20:13 -0700465 stackView.startLaunchTaskAnimation(tv, launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800466 }
467 }
Winson Chung9f9679d2014-04-11 16:49:09 -0700468
469 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700470 public void onTaskViewAppInfoClicked(Task t) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700471 // Create a new task stack with the application info details activity
472 Intent baseIntent = t.key.baseIntent;
473 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
474 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
475 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
476 TaskStackBuilder.create(getContext())
477 .addNextIntentWithParentStack(intent).startActivities();
478 }
Winson Chung9f49df92014-05-07 18:08:34 -0700479
Winson Chung5393dff2014-05-08 14:25:43 -0700480 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700481 public void onTaskViewDismissed(Task t) {
Winson Chung5393dff2014-05-08 14:25:43 -0700482 // Remove any stored data from the loader. We currently don't bother notifying the views
Winson Chung7aceb9a2014-07-03 13:38:01 -0700483 // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views
Winson Chung5393dff2014-05-08 14:25:43 -0700484 // either don't need to be updated, or have already been removed.
485 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
486 loader.deleteTaskData(t, false);
487
488 // Remove the old task from activity manager
Winson Chung5393dff2014-05-08 14:25:43 -0700489 RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
Winson Chungff88d7b2014-07-17 12:30:07 -0700490 Utilities.isDocument(t.key.baseIntent));
Winson Chung5393dff2014-05-08 14:25:43 -0700491 }
492
Winson Chung8e548f72014-06-24 14:40:53 -0700493 @Override
Winson Chung7aceb9a2014-07-03 13:38:01 -0700494 public void onAllTaskViewsDismissed() {
495 mCb.onAllTaskViewsDismissed();
Winson Chungd7b2cb12014-06-26 15:08:50 -0700496 }
497
498 @Override
Winson Chung8e548f72014-06-24 14:40:53 -0700499 public void onTaskStackFilterTriggered() {
500 // Hide the search bar
501 if (mSearchBar != null) {
502 mSearchBar.animate()
503 .alpha(0f)
504 .setStartDelay(0)
505 .setInterpolator(mConfig.fastOutSlowInInterpolator)
506 .setDuration(mConfig.filteringCurrentViewsAnimDuration)
507 .withLayer()
508 .start();
509 }
510 }
511
512 @Override
513 public void onTaskStackUnfilterTriggered() {
514 // Show the search bar
515 if (mSearchBar != null) {
516 mSearchBar.animate()
517 .alpha(1f)
518 .setStartDelay(0)
519 .setInterpolator(mConfig.fastOutSlowInInterpolator)
520 .setDuration(mConfig.filteringNewViewsAnimDuration)
521 .withLayer()
522 .start();
523 }
524 }
525
Winson Chung9f49df92014-05-07 18:08:34 -0700526 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
527
528 @Override
Winson Chung82c8c5e2014-07-14 17:31:41 -0700529 public void onComponentRemoved(HashSet<ComponentName> cns) {
Winson Chung9f49df92014-05-07 18:08:34 -0700530 // Propagate this event down to each task stack view
531 int childCount = getChildCount();
532 for (int i = 0; i < childCount; i++) {
533 View child = getChildAt(i);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700534 if (child != mSearchBar) {
Winson Chung9f49df92014-05-07 18:08:34 -0700535 TaskStackView stackView = (TaskStackView) child;
536 stackView.onComponentRemoved(cns);
537 }
538 }
539 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800540}