Home | History | Annotate | Download | only in impl
      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