RecentApplicationsDialog.java revision 621e17de87f18003aba2dedb719a2941020a7902
1/* 2 * Copyright (C) 2008 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.internal.policy.impl; 18 19import android.app.ActivityManager; 20import android.app.ActivityManagerNative; 21import android.app.Dialog; 22import android.app.IActivityManager; 23import android.app.StatusBarManager; 24import android.content.ActivityNotFoundException; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.res.Resources; 30import android.content.pm.ActivityInfo; 31import android.content.pm.PackageManager; 32import android.content.pm.ResolveInfo; 33import android.graphics.drawable.Drawable; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.RemoteException; 37import android.util.Log; 38import android.view.View; 39import android.view.Window; 40import android.view.WindowManager; 41import android.view.View.OnClickListener; 42import android.widget.TextView; 43 44import java.util.List; 45 46public class RecentApplicationsDialog extends Dialog implements OnClickListener { 47 // Elements for debugging support 48// private static final String LOG_TAG = "RecentApplicationsDialog"; 49 private static final boolean DBG_FORCE_EMPTY_LIST = false; 50 51 static private StatusBarManager sStatusBar; 52 53 private static final int NUM_BUTTONS = 8; 54 private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2; // allow for some discards 55 56 final TextView[] mIcons = new TextView[NUM_BUTTONS]; 57 View mNoAppsText; 58 IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 59 60 class RecentTag { 61 ActivityManager.RecentTaskInfo info; 62 Intent intent; 63 } 64 65 Handler mHandler = new Handler(); 66 Runnable mCleanup = new Runnable() { 67 public void run() { 68 // dump extra memory we're hanging on to 69 for (TextView icon: mIcons) { 70 icon.setCompoundDrawables(null, null, null, null); 71 icon.setTag(null); 72 } 73 } 74 }; 75 76 private int mIconSize; 77 78 public RecentApplicationsDialog(Context context) { 79 super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications); 80 81 final Resources resources = context.getResources(); 82 mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size); 83 } 84 85 /** 86 * We create the recent applications dialog just once, and it stays around (hidden) 87 * until activated by the user. 88 * 89 * @see PhoneWindowManager#showRecentAppsDialog 90 */ 91 @Override 92 protected void onCreate(Bundle savedInstanceState) { 93 super.onCreate(savedInstanceState); 94 95 Context context = getContext(); 96 97 if (sStatusBar == null) { 98 sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE); 99 } 100 101 Window window = getWindow(); 102 window.requestFeature(Window.FEATURE_NO_TITLE); 103 window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); 104 window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 105 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 106 window.setTitle("Recents"); 107 108 setContentView(com.android.internal.R.layout.recent_apps_dialog); 109 110 final WindowManager.LayoutParams params = window.getAttributes(); 111 params.width = WindowManager.LayoutParams.MATCH_PARENT; 112 params.height = WindowManager.LayoutParams.MATCH_PARENT; 113 window.setAttributes(params); 114 window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND); 115 116 mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0); 117 mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1); 118 mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2); 119 mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3); 120 mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4); 121 mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5); 122 mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6); 123 mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7); 124 mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message); 125 126 for (TextView b: mIcons) { 127 b.setOnClickListener(this); 128 } 129 } 130 131 /** 132 * Handler for user clicks. If a button was clicked, launch the corresponding activity. 133 */ 134 public void onClick(View v) { 135 136 for (TextView b: mIcons) { 137 if (b == v) { 138 RecentTag tag = (RecentTag)b.getTag(); 139 if (tag.info.id >= 0) { 140 // This is an active task; it should just go to the foreground. 141 final ActivityManager am = (ActivityManager) 142 getContext().getSystemService(Context.ACTIVITY_SERVICE); 143 am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME); 144 } else if (tag.intent != null) { 145 tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 146 | Intent.FLAG_ACTIVITY_TASK_ON_HOME); 147 try { 148 getContext().startActivity(tag.intent); 149 } catch (ActivityNotFoundException e) { 150 Log.w("Recent", "Unable to launch recent task", e); 151 } 152 } 153 break; 154 } 155 } 156 dismiss(); 157 } 158 159 /** 160 * Set up and show the recent activities dialog. 161 */ 162 @Override 163 public void onStart() { 164 super.onStart(); 165 reloadButtons(); 166 if (sStatusBar != null) { 167 sStatusBar.disable(StatusBarManager.DISABLE_EXPAND); 168 } 169 170 // receive broadcasts 171 getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter); 172 173 mHandler.removeCallbacks(mCleanup); 174 } 175 176 /** 177 * Dismiss the recent activities dialog. 178 */ 179 @Override 180 public void onStop() { 181 super.onStop(); 182 183 if (sStatusBar != null) { 184 sStatusBar.disable(StatusBarManager.DISABLE_NONE); 185 } 186 187 // stop receiving broadcasts 188 getContext().unregisterReceiver(mBroadcastReceiver); 189 190 mHandler.postDelayed(mCleanup, 100); 191 } 192 193 /** 194 * Reload the 6 buttons with recent activities 195 */ 196 private void reloadButtons() { 197 198 final Context context = getContext(); 199 final PackageManager pm = context.getPackageManager(); 200 final ActivityManager am = (ActivityManager) 201 context.getSystemService(Context.ACTIVITY_SERVICE); 202 final List<ActivityManager.RecentTaskInfo> recentTasks = 203 am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); 204 205 ActivityInfo homeInfo = 206 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) 207 .resolveActivityInfo(pm, 0); 208 209 IconUtilities iconUtilities = new IconUtilities(getContext()); 210 211 // Performance note: Our android performance guide says to prefer Iterator when 212 // using a List class, but because we know that getRecentTasks() always returns 213 // an ArrayList<>, we'll use a simple index instead. 214 int index = 0; 215 int numTasks = recentTasks.size(); 216 for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) { 217 final ActivityManager.RecentTaskInfo info = recentTasks.get(i); 218 219 // for debug purposes only, disallow first result to create empty lists 220 if (DBG_FORCE_EMPTY_LIST && (i == 0)) continue; 221 222 Intent intent = new Intent(info.baseIntent); 223 if (info.origActivity != null) { 224 intent.setComponent(info.origActivity); 225 } 226 227 // Skip the current home activity. 228 if (homeInfo != null) { 229 if (homeInfo.packageName.equals( 230 intent.getComponent().getPackageName()) 231 && homeInfo.name.equals( 232 intent.getComponent().getClassName())) { 233 continue; 234 } 235 } 236 237 intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) 238 | Intent.FLAG_ACTIVITY_NEW_TASK); 239 final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); 240 if (resolveInfo != null) { 241 final ActivityInfo activityInfo = resolveInfo.activityInfo; 242 final String title = activityInfo.loadLabel(pm).toString(); 243 Drawable icon = activityInfo.loadIcon(pm); 244 245 if (title != null && title.length() > 0 && icon != null) { 246 final TextView tv = mIcons[index]; 247 tv.setText(title); 248 icon = iconUtilities.createIconDrawable(icon); 249 tv.setCompoundDrawables(null, icon, null, null); 250 RecentTag tag = new RecentTag(); 251 tag.info = info; 252 tag.intent = intent; 253 tv.setTag(tag); 254 tv.setVisibility(View.VISIBLE); 255 tv.setPressed(false); 256 tv.clearFocus(); 257 ++index; 258 } 259 } 260 } 261 262 // handle the case of "no icons to show" 263 mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE); 264 265 // hide the rest 266 for (; index < NUM_BUTTONS; ++index) { 267 mIcons[index].setVisibility(View.GONE); 268 } 269 } 270 271 /** 272 * This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent. It's an indication that 273 * we should close ourselves immediately, in order to allow a higher-priority UI to take over 274 * (e.g. phone call received). 275 */ 276 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 277 @Override 278 public void onReceive(Context context, Intent intent) { 279 String action = intent.getAction(); 280 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 281 String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY); 282 if (! PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) { 283 dismiss(); 284 } 285 } 286 } 287 }; 288} 289