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