RecentsView.java revision 7124390f9d03fd451a0291eca679bfc901e84b95
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 // The space partitioning root of this container 44 SpaceNode mBSP; 45 46 public RecentsView(Context context) { 47 super(context); 48 setWillNotDraw(false); 49 } 50 51 /** Set/get the bsp root node */ 52 public void setBSP(SpaceNode n) { 53 mBSP = n; 54 55 // Create and add all the stacks for this partition of space. 56 removeAllViews(); 57 ArrayList<TaskStack> stacks = mBSP.getStacks(); 58 for (TaskStack stack : stacks) { 59 TaskStackView stackView = new TaskStackView(getContext(), stack); 60 stackView.setCallbacks(this); 61 addView(stackView); 62 } 63 } 64 65 /** Launches the first task from the first stack if possible */ 66 public boolean launchFirstTask() { 67 int childCount = getChildCount(); 68 for (int i = 0; i < childCount; i++) { 69 TaskStackView stackView = (TaskStackView) getChildAt(i); 70 TaskStack stack = stackView.mStack; 71 ArrayList<Task> tasks = stack.getTasks(); 72 if (!tasks.isEmpty()) { 73 Task task = tasks.get(tasks.size() - 1); 74 TaskView tv = null; 75 if (stackView.getChildCount() > 0) { 76 TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1); 77 if (stv.getTask() == task) { 78 tv = stv; 79 } 80 } 81 onTaskLaunched(stackView, tv, stack, task); 82 return true; 83 } 84 } 85 return false; 86 } 87 88 @Override 89 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 90 int width = MeasureSpec.getSize(widthMeasureSpec); 91 int height = MeasureSpec.getSize(heightMeasureSpec); 92 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 93 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]", 94 "width: " + width + " height: " + height, Console.AnsiGreen); 95 96 // We measure our stack views sans the status bar. It will handle the nav bar itself. 97 RecentsConfiguration config = RecentsConfiguration.getInstance(); 98 int childHeight = height - config.systemInsets.top; 99 100 // Measure each child 101 int childCount = getChildCount(); 102 for (int i = 0; i < childCount; i++) { 103 final View child = getChildAt(i); 104 if (child.getVisibility() != GONE) { 105 child.measure(widthMeasureSpec, 106 MeasureSpec.makeMeasureSpec(childHeight, heightMode)); 107 } 108 } 109 110 setMeasuredDimension(width, height); 111 } 112 113 @Override 114 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 115 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]", 116 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen); 117 // We offset our stack views by the status bar height. It will handle the nav bar itself. 118 RecentsConfiguration config = RecentsConfiguration.getInstance(); 119 top += config.systemInsets.top; 120 121 // Layout each child 122 // XXX: Based on the space node for that task view 123 int childCount = getChildCount(); 124 for (int i = 0; i < childCount; i++) { 125 final View child = getChildAt(i); 126 if (child.getVisibility() != GONE) { 127 final int width = child.getMeasuredWidth(); 128 final int height = child.getMeasuredHeight(); 129 child.layout(left, top, left + width, top + height); 130 } 131 } 132 } 133 134 @Override 135 protected void dispatchDraw(Canvas canvas) { 136 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "", Console.AnsiPurple); 137 super.dispatchDraw(canvas); 138 } 139 140 @Override 141 protected boolean fitSystemWindows(Rect insets) { 142 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen); 143 144 // Update the configuration with the latest system insets and trigger a relayout 145 RecentsConfiguration config = RecentsConfiguration.getInstance(); 146 config.updateSystemInsets(insets); 147 requestLayout(); 148 149 return true; 150 } 151 152 /** Unfilters any filtered stacks */ 153 public boolean unfilterFilteredStacks() { 154 if (mBSP != null) { 155 // Check if there are any filtered stacks and unfilter them before we back out of Recents 156 boolean stacksUnfiltered = false; 157 ArrayList<TaskStack> stacks = mBSP.getStacks(); 158 for (TaskStack stack : stacks) { 159 if (stack.hasFilteredTasks()) { 160 stack.unfilterTasks(); 161 stacksUnfiltered = true; 162 } 163 } 164 return stacksUnfiltered; 165 } 166 return false; 167 } 168 169 /**** View.OnClickListener Implementation ****/ 170 171 @Override 172 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv, 173 final TaskStack stack, final Task task) { 174 final Runnable launchRunnable = new Runnable() { 175 @Override 176 public void run() { 177 TaskViewTransform transform; 178 View sourceView = tv; 179 int offsetX = 0; 180 int offsetY = 0; 181 if (tv == null) { 182 // Launch the activity 183 sourceView = stackView; 184 transform = stackView.getStackTransform(stack.indexOfTask(task)); 185 offsetX = transform.rect.left; 186 offsetY = transform.rect.top; 187 } else { 188 transform = stackView.getStackTransform(stack.indexOfTask(task)); 189 } 190 191 // Compute the thumbnail to scale up from 192 ActivityOptions opts = null; 193 int thumbnailWidth = transform.rect.width(); 194 int thumbnailHeight = transform.rect.height(); 195 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 && 196 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { 197 // Resize the thumbnail to the size of the view that we are animating from 198 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, 199 Bitmap.Config.ARGB_8888); 200 Canvas c = new Canvas(b); 201 c.drawBitmap(task.thumbnail, 202 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()), 203 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null); 204 c.setBitmap(null); 205 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView, 206 b, offsetX, offsetY); 207 } 208 209 // Launch the activity with the desired animation 210 Intent i = new Intent(task.key.intent); 211 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 212 | Intent.FLAG_ACTIVITY_TASK_ON_HOME 213 | Intent.FLAG_ACTIVITY_NEW_TASK); 214 if (opts != null) { 215 getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT); 216 } else { 217 getContext().startActivityAsUser(i, UserHandle.CURRENT); 218 } 219 } 220 }; 221 222 // Launch the app right away if there is no task view, otherwise, animate the icon out first 223 if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingRecents) { 224 launchRunnable.run(); 225 } else { 226 tv.animateOnLeavingRecents(launchRunnable); 227 } 228 } 229} 230