blob: 9133f7d3274fb58b8459737fc824f8751134d32f [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 Chung71243902014-03-14 17:52:47 -0700111 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]",
112 "width: " + width + " height: " + height, Console.AnsiGreen);
Winson Chung303e1ff2014-03-07 15:06:19 -0800113
114 // We measure our stack views sans the status bar. It will handle the nav bar itself.
115 RecentsConfiguration config = RecentsConfiguration.getInstance();
116 int childHeight = height - config.systemInsets.top;
117
118 // Measure each child
119 int childCount = getChildCount();
120 for (int i = 0; i < childCount; i++) {
121 final View child = getChildAt(i);
122 if (child.getVisibility() != GONE) {
123 child.measure(widthMeasureSpec,
124 MeasureSpec.makeMeasureSpec(childHeight, heightMode));
125 }
126 }
127
128 setMeasuredDimension(width, height);
129 }
130
131 @Override
132 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung71243902014-03-14 17:52:47 -0700133 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]",
134 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
Winson Chung303e1ff2014-03-07 15:06:19 -0800135 // We offset our stack views by the status bar height. It will handle the nav bar itself.
136 RecentsConfiguration config = RecentsConfiguration.getInstance();
137 top += config.systemInsets.top;
138
139 // Layout each child
140 // XXX: Based on the space node for that task view
141 int childCount = getChildCount();
142 for (int i = 0; i < childCount; i++) {
143 final View child = getChildAt(i);
144 if (child.getVisibility() != GONE) {
145 final int width = child.getMeasuredWidth();
146 final int height = child.getMeasuredHeight();
147 child.layout(left, top, left + width, top + height);
148 }
149 }
150 }
151
152 @Override
153 protected void dispatchDraw(Canvas canvas) {
Winson Chung47c4c692014-03-17 10:17:11 -0700154 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "",
155 Console.AnsiPurple);
Winson Chung303e1ff2014-03-07 15:06:19 -0800156 super.dispatchDraw(canvas);
157 }
158
159 @Override
160 protected boolean fitSystemWindows(Rect insets) {
Winson Chung47c4c692014-03-17 10:17:11 -0700161 Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
162 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
Winson Chung303e1ff2014-03-07 15:06:19 -0800163
164 // Update the configuration with the latest system insets and trigger a relayout
165 RecentsConfiguration config = RecentsConfiguration.getInstance();
166 config.updateSystemInsets(insets);
167 requestLayout();
168
169 return true;
170 }
171
172 /** Unfilters any filtered stacks */
173 public boolean unfilterFilteredStacks() {
174 if (mBSP != null) {
175 // Check if there are any filtered stacks and unfilter them before we back out of Recents
176 boolean stacksUnfiltered = false;
177 ArrayList<TaskStack> stacks = mBSP.getStacks();
178 for (TaskStack stack : stacks) {
179 if (stack.hasFilteredTasks()) {
180 stack.unfilterTasks();
181 stacksUnfiltered = true;
182 }
183 }
184 return stacksUnfiltered;
185 }
186 return false;
187 }
188
Winson Chung47c4c692014-03-17 10:17:11 -0700189 /**** TaskStackView.TaskStackCallbacks Implementation ****/
Winson Chung303e1ff2014-03-07 15:06:19 -0800190
191 @Override
192 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv,
193 final TaskStack stack, final Task task) {
Winson Chung47c4c692014-03-17 10:17:11 -0700194 // Notify any callbacks of the launching of a new task
195 if (mCb != null) {
196 mCb.onTaskLaunching();
197 }
198
Winson Chung303e1ff2014-03-07 15:06:19 -0800199 final Runnable launchRunnable = new Runnable() {
200 @Override
201 public void run() {
202 TaskViewTransform transform;
203 View sourceView = tv;
204 int offsetX = 0;
205 int offsetY = 0;
206 if (tv == null) {
207 // Launch the activity
208 sourceView = stackView;
209 transform = stackView.getStackTransform(stack.indexOfTask(task));
210 offsetX = transform.rect.left;
211 offsetY = transform.rect.top;
212 } else {
213 transform = stackView.getStackTransform(stack.indexOfTask(task));
214 }
215
216 // Compute the thumbnail to scale up from
217 ActivityOptions opts = null;
218 int thumbnailWidth = transform.rect.width();
219 int thumbnailHeight = transform.rect.height();
220 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
221 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
222 // Resize the thumbnail to the size of the view that we are animating from
223 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
224 Bitmap.Config.ARGB_8888);
225 Canvas c = new Canvas(b);
226 c.drawBitmap(task.thumbnail,
227 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
228 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
229 c.setBitmap(null);
230 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
231 b, offsetX, offsetY);
232 }
233
234 // Launch the activity with the desired animation
Winson Chung04dfe0d2014-03-14 14:06:29 -0700235 Intent i = new Intent(task.key.intent);
Winson Chung303e1ff2014-03-07 15:06:19 -0800236 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
237 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
238 | Intent.FLAG_ACTIVITY_NEW_TASK);
239 if (opts != null) {
240 getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
241 } else {
242 getContext().startActivityAsUser(i, UserHandle.CURRENT);
243 }
244 }
245 };
246
247 // Launch the app right away if there is no task view, otherwise, animate the icon out first
248 if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingRecents) {
Winson Chung47c4c692014-03-17 10:17:11 -0700249 post(launchRunnable);
Winson Chung303e1ff2014-03-07 15:06:19 -0800250 } else {
251 tv.animateOnLeavingRecents(launchRunnable);
252 }
253 }
254}