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