RecentsView.java revision 47c4c69575e94635a9356e8a1faf4b56a0120452
1/* 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 */ 42public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks { 43 44 /** The RecentsView callbacks */ 45 public interface RecentsViewCallbacks { 46 public void onTaskLaunching(); 47 } 48 49 // The space partitioning root of this container 50 SpaceNode mBSP; 51 // Recents view callbacks 52 RecentsViewCallbacks mCb; 53 54 public RecentsView(Context context) { 55 super(context); 56 setWillNotDraw(false); 57 } 58 59 /** Sets the callbacks */ 60 public void setCallbacks(RecentsViewCallbacks cb) { 61 mCb = cb; 62 } 63 64 /** Set/get the bsp root node */ 65 public void setBSP(SpaceNode n) { 66 mBSP = n; 67 68 // Create and add all the stacks for this partition of space. 69 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() { 80 // Get the first stack view 81 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(); 86 87 // Get the first task in the stack 88 if (!tasks.isEmpty()) { 89 Task task = tasks.get(tasks.size() - 1); 90 TaskView tv = null; 91 92 // Try and use the first child task view as the source of the launch animation 93 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); 111 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]", 112 "width: " + width + " height: " + height, Console.AnsiGreen); 113 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) { 133 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]", 134 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen); 135 // 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) { 154 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "", 155 Console.AnsiPurple); 156 super.dispatchDraw(canvas); 157 } 158 159 @Override 160 protected boolean fitSystemWindows(Rect insets) { 161 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, 162 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen); 163 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 189 /**** TaskStackView.TaskStackCallbacks Implementation ****/ 190 191 @Override 192 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv, 193 final TaskStack stack, final Task task) { 194 // Notify any callbacks of the launching of a new task 195 if (mCb != null) { 196 mCb.onTaskLaunching(); 197 } 198 199 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 235 Intent i = new Intent(task.key.intent); 236 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) { 249 post(launchRunnable); 250 } else { 251 tv.animateOnLeavingRecents(launchRunnable); 252 } 253 } 254} 255