GlobalActions.java revision c5d93b3b6a0ba4831903f8e8d1664c4470cf15d7
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 com.android.internal.R; 20import com.google.android.collect.Lists; 21 22import android.app.AlertDialog; 23import android.app.StatusBarManager; 24import android.content.Context; 25import android.content.BroadcastReceiver; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.DialogInterface; 29import android.media.AudioManager; 30import android.os.LocalPowerManager; 31import android.os.Handler; 32import android.os.Message; 33import android.os.SystemClock; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.WindowManager; 38import android.widget.BaseAdapter; 39import android.widget.ImageView; 40import android.widget.LinearLayout; 41import android.widget.TextView; 42 43import java.util.ArrayList; 44 45/** 46 * Helper to show the global actions dialog. Each item is an {@link Action} that 47 * may show depending on whether the keyguard is showing, and whether the device 48 * is provisioned. 49 */ 50class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener { 51 52 private StatusBarManager mStatusBar; 53 54 private final Context mContext; 55 private final LocalPowerManager mPowerManager; 56 private final AudioManager mAudioManager; 57 private ArrayList<Action> mItems; 58 private AlertDialog mDialog; 59 60 private ToggleAction mSilentModeToggle; 61 62 private MyAdapter mAdapter; 63 64 private boolean mKeyguardShowing = false; 65 private boolean mDeviceProvisioned = false; 66 67 /** 68 * @param context everything needs a context :) 69 * @param powerManager used to turn the screen off (the lock action). 70 */ 71 public GlobalActions(Context context, LocalPowerManager powerManager) { 72 mContext = context; 73 mPowerManager = powerManager; 74 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 75 76 // receive broadcasts 77 IntentFilter filter = new IntentFilter(); 78 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 79 context.registerReceiver(mBroadcastReceiver, filter); 80 } 81 82 /** 83 * Show the global actions dialog (creating if necessary) 84 * @param keyguardShowing True if keyguard is showing 85 */ 86 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) { 87 mKeyguardShowing = keyguardShowing; 88 mDeviceProvisioned = isDeviceProvisioned; 89 if (mDialog == null) { 90 mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE); 91 mDialog = createDialog(); 92 } 93 prepareDialog(); 94 95 mStatusBar.disable(StatusBarManager.DISABLE_EXPAND); 96 mDialog.show(); 97 } 98 99 /** 100 * Create the global actions dialog. 101 * @return A new dialog. 102 */ 103 private AlertDialog createDialog() { 104 105 mSilentModeToggle = new ToggleAction( 106 R.drawable.ic_lock_silent_mode, 107 R.drawable.ic_lock_silent_mode_off, 108 R.string.global_action_toggle_silent_mode, 109 R.string.global_action_silent_mode_on_status, 110 R.string.global_action_silent_mode_off_status) { 111 112 void onToggle(boolean on) { 113 mAudioManager.setRingerMode(on ? AudioManager.RINGER_MODE_SILENT 114 : AudioManager.RINGER_MODE_NORMAL); 115 } 116 117 public boolean showDuringKeyguard() { 118 return true; 119 } 120 121 public boolean showBeforeProvisioning() { 122 return false; 123 } 124 }; 125 126 mItems = Lists.newArrayList( 127 // first: lock screen 128 new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock, R.string.global_action_lock) { 129 130 public void onPress() { 131 mPowerManager.goToSleep(SystemClock.uptimeMillis() + 1); 132 } 133 134 public boolean showDuringKeyguard() { 135 return false; 136 } 137 138 public boolean showBeforeProvisioning() { 139 return false; 140 } 141 }, 142 // next: silent mode 143 mSilentModeToggle, 144 // last: power off 145 new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off, R.string.global_action_power_off) { 146 147 public void onPress() { 148 // shutdown by making sure radio and power are handled accordingly. 149 ShutdownThread.shutdownAfterDisablingRadio(mContext, true); 150 } 151 152 public boolean showDuringKeyguard() { 153 return true; 154 } 155 156 public boolean showBeforeProvisioning() { 157 return true; 158 } 159 }); 160 161 mAdapter = new MyAdapter(); 162 163 final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); 164 165 ab.setAdapter(mAdapter, this) 166 .setInverseBackgroundForced(true) 167 .setTitle(R.string.global_actions); 168 169 final AlertDialog dialog = ab.create(); 170 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); 171 dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, 172 WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 173 174 dialog.setOnDismissListener(this); 175 176 return dialog; 177 } 178 179 private void prepareDialog() { 180 // TODO: May need another 'Vibrate' toggle button, but for now treat them the same 181 final boolean silentModeOn = 182 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; 183 mSilentModeToggle.updateState(silentModeOn); 184 mAdapter.notifyDataSetChanged(); 185 if (mKeyguardShowing) { 186 mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 187 } else { 188 mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); 189 } 190 } 191 192 /** {@inheritDoc} */ 193 public void onDismiss(DialogInterface dialog) { 194 mStatusBar.disable(StatusBarManager.DISABLE_NONE); 195 } 196 197 /** {@inheritDoc} */ 198 public void onClick(DialogInterface dialog, int which) { 199 dialog.dismiss(); 200 mAdapter.getItem(which).onPress(); 201 } 202 203 204 /** 205 * The adapter used for the list within the global actions dialog, taking 206 * into account whether the keyguard is showing via 207 * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned 208 * via {@link GlobalActions#mDeviceProvisioned}. 209 */ 210 private class MyAdapter extends BaseAdapter { 211 212 public int getCount() { 213 int count = 0; 214 215 for (int i = 0; i < mItems.size(); i++) { 216 final Action action = mItems.get(i); 217 218 if (mKeyguardShowing && !action.showDuringKeyguard()) { 219 continue; 220 } 221 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { 222 continue; 223 } 224 count++; 225 } 226 return count; 227 } 228 229 public Action getItem(int position) { 230 231 int filteredPos = 0; 232 for (int i = 0; i < mItems.size(); i++) { 233 final Action action = mItems.get(i); 234 if (mKeyguardShowing && !action.showDuringKeyguard()) { 235 continue; 236 } 237 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { 238 continue; 239 } 240 if (filteredPos == position) { 241 return action; 242 } 243 filteredPos++; 244 } 245 246 throw new IllegalArgumentException("position " + position + " out of " 247 + "range of showable actions, filtered count = " 248 + "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing 249 + ", provisioned=" + mDeviceProvisioned); 250 } 251 252 253 public long getItemId(int position) { 254 return position; 255 } 256 257 public View getView(int position, View convertView, ViewGroup parent) { 258 Action action = getItem(position); 259 return action.create(mContext, (LinearLayout) convertView, LayoutInflater.from(mContext)); 260 } 261 } 262 263 // note: the scheme below made more sense when we were planning on having 264 // 8 different things in the global actions dialog. seems overkill with 265 // only 3 items now, but may as well keep this flexible approach so it will 266 // be easy should someone decide at the last minute to include something 267 // else, such as 'enable wifi', or 'enable bluetooth' 268 269 /** 270 * What each item in the global actions dialog must be able to support. 271 */ 272 private interface Action { 273 LinearLayout create(Context context, LinearLayout convertView, LayoutInflater inflater); 274 275 void onPress(); 276 277 /** 278 * @return whether this action should appear in the dialog when the keygaurd 279 * is showing. 280 */ 281 boolean showDuringKeyguard(); 282 283 /** 284 * @return whether this action should appear in the dialog before the 285 * device is provisioned. 286 */ 287 boolean showBeforeProvisioning(); 288 } 289 290 /** 291 * A single press action maintains no state, just responds to a press 292 * and takes an action. 293 */ 294 private static abstract class SinglePressAction implements Action { 295 private final int mIconResId; 296 private final int mMessageResId; 297 298 protected SinglePressAction(int iconResId, int messageResId) { 299 mIconResId = iconResId; 300 mMessageResId = messageResId; 301 } 302 303 abstract public void onPress(); 304 305 public LinearLayout create(Context context, LinearLayout convertView, LayoutInflater inflater) { 306 LinearLayout v = (LinearLayout) ((convertView != null) ? 307 convertView : 308 inflater.inflate(R.layout.global_actions_item, null)); 309 310 ImageView icon = (ImageView) v.findViewById(R.id.icon); 311 TextView messageView = (TextView) v.findViewById(R.id.message); 312 313 v.findViewById(R.id.status).setVisibility(View.GONE); 314 315 icon.setImageDrawable(context.getResources().getDrawable(mIconResId)); 316 messageView.setText(mMessageResId); 317 318 return v; 319 } 320 } 321 322 /** 323 * A toggle action knows whether it is on or off, and displays an icon 324 * and status message accordingly. 325 */ 326 static abstract class ToggleAction implements Action { 327 328 private boolean mOn = false; 329 330 // prefs 331 private final int mEnabledIconResId; 332 private final int mDisabledIconResid; 333 private final int mMessageResId; 334 private final int mEnabledStatusMessageResId; 335 private final int mDisabledStatusMessageResId; 336 337 /** 338 * @param enabledIconResId The icon for when this action is on. 339 * @param disabledIconResid The icon for when this action is off. 340 * @param essage The general information message, e.g 'Silent Mode' 341 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled' 342 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled' 343 */ 344 public ToggleAction(int enabledIconResId, 345 int disabledIconResid, 346 int essage, 347 int enabledStatusMessageResId, 348 int disabledStatusMessageResId) { 349 mEnabledIconResId = enabledIconResId; 350 mDisabledIconResid = disabledIconResid; 351 mMessageResId = essage; 352 mEnabledStatusMessageResId = enabledStatusMessageResId; 353 mDisabledStatusMessageResId = disabledStatusMessageResId; 354 } 355 356 public LinearLayout create(Context context, LinearLayout convertView, 357 LayoutInflater inflater) { 358 LinearLayout v = (LinearLayout) ((convertView != null) ? 359 convertView : 360 inflater.inflate(R 361 .layout.global_actions_item, null)); 362 363 ImageView icon = (ImageView) v.findViewById(R.id.icon); 364 TextView messageView = (TextView) v.findViewById(R.id.message); 365 TextView statusView = (TextView) v.findViewById(R.id.status); 366 367 messageView.setText(mMessageResId); 368 369 icon.setImageDrawable(context.getResources().getDrawable( 370 (mOn ? mEnabledIconResId : mDisabledIconResid))); 371 statusView.setText(mOn ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); 372 statusView.setVisibility(View.VISIBLE); 373 374 return v; 375 } 376 377 public void onPress() { 378 updateState(!mOn); 379 onToggle(mOn); 380 } 381 382 abstract void onToggle(boolean on); 383 384 public void updateState(boolean on) { 385 mOn = on; 386 } 387 } 388 389 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 390 public void onReceive(Context context, Intent intent) { 391 String action = intent.getAction(); 392 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 393 String reason = intent.getStringExtra(MidWindowManager.SYSTEM_DIALOG_REASON_KEY); 394 if (! MidWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) { 395 mHandler.sendEmptyMessage(MESSAGE_DISMISS); 396 } 397 } 398 } 399 }; 400 401 private static final int MESSAGE_DISMISS = 0; 402 private Handler mHandler = new Handler() { 403 public void handleMessage(Message msg) { 404 if (msg.what == MESSAGE_DISMISS) { 405 if (mDialog != null) { 406 mDialog.dismiss(); 407 } 408 } 409 } 410 }; 411} 412