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