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