OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / policy / src / com / android / internal / policy / impl / GlobalActions.java
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 android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.StatusBarManager;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.media.AudioManager;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.SystemProperties;
31 import android.provider.Settings;
32 import android.telephony.PhoneStateListener;
33 import android.telephony.ServiceState;
34 import android.telephony.TelephonyManager;
35 import android.util.Log;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.WindowManager;
40 import android.widget.BaseAdapter;
41 import android.widget.ImageView;
42 import android.widget.TextView;
43 import com.android.internal.R;
44 import com.android.internal.app.ShutdownThread;
45 import com.android.internal.telephony.TelephonyIntents;
46 import com.android.internal.telephony.TelephonyProperties;
47 import com.google.android.collect.Lists;
48
49 import java.util.ArrayList;
50
51 /**
52  * Helper to show the global actions dialog.  Each item is an {@link Action} that
53  * may show depending on whether the keyguard is showing, and whether the device
54  * is provisioned.
55  */
56 class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener  {
57
58     private static final String TAG = "GlobalActions";
59
60     private StatusBarManager mStatusBar;
61
62     private final Context mContext;
63     private final AudioManager mAudioManager;
64
65     private ArrayList<Action> mItems;
66     private AlertDialog mDialog;
67
68     private ToggleAction mSilentModeToggle;
69     private ToggleAction mAirplaneModeOn;
70
71     private MyAdapter mAdapter;
72
73     private boolean mKeyguardShowing = false;
74     private boolean mDeviceProvisioned = false;
75     private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
76     private boolean mIsWaitingForEcmExit = false;
77
78     /**
79      * @param context everything needs a context :(
80      */
81     public GlobalActions(Context context) {
82         mContext = context;
83         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
84
85         // receive broadcasts
86         IntentFilter filter = new IntentFilter();
87         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
88         filter.addAction(Intent.ACTION_SCREEN_OFF);
89         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
90         context.registerReceiver(mBroadcastReceiver, filter);
91
92         // get notified of phone state changes
93         TelephonyManager telephonyManager =
94                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
95         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
96     }
97
98     /**
99      * Show the global actions dialog (creating if necessary)
100      * @param keyguardShowing True if keyguard is showing
101      */
102     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
103         mKeyguardShowing = keyguardShowing;
104         mDeviceProvisioned = isDeviceProvisioned;
105         if (mDialog == null) {
106             mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
107             mDialog = createDialog();
108         }
109         prepareDialog();
110
111         mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
112         mDialog.show();
113     }
114
115     /**
116      * Create the global actions dialog.
117      * @return A new dialog.
118      */
119     private AlertDialog createDialog() {
120         mSilentModeToggle = new ToggleAction(
121                 R.drawable.ic_lock_silent_mode,
122                 R.drawable.ic_lock_silent_mode_off,
123                 R.string.global_action_toggle_silent_mode,
124                 R.string.global_action_silent_mode_on_status,
125                 R.string.global_action_silent_mode_off_status) {
126
127             void willCreate() {
128                 // XXX: FIXME: switch to ic_lock_vibrate_mode when available
129                 mEnabledIconResId = (Settings.System.getInt(mContext.getContentResolver(),
130                         Settings.System.VIBRATE_IN_SILENT, 1) == 1)
131                     ? R.drawable.ic_lock_silent_mode_vibrate
132                     : R.drawable.ic_lock_silent_mode;
133             }
134
135             void onToggle(boolean on) {
136                 if (on) {
137                     mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
138                         Settings.System.VIBRATE_IN_SILENT, 1) == 1)
139                         ? AudioManager.RINGER_MODE_VIBRATE
140                         : AudioManager.RINGER_MODE_SILENT);
141                 } else {
142                     mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
143                 }
144             }
145
146             public boolean showDuringKeyguard() {
147                 return true;
148             }
149
150             public boolean showBeforeProvisioning() {
151                 return false;
152             }
153         };
154
155         mAirplaneModeOn = new ToggleAction(
156                 R.drawable.ic_lock_airplane_mode,
157                 R.drawable.ic_lock_airplane_mode_off,
158                 R.string.global_actions_toggle_airplane_mode,
159                 R.string.global_actions_airplane_mode_on_status,
160                 R.string.global_actions_airplane_mode_off_status) {
161
162             void onToggle(boolean on) {
163                 if (Boolean.parseBoolean(
164                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
165                     mIsWaitingForEcmExit = true;
166                     // Launch ECM exit dialog
167                     Intent ecmDialogIntent =
168                             new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
169                     ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
170                     mContext.startActivity(ecmDialogIntent);
171                 } else {
172                     changeAirplaneModeSystemSetting(on);
173                 }
174             }
175
176             @Override
177             protected void changeStateFromPress(boolean buttonOn) {
178                 // In ECM mode airplane state cannot be changed
179                 if (!(Boolean.parseBoolean(
180                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
181                     mState = buttonOn ? State.TurningOn : State.TurningOff;
182                     mAirplaneState = mState;
183                 }
184             }
185
186             public boolean showDuringKeyguard() {
187                 return true;
188             }
189
190             public boolean showBeforeProvisioning() {
191                 return false;
192             }
193         };
194
195         mItems = Lists.newArrayList(
196                 // silent mode
197                 mSilentModeToggle,
198                 // next: airplane mode
199                 mAirplaneModeOn,
200                 // last: power off
201                 new SinglePressAction(
202                         com.android.internal.R.drawable.ic_lock_power_off,
203                         R.string.global_action_power_off) {
204
205                     public void onPress() {
206                         // shutdown by making sure radio and power are handled accordingly.
207                         ShutdownThread.shutdown(mContext, true);
208                     }
209
210                     public boolean showDuringKeyguard() {
211                         return true;
212                     }
213
214                     public boolean showBeforeProvisioning() {
215                         return true;
216                     }
217                 });
218
219         mAdapter = new MyAdapter();
220
221         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
222
223         ab.setAdapter(mAdapter, this)
224                 .setInverseBackgroundForced(true);
225
226         final AlertDialog dialog = ab.create();
227         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
228         if (!mContext.getResources().getBoolean(
229                 com.android.internal.R.bool.config_sf_slowBlur)) {
230             dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
231                     WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
232         }
233
234         dialog.setOnDismissListener(this);
235
236         return dialog;
237     }
238
239     private void prepareDialog() {
240         final boolean silentModeOn =
241                 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
242         mSilentModeToggle.updateState(
243                 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
244         mAirplaneModeOn.updateState(mAirplaneState);
245         mAdapter.notifyDataSetChanged();
246         if (mKeyguardShowing) {
247             mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
248         } else {
249             mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
250         }
251         mDialog.setTitle(R.string.global_actions);
252     }
253
254
255     /** {@inheritDoc} */
256     public void onDismiss(DialogInterface dialog) {
257         mStatusBar.disable(StatusBarManager.DISABLE_NONE);
258     }
259
260     /** {@inheritDoc} */
261     public void onClick(DialogInterface dialog, int which) {
262         dialog.dismiss();
263         mAdapter.getItem(which).onPress();
264     }
265
266
267     /**
268      * The adapter used for the list within the global actions dialog, taking
269      * into account whether the keyguard is showing via
270      * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
271      * via {@link GlobalActions#mDeviceProvisioned}.
272      */
273     private class MyAdapter extends BaseAdapter {
274
275         public int getCount() {
276             int count = 0;
277
278             for (int i = 0; i < mItems.size(); i++) {
279                 final Action action = mItems.get(i);
280
281                 if (mKeyguardShowing && !action.showDuringKeyguard()) {
282                     continue;
283                 }
284                 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
285                     continue;
286                 }
287                 count++;
288             }
289             return count;
290         }
291
292         @Override
293         public boolean isEnabled(int position) {
294             return getItem(position).isEnabled();
295         }
296
297         @Override
298         public boolean areAllItemsEnabled() {
299             return false;
300         }
301
302         public Action getItem(int position) {
303
304             int filteredPos = 0;
305             for (int i = 0; i < mItems.size(); i++) {
306                 final Action action = mItems.get(i);
307                 if (mKeyguardShowing && !action.showDuringKeyguard()) {
308                     continue;
309                 }
310                 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
311                     continue;
312                 }
313                 if (filteredPos == position) {
314                     return action;
315                 }
316                 filteredPos++;
317             }
318
319             throw new IllegalArgumentException("position " + position + " out of "
320                     + "range of showable actions, filtered count = "
321                     + "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing
322                     + ", provisioned=" + mDeviceProvisioned);
323         }
324
325
326         public long getItemId(int position) {
327             return position;
328         }
329
330         public View getView(int position, View convertView, ViewGroup parent) {
331             Action action = getItem(position);
332             return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
333         }
334     }
335
336     // note: the scheme below made more sense when we were planning on having
337     // 8 different things in the global actions dialog.  seems overkill with
338     // only 3 items now, but may as well keep this flexible approach so it will
339     // be easy should someone decide at the last minute to include something
340     // else, such as 'enable wifi', or 'enable bluetooth'
341
342     /**
343      * What each item in the global actions dialog must be able to support.
344      */
345     private interface Action {
346         View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
347
348         void onPress();
349
350         /**
351          * @return whether this action should appear in the dialog when the keygaurd
352          *    is showing.
353          */
354         boolean showDuringKeyguard();
355
356         /**
357          * @return whether this action should appear in the dialog before the
358          *   device is provisioned.
359          */
360         boolean showBeforeProvisioning();
361
362         boolean isEnabled();
363     }
364
365     /**
366      * A single press action maintains no state, just responds to a press
367      * and takes an action.
368      */
369     private static abstract class SinglePressAction implements Action {
370         private final int mIconResId;
371         private final int mMessageResId;
372
373         protected SinglePressAction(int iconResId, int messageResId) {
374             mIconResId = iconResId;
375             mMessageResId = messageResId;
376         }
377
378         public boolean isEnabled() {
379             return true;
380         }
381
382         abstract public void onPress();
383
384         public View create(
385                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
386             View v = (convertView != null) ?
387                     convertView :
388                     inflater.inflate(R.layout.global_actions_item, parent, false);
389
390             ImageView icon = (ImageView) v.findViewById(R.id.icon);
391             TextView messageView = (TextView) v.findViewById(R.id.message);
392
393             v.findViewById(R.id.status).setVisibility(View.GONE);
394
395             icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
396             messageView.setText(mMessageResId);
397
398             return v;
399         }
400     }
401
402     /**
403      * A toggle action knows whether it is on or off, and displays an icon
404      * and status message accordingly.
405      */
406     private static abstract class ToggleAction implements Action {
407
408         enum State {
409             Off(false),
410             TurningOn(true),
411             TurningOff(true),
412             On(false);
413
414             private final boolean inTransition;
415
416             State(boolean intermediate) {
417                 inTransition = intermediate;
418             }
419
420             public boolean inTransition() {
421                 return inTransition;
422             }
423         }
424
425         protected State mState = State.Off;
426
427         // prefs
428         protected int mEnabledIconResId;
429         protected int mDisabledIconResid;
430         protected int mMessageResId;
431         protected int mEnabledStatusMessageResId;
432         protected int mDisabledStatusMessageResId;
433
434         /**
435          * @param enabledIconResId The icon for when this action is on.
436          * @param disabledIconResid The icon for when this action is off.
437          * @param essage The general information message, e.g 'Silent Mode'
438          * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
439          * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
440          */
441         public ToggleAction(int enabledIconResId,
442                 int disabledIconResid,
443                 int essage,
444                 int enabledStatusMessageResId,
445                 int disabledStatusMessageResId) {
446             mEnabledIconResId = enabledIconResId;
447             mDisabledIconResid = disabledIconResid;
448             mMessageResId = essage;
449             mEnabledStatusMessageResId = enabledStatusMessageResId;
450             mDisabledStatusMessageResId = disabledStatusMessageResId;
451         }
452
453         /**
454          * Override to make changes to resource IDs just before creating the
455          * View.
456          */
457         void willCreate() {
458
459         }
460
461         public View create(Context context, View convertView, ViewGroup parent,
462                 LayoutInflater inflater) {
463             willCreate();
464
465             View v = (convertView != null) ?
466                     convertView :
467                     inflater.inflate(R
468                             .layout.global_actions_item, parent, false);
469
470             ImageView icon = (ImageView) v.findViewById(R.id.icon);
471             TextView messageView = (TextView) v.findViewById(R.id.message);
472             TextView statusView = (TextView) v.findViewById(R.id.status);
473
474             messageView.setText(mMessageResId);
475
476             boolean on = ((mState == State.On) || (mState == State.TurningOn));
477             icon.setImageDrawable(context.getResources().getDrawable(
478                     (on ? mEnabledIconResId : mDisabledIconResid)));
479             statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
480             statusView.setVisibility(View.VISIBLE);
481
482             final boolean enabled = isEnabled();
483             messageView.setEnabled(enabled);
484             statusView.setEnabled(enabled);
485             icon.setEnabled(enabled);
486             v.setEnabled(enabled);
487
488             return v;
489         }
490
491         public final void onPress() {
492             if (mState.inTransition()) {
493                 Log.w(TAG, "shouldn't be able to toggle when in transition");
494                 return;
495             }
496
497             final boolean nowOn = !(mState == State.On);
498             onToggle(nowOn);
499             changeStateFromPress(nowOn);
500         }
501
502         public boolean isEnabled() {
503             return !mState.inTransition();
504         }
505
506         /**
507          * Implementations may override this if their state can be in on of the intermediate
508          * states until some notification is received (e.g airplane mode is 'turning off' until
509          * we know the wireless connections are back online
510          * @param buttonOn Whether the button was turned on or off
511          */
512         protected void changeStateFromPress(boolean buttonOn) {
513             mState = buttonOn ? State.On : State.Off;
514         }
515
516         abstract void onToggle(boolean on);
517
518         public void updateState(State state) {
519             mState = state;
520         }
521     }
522
523     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
524         public void onReceive(Context context, Intent intent) {
525             String action = intent.getAction();
526             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
527                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
528                 String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
529                 if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
530                     mHandler.sendEmptyMessage(MESSAGE_DISMISS);
531                 }
532             } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
533                 // Airplane mode can be changed after ECM exits if airplane toggle button
534                 // is pressed during ECM mode
535                 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
536                         mIsWaitingForEcmExit) {
537                     mIsWaitingForEcmExit = false;
538                     changeAirplaneModeSystemSetting(true);
539                 }
540             }
541         }
542     };
543
544     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
545         @Override
546         public void onServiceStateChanged(ServiceState serviceState) {
547             final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
548             mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
549             mAirplaneModeOn.updateState(mAirplaneState);
550             mAdapter.notifyDataSetChanged();
551         }
552     };
553
554     private static final int MESSAGE_DISMISS = 0;
555     private Handler mHandler = new Handler() {
556         public void handleMessage(Message msg) {
557             if (msg.what == MESSAGE_DISMISS) {
558                 if (mDialog != null) {
559                     mDialog.dismiss();
560                 }
561             }
562         }
563     };
564
565     /**
566      * Change the airplane mode system setting
567      */
568     private void changeAirplaneModeSystemSetting(boolean on) {
569         Settings.System.putInt(
570                 mContext.getContentResolver(),
571                 Settings.System.AIRPLANE_MODE_ON,
572                 on ? 1 : 0);
573         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
574         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
575         intent.putExtra("state", on);
576         mContext.sendBroadcast(intent);
577     }
578 }