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