RecentsView.java revision b44c24fb50845dfbc1f49e78085cf5e01a32067f
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.ActivityNotFoundException; 21import android.content.Context; 22import android.content.Intent; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Rect; 26import android.os.UserHandle; 27import android.view.View; 28import android.widget.FrameLayout; 29import com.android.systemui.recents.Console; 30import com.android.systemui.recents.Constants; 31import com.android.systemui.recents.RecentsConfiguration; 32import com.android.systemui.recents.RecentsTaskLoader; 33import com.android.systemui.recents.model.SpaceNode; 34import com.android.systemui.recents.model.Task; 35import com.android.systemui.recents.model.TaskStack; 36 37import java.util.ArrayList; 38 39 40/** 41 * This view is the the top level layout that contains TaskStacks (which are laid out according 42 * to their SpaceNode bounds. 43 */ 44public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks { 45 46 /** The RecentsView callbacks */ 47 public interface RecentsViewCallbacks { 48 public void onTaskLaunching(); 49 } 50 51 // The space partitioning root of this container 52 SpaceNode mBSP; 53 // Recents view callbacks 54 RecentsViewCallbacks mCb; 55 56 public RecentsView(Context context) { 57 super(context); 58 setWillNotDraw(false); 59 } 60 61 /** Sets the callbacks */ 62 public void setCallbacks(RecentsViewCallbacks cb) { 63 mCb = cb; 64 } 65 66 /** Set/get the bsp root node */ 67 public void setBSP(SpaceNode n) { 68 mBSP = n; 69 70 // Create and add all the stacks for this partition of space. 71 removeAllViews(); 72 ArrayList<TaskStack> stacks = mBSP.getStacks(); 73 for (TaskStack stack : stacks) { 74 TaskStackView stackView = new TaskStackView(getContext(), stack); 75 stackView.setCallbacks(this); 76 addView(stackView); 77 } 78 } 79 80 /** Launches the first task from the first stack if possible */ 81 public boolean launchFirstTask() { 82 // Get the first stack view 83 int childCount = getChildCount(); 84 for (int i = 0; i < childCount; i++) { 85 TaskStackView stackView = (TaskStackView) getChildAt(i); 86 TaskStack stack = stackView.mStack; 87 ArrayList<Task> tasks = stack.getTasks(); 88 89 // Get the first task in the stack 90 if (!tasks.isEmpty()) { 91 Task task = tasks.get(tasks.size() - 1); 92 TaskView tv = null; 93 94 // Try and use the first child task view as the source of the launch animation 95 if (stackView.getChildCount() > 0) { 96 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1); 97 if (stv.getTask() == task) { 98 tv = stv; 99 } 100 } 101 onTaskLaunched(stackView, tv, stack, task); 102 return true; 103 } 104 } 105 return false; 106 } 107 108 @Override 109 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 110 int width = MeasureSpec.getSize(widthMeasureSpec); 111 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 112 int height = MeasureSpec.getSize(heightMeasureSpec); 113 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 114 115 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]", 116 "width: " + width + " height: " + height, Console.AnsiGreen); 117 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup, 118 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure"); 119 120 // We measure our stack views sans the status bar. It will handle the nav bar itself. 121 RecentsConfiguration config = RecentsConfiguration.getInstance(); 122 int childWidth = width - config.systemInsets.right; 123 int childHeight = height - config.systemInsets.top; 124 125 // Measure each child 126 int childCount = getChildCount(); 127 for (int i = 0; i < childCount; i++) { 128 final View child = getChildAt(i); 129 if (child.getVisibility() != GONE) { 130 child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode), 131 MeasureSpec.makeMeasureSpec(childHeight, heightMode)); 132 } 133 } 134 135 setMeasuredDimension(width, height); 136 } 137 138 @Override 139 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 140 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]", 141 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen); 142 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup, 143 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout"); 144 145 // We offset our stack views by the status bar height. It will handle the nav bar itself. 146 RecentsConfiguration config = RecentsConfiguration.getInstance(); 147 top += config.systemInsets.top; 148 149 // Layout each child 150 // XXX: Based on the space node for that task view 151 int childCount = getChildCount(); 152 for (int i = 0; i < childCount; i++) { 153 final View child = getChildAt(i); 154 if (child.getVisibility() != GONE) { 155 final int width = child.getMeasuredWidth(); 156 final int height = child.getMeasuredHeight(); 157 child.layout(left, top, left + width, top + height); 158 } 159 } 160 } 161 162 @Override 163 protected void dispatchDraw(Canvas canvas) { 164 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "", 165 Console.AnsiPurple); 166 super.dispatchDraw(canvas); 167 } 168 169 @Override 170 protected boolean fitSystemWindows(Rect insets) { 171 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, 172 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen); 173 174 // Update the configuration with the latest system insets and trigger a relayout 175 RecentsConfiguration config = RecentsConfiguration.getInstance(); 176 config.updateSystemInsets(insets); 177 requestLayout(); 178 179 return true; 180 } 181 182 /** Unfilters any filtered stacks */ 183 public boolean unfilterFilteredStacks() { 184 if (mBSP != null) { 185 // Check if there are any filtered stacks and unfilter them before we back out of Recents 186 boolean stacksUnfiltered = false; 187 ArrayList<TaskStack> stacks = mBSP.getStacks(); 188 for (TaskStack stack : stacks) { 189 if (stack.hasFilteredTasks()) { 190 stack.unfilterTasks(); 191 stacksUnfiltered = true; 192 } 193 } 194 return stacksUnfiltered; 195 } 196 return false; 197 } 198 199 /**** TaskStackView.TaskStackCallbacks Implementation ****/ 200 201 @Override 202 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv, 203 final TaskStack stack, final Task task) { 204 // Notify any callbacks of the launching of a new task 205 if (mCb != null) { 206 mCb.onTaskLaunching(); 207 } 208 209 final Runnable launchRunnable = new Runnable() { 210 @Override 211 public void run() { 212 TaskViewTransform transform; 213 View sourceView = tv; 214 int offsetX = 0; 215 int offsetY = 0; 216 int stackScroll = stackView.getStackScroll(); 217 if (tv == null) { 218 // If there is no actual task view, then use the stack view as the source view 219 // and then offset to the expected transform rect, but bound this to just 220 // outside the display rect (to ensure we don't animate from too far away) 221 RecentsConfiguration config = RecentsConfiguration.getInstance(); 222 sourceView = stackView; 223 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); 224 offsetX = transform.rect.left; 225 offsetY = Math.min(transform.rect.top, config.displayRect.height()); 226 } else { 227 transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); 228 } 229 230 // Compute the thumbnail to scale up from 231 ActivityOptions opts = null; 232 int thumbnailWidth = transform.rect.width(); 233 int thumbnailHeight = transform.rect.height(); 234 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 && 235 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { 236 // Resize the thumbnail to the size of the view that we are animating from 237 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, 238 Bitmap.Config.ARGB_8888); 239 Canvas c = new Canvas(b); 240 c.drawBitmap(task.thumbnail, 241 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()), 242 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null); 243 c.setBitmap(null); 244 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView, 245 b, offsetX, offsetY); 246 } 247 248 249 if (task.isActive) { 250 // Bring an active task to the foreground 251 RecentsTaskLoader.getInstance().getSystemServicesProxy() 252 .moveTaskToFront(task.key.id, opts); 253 } else { 254 // Launch the activity with the desired animation 255 Intent i = new Intent(task.key.baseIntent); 256 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 257 | Intent.FLAG_ACTIVITY_TASK_ON_HOME 258 | Intent.FLAG_ACTIVITY_NEW_TASK); 259 try { 260 UserHandle taskUser = new UserHandle(task.userId); 261 if (opts != null) { 262 getContext().startActivityAsUser(i, opts.toBundle(), taskUser); 263 } else { 264 getContext().startActivityAsUser(i, taskUser); 265 } 266 } catch (ActivityNotFoundException anfe) { 267 Console.logError(getContext(), "Could not start Activity"); 268 } 269 } 270 271 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask, 272 Constants.DebugFlags.App.TimeRecentsLaunchKey, "startActivity"); 273 } 274 }; 275 276 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask, 277 Constants.DebugFlags.App.TimeRecentsLaunchKey, "onTaskLaunched"); 278 279 // Launch the app right away if there is no task view, otherwise, animate the icon out first 280 if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskBarOnLeavingRecents) { 281 post(launchRunnable); 282 } else { 283 tv.animateOnLeavingRecents(launchRunnable); 284 } 285 } 286} 287