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