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