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