RecentsView.java revision 04dfe0d26b944324ee920001f40d74cff47281d6
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]", "width: " + width + " height: " + height, Console.AnsiGreen); 94 95 // We measure our stack views sans the status bar. It will handle the nav bar itself. 96 RecentsConfiguration config = RecentsConfiguration.getInstance(); 97 int childHeight = height - config.systemInsets.top; 98 99 // Measure each child 100 int childCount = getChildCount(); 101 for (int i = 0; i < childCount; i++) { 102 final View child = getChildAt(i); 103 if (child.getVisibility() != GONE) { 104 child.measure(widthMeasureSpec, 105 MeasureSpec.makeMeasureSpec(childHeight, heightMode)); 106 } 107 } 108 109 setMeasuredDimension(width, height); 110 } 111 112 @Override 113 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 114 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]", new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen); 115 // We offset our stack views by the status bar height. It will handle the nav bar itself. 116 RecentsConfiguration config = RecentsConfiguration.getInstance(); 117 top += config.systemInsets.top; 118 119 // Layout each child 120 // XXX: Based on the space node for that task view 121 int childCount = getChildCount(); 122 for (int i = 0; i < childCount; i++) { 123 final View child = getChildAt(i); 124 if (child.getVisibility() != GONE) { 125 final int width = child.getMeasuredWidth(); 126 final int height = child.getMeasuredHeight(); 127 child.layout(left, top, left + width, top + height); 128 } 129 } 130 } 131 132 @Override 133 protected void dispatchDraw(Canvas canvas) { 134 Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "", Console.AnsiPurple); 135 super.dispatchDraw(canvas); 136 } 137 138 @Override 139 protected boolean fitSystemWindows(Rect insets) { 140 Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen); 141 142 // Update the configuration with the latest system insets and trigger a relayout 143 RecentsConfiguration config = RecentsConfiguration.getInstance(); 144 config.updateSystemInsets(insets); 145 requestLayout(); 146 147 return true; 148 } 149 150 /** Unfilters any filtered stacks */ 151 public boolean unfilterFilteredStacks() { 152 if (mBSP != null) { 153 // Check if there are any filtered stacks and unfilter them before we back out of Recents 154 boolean stacksUnfiltered = false; 155 ArrayList<TaskStack> stacks = mBSP.getStacks(); 156 for (TaskStack stack : stacks) { 157 if (stack.hasFilteredTasks()) { 158 stack.unfilterTasks(); 159 stacksUnfiltered = true; 160 } 161 } 162 return stacksUnfiltered; 163 } 164 return false; 165 } 166 167 /**** View.OnClickListener Implementation ****/ 168 169 @Override 170 public void onTaskLaunched(final TaskStackView stackView, final TaskView tv, 171 final TaskStack stack, final Task task) { 172 final Runnable launchRunnable = new Runnable() { 173 @Override 174 public void run() { 175 TaskViewTransform transform; 176 View sourceView = tv; 177 int offsetX = 0; 178 int offsetY = 0; 179 if (tv == null) { 180 // Launch the activity 181 sourceView = stackView; 182 transform = stackView.getStackTransform(stack.indexOfTask(task)); 183 offsetX = transform.rect.left; 184 offsetY = transform.rect.top; 185 } else { 186 transform = stackView.getStackTransform(stack.indexOfTask(task)); 187 } 188 189 // Compute the thumbnail to scale up from 190 ActivityOptions opts = null; 191 int thumbnailWidth = transform.rect.width(); 192 int thumbnailHeight = transform.rect.height(); 193 if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 && 194 task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { 195 // Resize the thumbnail to the size of the view that we are animating from 196 Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, 197 Bitmap.Config.ARGB_8888); 198 Canvas c = new Canvas(b); 199 c.drawBitmap(task.thumbnail, 200 new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()), 201 new Rect(0, 0, thumbnailWidth, thumbnailHeight), null); 202 c.setBitmap(null); 203 opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView, 204 b, offsetX, offsetY); 205 } 206 207 // Launch the activity with the desired animation 208 Intent i = new Intent(task.key.intent); 209 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 210 | Intent.FLAG_ACTIVITY_TASK_ON_HOME 211 | Intent.FLAG_ACTIVITY_NEW_TASK); 212 if (opts != null) { 213 getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT); 214 } else { 215 getContext().startActivityAsUser(i, UserHandle.CURRENT); 216 } 217 } 218 }; 219 220 // Launch the app right away if there is no task view, otherwise, animate the icon out first 221 if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingRecents) { 222 launchRunnable.run(); 223 } else { 224 tv.animateOnLeavingRecents(launchRunnable); 225 } 226 } 227} 228