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