Recents.java revision 6cb485fd2e2f8ace05e1ff281ab0fc824d3652cb
1/*
2 * Copyright (C) 2013 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.recent;
18
19import android.app.ActivityOptions;
20import android.content.ActivityNotFoundException;
21import android.content.Intent;
22import android.content.res.Configuration;
23import android.content.res.Resources;
24import android.graphics.Bitmap;
25import android.graphics.Canvas;
26import android.graphics.Paint;
27import android.graphics.drawable.BitmapDrawable;
28import android.graphics.drawable.Drawable;
29import android.os.Bundle;
30import android.os.UserHandle;
31import android.util.DisplayMetrics;
32import android.util.Log;
33import android.view.Display;
34import android.view.View;
35import com.android.systemui.R;
36import com.android.systemui.RecentsComponent;
37import com.android.systemui.SystemUI;
38import com.android.systemui.recents.AlternateRecentsComponent;
39
40
41public class Recents extends SystemUI implements RecentsComponent {
42    private static final String TAG = "Recents";
43    private static final boolean DEBUG = true;
44
45    // Which recents to use
46    boolean mUseAlternateRecents = true;
47    AlternateRecentsComponent mAlternateRecents;
48    boolean mBootCompleted = false;
49
50    @Override
51    public void start() {
52        if (mUseAlternateRecents) {
53            if (mAlternateRecents == null) {
54                mAlternateRecents = new AlternateRecentsComponent(mContext);
55            }
56            mAlternateRecents.onStart();
57        }
58
59        putComponent(RecentsComponent.class, this);
60    }
61
62    @Override
63    protected void onBootCompleted() {
64        mBootCompleted = true;
65    }
66
67    @Override
68    public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
69        if (mUseAlternateRecents) {
70            mAlternateRecents.onShowRecents(triggeredFromAltTab, statusBarView);
71        }
72    }
73
74    @Override
75    public void hideRecents(boolean triggeredFromAltTab) {
76        if (mUseAlternateRecents) {
77            mAlternateRecents.onHideRecents(triggeredFromAltTab);
78        } else {
79            Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
80            intent.setPackage("com.android.systemui");
81            sendBroadcastSafely(intent);
82
83            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
84        }
85    }
86
87    @Override
88    public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
89        if (mUseAlternateRecents) {
90            // Launch the alternate recents if required
91            mAlternateRecents.onToggleRecents(statusBarView);
92            return;
93        }
94
95        if (DEBUG) Log.d(TAG, "toggle recents panel");
96        try {
97            TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();
98
99            Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);
100            intent.setClassName("com.android.systemui",
101                    "com.android.systemui.recent.RecentsActivity");
102            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
103                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
104
105            if (firstTask == null) {
106                if (RecentsActivity.forceOpaqueBackground(mContext)) {
107                    ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
108                            R.anim.recents_launch_from_launcher_enter,
109                            R.anim.recents_launch_from_launcher_exit);
110                    mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
111                            UserHandle.USER_CURRENT));
112                } else {
113                    // The correct window animation will be applied via the activity's style
114                    mContext.startActivityAsUser(intent, new UserHandle(
115                            UserHandle.USER_CURRENT));
116                }
117
118            } else {
119                Bitmap first = null;
120                if (firstTask.getThumbnail() instanceof BitmapDrawable) {
121                    first = ((BitmapDrawable) firstTask.getThumbnail()).getBitmap();
122                } else {
123                    first = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
124                    Drawable d = RecentTasksLoader.getInstance(mContext).getDefaultThumbnail();
125                    d.draw(new Canvas(first));
126                }
127                final Resources res = mContext.getResources();
128
129                float thumbWidth = res
130                        .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
131                float thumbHeight = res
132                        .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
133                if (first == null) {
134                    throw new RuntimeException("Recents thumbnail is null");
135                }
136                if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
137                    first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
138                            true);
139                    if (first == null) {
140                        throw new RuntimeException("Recents thumbnail is null");
141                    }
142                }
143
144
145                DisplayMetrics dm = new DisplayMetrics();
146                display.getMetrics(dm);
147                // calculate it here, but consider moving it elsewhere
148                // first, determine which orientation you're in.
149                final Configuration config = res.getConfiguration();
150                int x, y;
151
152                if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
153                    float appLabelLeftMargin = res.getDimensionPixelSize(
154                            R.dimen.status_bar_recents_app_label_left_margin);
155                    float appLabelWidth = res.getDimensionPixelSize(
156                            R.dimen.status_bar_recents_app_label_width);
157                    float thumbLeftMargin = res.getDimensionPixelSize(
158                            R.dimen.status_bar_recents_thumbnail_left_margin);
159                    float thumbBgPadding = res.getDimensionPixelSize(
160                            R.dimen.status_bar_recents_thumbnail_bg_padding);
161
162                    float width = appLabelLeftMargin +
163                            +appLabelWidth
164                            + thumbLeftMargin
165                            + thumbWidth
166                            + 2 * thumbBgPadding;
167
168                    x = (int) ((dm.widthPixels - width) / 2f + appLabelLeftMargin + appLabelWidth
169                            + thumbBgPadding + thumbLeftMargin);
170                    y = (int) (dm.heightPixels
171                            - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height)
172                            - thumbBgPadding);
173                    if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
174                        x = dm.widthPixels - x - res.getDimensionPixelSize(
175                                R.dimen.status_bar_recents_thumbnail_width);
176                    }
177
178                } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
179                    float thumbTopMargin = res.getDimensionPixelSize(
180                            R.dimen.status_bar_recents_thumbnail_top_margin);
181                    float thumbBgPadding = res.getDimensionPixelSize(
182                            R.dimen.status_bar_recents_thumbnail_bg_padding);
183                    float textPadding = res.getDimensionPixelSize(
184                            R.dimen.status_bar_recents_text_description_padding);
185                    float labelTextSize = res.getDimensionPixelSize(
186                            R.dimen.status_bar_recents_app_label_text_size);
187                    Paint p = new Paint();
188                    p.setTextSize(labelTextSize);
189                    float labelTextHeight = p.getFontMetricsInt().bottom
190                            - p.getFontMetricsInt().top;
191                    float descriptionTextSize = res.getDimensionPixelSize(
192                            R.dimen.status_bar_recents_app_description_text_size);
193                    p.setTextSize(descriptionTextSize);
194                    float descriptionTextHeight = p.getFontMetricsInt().bottom
195                            - p.getFontMetricsInt().top;
196
197                    float statusBarHeight = res.getDimensionPixelSize(
198                            com.android.internal.R.dimen.status_bar_height);
199                    float recentsItemTopPadding = statusBarHeight;
200
201                    float height = thumbTopMargin
202                            + thumbHeight
203                            + 2 * thumbBgPadding + textPadding + labelTextHeight
204                            + recentsItemTopPadding + textPadding + descriptionTextHeight;
205                    float recentsItemRightPadding = res
206                            .getDimensionPixelSize(R.dimen.status_bar_recents_item_padding);
207                    float recentsScrollViewRightPadding = res
208                            .getDimensionPixelSize(R.dimen.status_bar_recents_right_glow_margin);
209                    x = (int) (dm.widthPixels - res
210                            .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width)
211                            - thumbBgPadding - recentsItemRightPadding
212                            - recentsScrollViewRightPadding);
213                    y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin
214                            + recentsItemTopPadding + thumbBgPadding + statusBarHeight);
215                }
216
217                ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(
218                        statusBarView,
219                        first, x, y,
220                        new ActivityOptions.OnAnimationStartedListener() {
221                            public void onAnimationStarted() {
222                                Intent intent =
223                                        new Intent(RecentsActivity.WINDOW_ANIMATION_START_INTENT);
224                                intent.setPackage("com.android.systemui");
225                                sendBroadcastSafely(intent);
226                            }
227                        });
228                intent.putExtra(RecentsActivity.WAITING_FOR_WINDOW_ANIMATION_PARAM, true);
229                startActivitySafely(intent, opts.toBundle());
230            }
231        } catch (ActivityNotFoundException e) {
232            Log.e(TAG, "Failed to launch RecentAppsIntent", e);
233        }
234    }
235
236    @Override
237    protected void onConfigurationChanged(Configuration newConfig) {
238        if (mUseAlternateRecents) {
239            mAlternateRecents.onConfigurationChanged(newConfig);
240        }
241    }
242
243    @Override
244    public void preloadRecents() {
245        if (mUseAlternateRecents) {
246            mAlternateRecents.onPreloadRecents();
247        } else {
248            Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
249            intent.setClassName("com.android.systemui",
250                    "com.android.systemui.recent.RecentsPreloadReceiver");
251            sendBroadcastSafely(intent);
252
253            RecentTasksLoader.getInstance(mContext).preloadFirstTask();
254        }
255    }
256
257    @Override
258    public void cancelPreloadingRecents() {
259        if (mUseAlternateRecents) {
260            mAlternateRecents.onCancelPreloadingRecents();
261        } else {
262            Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
263            intent.setClassName("com.android.systemui",
264                    "com.android.systemui.recent.RecentsPreloadReceiver");
265            sendBroadcastSafely(intent);
266
267            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
268        }
269    }
270
271    /**
272     * Send broadcast only if BOOT_COMPLETED
273     */
274    private void sendBroadcastSafely(Intent intent) {
275        if (!mBootCompleted) return;
276        mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
277    }
278
279    /**
280     * Start activity only if BOOT_COMPLETED
281     */
282    private void startActivitySafely(Intent intent, Bundle opts) {
283        if (!mBootCompleted) return;
284        mContext.startActivityAsUser(intent, opts, new UserHandle(UserHandle.USER_CURRENT));
285    }
286}
287