blob: 77b78f395f05a6b1ab98243e405c429115228d16 [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;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.Bitmap;
23import android.graphics.Canvas;
24import android.graphics.Rect;
25import android.os.UserHandle;
26import android.view.View;
27import android.widget.FrameLayout;
28import com.android.systemui.recents.Console;
29import com.android.systemui.recents.Constants;
30import com.android.systemui.recents.RecentsConfiguration;
31import com.android.systemui.recents.model.SpaceNode;
32import com.android.systemui.recents.model.Task;
33import com.android.systemui.recents.model.TaskStack;
34
35import java.util.ArrayList;
36
37
38/**
39 * This view is the the top level layout that contains TaskStacks (which are laid out according
40 * to their SpaceNode bounds.
41 */
Winson Chung04dfe0d2014-03-14 14:06:29 -070042public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
Winson Chung47c4c692014-03-17 10:17:11 -070043
44 /** The RecentsView callbacks */
45 public interface RecentsViewCallbacks {
46 public void onTaskLaunching();
47 }
48
Winson Chung303e1ff2014-03-07 15:06:19 -080049 // The space partitioning root of this container
50 SpaceNode mBSP;
Winson Chung47c4c692014-03-17 10:17:11 -070051 // Recents view callbacks
52 RecentsViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080053
54 public RecentsView(Context context) {
55 super(context);
56 setWillNotDraw(false);
57 }
58
Winson Chung47c4c692014-03-17 10:17:11 -070059 /** Sets the callbacks */
60 public void setCallbacks(RecentsViewCallbacks cb) {
61 mCb = cb;
62 }
63
Winson Chung303e1ff2014-03-07 15:06:19 -080064 /** Set/get the bsp root node */
65 public void setBSP(SpaceNode n) {
66 mBSP = n;
67
Winson Chung04dfe0d2014-03-14 14:06:29 -070068 // Create and add all the stacks for this partition of space.
Winson Chung303e1ff2014-03-07 15:06:19 -080069 removeAllViews();
70 ArrayList<TaskStack> stacks = mBSP.getStacks();
71 for (TaskStack stack : stacks) {
72 TaskStackView stackView = new TaskStackView(getContext(), stack);
73 stackView.setCallbacks(this);
74 addView(stackView);
75 }
76 }
77
78 /** Launches the first task from the first stack if possible */
79 public boolean launchFirstTask() {
Winson Chung47c4c692014-03-17 10:17:11 -070080 // Get the first stack view
Winson Chung303e1ff2014-03-07 15:06:19 -080081 int childCount = getChildCount();
82 for (int i = 0; i < childCount; i++) {
83 TaskStackView stackView = (TaskStackView) getChildAt(i);
84 TaskStack stack = stackView.mStack;
85 ArrayList<Task> tasks = stack.getTasks();
Winson Chung47c4c692014-03-17 10:17:11 -070086
87 // Get the first task in the stack
Winson Chung303e1ff2014-03-07 15:06:19 -080088 if (!tasks.isEmpty()) {
89 Task task = tasks.get(tasks.size() - 1);
90 TaskView tv = null;
Winson Chung47c4c692014-03-17 10:17:11 -070091
92 // Try and use the first child task view as the source of the launch animation
Winson Chung303e1ff2014-03-07 15:06:19 -080093 if (stackView.getChildCount() > 0) {
94 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
95 if (stv.getTask() == task) {
96 tv = stv;
97 }
98 }
99 onTaskLaunched(stackView, tv, stack, task);
100 return true;
101 }
102 }
103 return false;
104 }
105
106 @Override
107 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
108 int width = MeasureSpec.getSize(widthMeasureSpec);
109 int height = MeasureSpec.getSize(heightMeasureSpec);
110 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700111
Winson Chung71243902014-03-14 17:52:47 -0700112 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]",
113 "width: " + width + " height: " + height, Console.AnsiGreen);
Winson Chungbd912972014-03-18 14:36:35 -0700114 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
115 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
Winson Chung303e1ff2014-03-07 15:06:19 -0800116
117 // We measure our stack views sans the status bar. It will handle the nav bar itself.
118 RecentsConfiguration config = RecentsConfiguration.getInstance();
119 int childHeight = height - config.systemInsets.top;
120
121 // Measure each child
122 int childCount = getChildCount();
123 for (int i = 0; i < childCount; i++) {
124 final View child = getChildAt(i);
125 if (child.getVisibility() != GONE) {
126 child.measure(widthMeasureSpec,
127 MeasureSpec.makeMeasureSpec(childHeight, heightMode));
128 }
129 }
130
131 setMeasuredDimension(width, height);
132 }
133
134 @Override
135 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung71243902014-03-14 17:52:47 -0700136 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]",
137 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
Winson Chungbd912972014-03-18 14:36:35 -0700138 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
139 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
140
Winson Chung303e1ff2014-03-07 15:06:19 -0800141 // We offset our stack views by the status bar height. It will handle the nav bar itself.
142 RecentsConfiguration config = RecentsConfiguration.getInstance();
143 top += config.systemInsets.top;
144
145 // Layout each child
146 // XXX: Based on the space node for that task view
147 int childCount = getChildCount();
148 for (int i = 0; i < childCount; i++) {
149 final View child = getChildAt(i);
150 if (child.getVisibility() != GONE) {
151 final int width = child.getMeasuredWidth();
152 final int height = child.getMeasuredHeight();
153 child.layout(left, top, left + width, top + height);
154 }
155 }
156 }
157
158 @Override
159 protected void dispatchDraw(Canvas canvas) {
Winson Chung47c4c692014-03-17 10:17:11 -0700160 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "",
161 Console.AnsiPurple);
Winson Chung303e1ff2014-03-07 15:06:19 -0800162 super.dispatchDraw(canvas);
163 }
164
165 @Override
166 protected boolean fitSystemWindows(Rect insets) {
Winson Chung47c4c692014-03-17 10:17:11 -0700167 Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
168 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
Winson Chung303e1ff2014-03-07 15:06:19 -0800169
170 // Update the configuration with the latest system insets and trigger a relayout
171 RecentsConfiguration config = RecentsConfiguration.getInstance();
172 config.updateSystemInsets(insets);
173 requestLayout();
174
175 return true;
176 }
177
178 /** Unfilters any filtered stacks */
179 public boolean unfilterFilteredStacks() {
180 if (mBSP != null) {
181 // Check if there are any filtered stacks and unfilter them before we back out of Recents
182 boolean stacksUnfiltered = false;
183 ArrayList<TaskStack> stacks = mBSP.getStacks();
184 for (TaskStack stack : stacks) {
185 if (stack.hasFilteredTasks()) {
186 stack.unfilterTasks();
187 stacksUnfiltered = true;
188 }
189 }
190 return stacksUnfiltered;
191 }
192 return false;
193 }
194
Winson Chung47c4c692014-03-17 10:17:11 -0700195 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800196
197 @Override
198 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv,
199 final TaskStack stack, final Task task) {
Winson Chung47c4c692014-03-17 10:17:11 -0700200 // Notify any callbacks of the launching of a new task
201 if (mCb != null) {
202 mCb.onTaskLaunching();
203 }
204
Winson Chung303e1ff2014-03-07 15:06:19 -0800205 final Runnable launchRunnable = new Runnable() {
206 @Override
207 public void run() {
208 TaskViewTransform transform;
209 View sourceView = tv;
210 int offsetX = 0;
211 int offsetY = 0;
212 if (tv == null) {
213 // Launch the activity
214 sourceView = stackView;
215 transform = stackView.getStackTransform(stack.indexOfTask(task));
216 offsetX = transform.rect.left;
217 offsetY = transform.rect.top;
218 } else {
219 transform = stackView.getStackTransform(stack.indexOfTask(task));
220 }
221
222 // Compute the thumbnail to scale up from
223 ActivityOptions opts = null;
224 int thumbnailWidth = transform.rect.width();
225 int thumbnailHeight = transform.rect.height();
226 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
227 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
228 // Resize the thumbnail to the size of the view that we are animating from
229 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
230 Bitmap.Config.ARGB_8888);
231 Canvas c = new Canvas(b);
232 c.drawBitmap(task.thumbnail,
233 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
234 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
235 c.setBitmap(null);
236 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
237 b, offsetX, offsetY);
238 }
239
240 // Launch the activity with the desired animation
Winson Chung04dfe0d2014-03-14 14:06:29 -0700241 Intent i = new Intent(task.key.intent);
Winson Chung303e1ff2014-03-07 15:06:19 -0800242 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
243 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
244 | Intent.FLAG_ACTIVITY_NEW_TASK);
245 if (opts != null) {
246 getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
247 } else {
248 getContext().startActivityAsUser(i, UserHandle.CURRENT);
249 }
Winson Chungbd912972014-03-18 14:36:35 -0700250
251 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
252 Constants.DebugFlags.App.TimeRecentsLaunchKey, "startActivity");
Winson Chung303e1ff2014-03-07 15:06:19 -0800253 }
254 };
255
Winson Chungbd912972014-03-18 14:36:35 -0700256 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
257 Constants.DebugFlags.App.TimeRecentsLaunchKey, "onTaskLaunched");
258
Winson Chung303e1ff2014-03-07 15:06:19 -0800259 // Launch the app right away if there is no task view, otherwise, animate the icon out first
260 if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingRecents) {
Winson Chung47c4c692014-03-17 10:17:11 -0700261 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800262 } else {
263 tv.animateOnLeavingRecents(launchRunnable);
264 }
265 }
266}