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.telephony.TelephonyIntents;
20import com.android.internal.telephony.TelephonyProperties;
21import com.android.internal.R;
22
23import android.app.ActivityManagerNative;
24import android.app.AlertDialog;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.UserInfo;
31import android.database.ContentObserver;
32import android.media.AudioManager;
33import android.net.ConnectivityManager;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Message;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemProperties;
40import android.os.Vibrator;
41import android.provider.Settings;
42import android.telephony.PhoneStateListener;
43import android.telephony.ServiceState;
44import android.telephony.TelephonyManager;
45import android.util.Log;
46import android.view.IWindowManager;
47import android.view.LayoutInflater;
48import android.view.View;
49import android.view.ViewGroup;
50import android.view.WindowManager;
51import android.view.WindowManagerPolicy.WindowManagerFuncs;
52import android.widget.AdapterView;
53import android.widget.BaseAdapter;
54import android.widget.ImageView;
55import android.widget.TextView;
56
57import java.util.ArrayList;
58import java.util.List;
59
60/**
61 * Helper to show the global actions dialog.  Each item is an {@link Action} that
62 * may show depending on whether the keyguard is showing, and whether the device
63 * is provisioned.
64 */
65class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener  {
66
67    private static final String TAG = "GlobalActions";
68
69    private static final boolean SHOW_SILENT_TOGGLE = true;
70
71    private final Context mContext;
72    private final WindowManagerFuncs mWindowManagerFuncs;
73    private final AudioManager mAudioManager;
74
75    private ArrayList<Action> mItems;
76    private AlertDialog mDialog;
77
78    private Action mSilentModeAction;
79    private ToggleAction mAirplaneModeOn;
80
81    private MyAdapter mAdapter;
82
83    private boolean mKeyguardShowing = false;
84    private boolean mDeviceProvisioned = false;
85    private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
86    private boolean mIsWaitingForEcmExit = false;
87    private boolean mHasTelephony;
88    private boolean mHasVibrator;
89
90    private IWindowManager mIWindowManager;
91
92    /**
93     * @param context everything needs a context :(
94     */
95    public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
96        mContext = context;
97        mWindowManagerFuncs = windowManagerFuncs;
98        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
99
100        // receive broadcasts
101        IntentFilter filter = new IntentFilter();
102        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
103        filter.addAction(Intent.ACTION_SCREEN_OFF);
104        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
105        context.registerReceiver(mBroadcastReceiver, filter);
106
107        // get notified of phone state changes
108        TelephonyManager telephonyManager =
109                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
110        telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
111        ConnectivityManager cm = (ConnectivityManager)
112                context.getSystemService(Context.CONNECTIVITY_SERVICE);
113        mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
114        mContext.getContentResolver().registerContentObserver(
115                Settings.System.getUriFor(Settings.System.AIRPLANE_MODE_ON), true,
116                mAirplaneModeObserver);
117        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
118        mHasVibrator = vibrator != null && vibrator.hasVibrator();
119    }
120
121    /**
122     * Show the global actions dialog (creating if necessary)
123     * @param keyguardShowing True if keyguard is showing
124     */
125    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
126        mKeyguardShowing = keyguardShowing;
127        mDeviceProvisioned = isDeviceProvisioned;
128        if (mDialog != null) {
129            mDialog.dismiss();
130            mDialog = null;
131            // Show delayed, so that the dismiss of the previous dialog completes
132            mHandler.sendEmptyMessage(MESSAGE_SHOW);
133        } else {
134            handleShow();
135        }
136    }
137
138    private void handleShow() {
139        mDialog = createDialog();
140        prepareDialog();
141
142        mDialog.show();
143        mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
144    }
145
146    /**
147     * Create the global actions dialog.
148     * @return A new dialog.
149     */
150    private AlertDialog createDialog() {
151        // Simple toggle style if there's no vibrator, otherwise use a tri-state
152        if (!mHasVibrator) {
153            mSilentModeAction = new SilentModeToggleAction();
154        } else {
155            mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
156        }
157        mAirplaneModeOn = new ToggleAction(
158                R.drawable.ic_lock_airplane_mode,
159                R.drawable.ic_lock_airplane_mode_off,
160                R.string.global_actions_toggle_airplane_mode,
161                R.string.global_actions_airplane_mode_on_status,
162                R.string.global_actions_airplane_mode_off_status) {
163
164            void onToggle(boolean on) {
165                if (mHasTelephony && Boolean.parseBoolean(
166                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
167                    mIsWaitingForEcmExit = true;
168                    // Launch ECM exit dialog
169                    Intent ecmDialogIntent =
170                            new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
171                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
172                    mContext.startActivity(ecmDialogIntent);
173                } else {
174                    changeAirplaneModeSystemSetting(on);
175                }
176            }
177
178            @Override
179            protected void changeStateFromPress(boolean buttonOn) {
180                if (!mHasTelephony) return;
181
182                // In ECM mode airplane state cannot be changed
183                if (!(Boolean.parseBoolean(
184                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
185                    mState = buttonOn ? State.TurningOn : State.TurningOff;
186                    mAirplaneState = mState;
187                }
188            }
189
190            public boolean showDuringKeyguard() {
191                return true;
192            }
193
194            public boolean showBeforeProvisioning() {
195                return false;
196            }
197        };
198        onAirplaneModeChanged();
199
200        mItems = new ArrayList<Action>();
201
202        // first: power off
203        mItems.add(
204            new SinglePressAction(
205                    com.android.internal.R.drawable.ic_lock_power_off,
206                    R.string.global_action_power_off) {
207
208                public void onPress() {
209                    // shutdown by making sure radio and power are handled accordingly.
210                    mWindowManagerFuncs.shutdown();
211                }
212
213                public boolean onLongPress() {
214                    mWindowManagerFuncs.rebootSafeMode();
215                    return true;
216                }
217
218                public boolean showDuringKeyguard() {
219                    return true;
220                }
221
222                public boolean showBeforeProvisioning() {
223                    return true;
224                }
225            });
226
227        // next: airplane mode
228        mItems.add(mAirplaneModeOn);
229
230        // last: silent mode
231        if (SHOW_SILENT_TOGGLE) {
232            mItems.add(mSilentModeAction);
233        }
234
235        List<UserInfo> users = mContext.getPackageManager().getUsers();
236        if (users.size() > 1) {
237            UserInfo currentUser;
238            try {
239                currentUser = ActivityManagerNative.getDefault().getCurrentUser();
240            } catch (RemoteException re) {
241                currentUser = null;
242            }
243            for (final UserInfo user : users) {
244                boolean isCurrentUser = currentUser == null
245                        ? user.id == 0 : (currentUser.id == user.id);
246                SinglePressAction switchToUser = new SinglePressAction(
247                        com.android.internal.R.drawable.ic_menu_cc,
248                        (user.name != null ? user.name : "Primary")
249                        + (isCurrentUser ? " \u2714" : "")) {
250                    public void onPress() {
251                        try {
252                            ActivityManagerNative.getDefault().switchUser(user.id);
253                            getWindowManager().lockNow();
254                        } catch (RemoteException re) {
255                            Log.e(TAG, "Couldn't switch user " + re);
256                        }
257                    }
258
259                    public boolean showDuringKeyguard() {
260                        return true;
261                    }
262
263                    public boolean showBeforeProvisioning() {
264                        return false;
265                    }
266                };
267                mItems.add(switchToUser);
268            }
269        }
270
271        mAdapter = new MyAdapter();
272
273        final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
274
275        ab.setAdapter(mAdapter, this)
276                .setInverseBackgroundForced(true);
277
278        final AlertDialog dialog = ab.create();
279        dialog.getListView().setItemsCanFocus(true);
280        dialog.getListView().setLongClickable(true);
281        dialog.getListView().setOnItemLongClickListener(
282                new AdapterView.OnItemLongClickListener() {
283                    @Override
284                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
285                            long id) {
286                        return mAdapter.getItem(position).onLongPress();
287                    }
288        });
289        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
290
291        dialog.setOnDismissListener(this);
292
293        return dialog;
294    }
295
296    private void prepareDialog() {
297        refreshSilentMode();
298        mAirplaneModeOn.updateState(mAirplaneState);
299        mAdapter.notifyDataSetChanged();
300        if (mKeyguardShowing) {
301            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
302        } else {
303            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
304        }
305        if (SHOW_SILENT_TOGGLE) {
306            IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
307            mContext.registerReceiver(mRingerModeReceiver, filter);
308        }
309    }
310
311    private void refreshSilentMode() {
312        if (!mHasVibrator) {
313            final boolean silentModeOn =
314                    mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
315            ((ToggleAction)mSilentModeAction).updateState(
316                    silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
317        }
318    }
319
320    /** {@inheritDoc} */
321    public void onDismiss(DialogInterface dialog) {
322        if (SHOW_SILENT_TOGGLE) {
323            mContext.unregisterReceiver(mRingerModeReceiver);
324        }
325    }
326
327    /** {@inheritDoc} */
328    public void onClick(DialogInterface dialog, int which) {
329        if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
330            dialog.dismiss();
331        }
332        mAdapter.getItem(which).onPress();
333    }
334
335    /**
336     * The adapter used for the list within the global actions dialog, taking
337     * into account whether the keyguard is showing via
338     * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
339     * via {@link GlobalActions#mDeviceProvisioned}.
340     */
341    private class MyAdapter extends BaseAdapter {
342
343        public int getCount() {
344            int count = 0;
345
346            for (int i = 0; i < mItems.size(); i++) {
347                final Action action = mItems.get(i);
348
349                if (mKeyguardShowing && !action.showDuringKeyguard()) {
350                    continue;
351                }
352                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
353                    continue;
354                }
355                count++;
356            }
357            return count;
358        }
359
360        @Override
361        public boolean isEnabled(int position) {
362            return getItem(position).isEnabled();
363        }
364
365        @Override
366        public boolean areAllItemsEnabled() {
367            return false;
368        }
369
370        public Action getItem(int position) {
371
372            int filteredPos = 0;
373            for (int i = 0; i < mItems.size(); i++) {
374                final Action action = mItems.get(i);
375                if (mKeyguardShowing && !action.showDuringKeyguard()) {
376                    continue;
377                }
378                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
379                    continue;
380                }
381                if (filteredPos == position) {
382                    return action;
383                }
384                filteredPos++;
385            }
386
387            throw new IllegalArgumentException("position " + position
388                    + " out of range of showable actions"
389                    + ", filtered count=" + getCount()
390                    + ", keyguardshowing=" + mKeyguardShowing
391                    + ", provisioned=" + mDeviceProvisioned);
392        }
393
394
395        public long getItemId(int position) {
396            return position;
397        }
398
399        public View getView(int position, View convertView, ViewGroup parent) {
400            Action action = getItem(position);
401            return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
402        }
403    }
404
405    // note: the scheme below made more sense when we were planning on having
406    // 8 different things in the global actions dialog.  seems overkill with
407    // only 3 items now, but may as well keep this flexible approach so it will
408    // be easy should someone decide at the last minute to include something
409    // else, such as 'enable wifi', or 'enable bluetooth'
410
411    /**
412     * What each item in the global actions dialog must be able to support.
413     */
414    private interface Action {
415        View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
416
417        void onPress();
418
419        public boolean onLongPress();
420
421        /**
422         * @return whether this action should appear in the dialog when the keygaurd
423         *    is showing.
424         */
425        boolean showDuringKeyguard();
426
427        /**
428         * @return whether this action should appear in the dialog before the
429         *   device is provisioned.
430         */
431        boolean showBeforeProvisioning();
432
433        boolean isEnabled();
434    }
435
436    /**
437     * A single press action maintains no state, just responds to a press
438     * and takes an action.
439     */
440    private static abstract class SinglePressAction implements Action {
441        private final int mIconResId;
442        private final int mMessageResId;
443        private final CharSequence mMessage;
444
445        protected SinglePressAction(int iconResId, int messageResId) {
446            mIconResId = iconResId;
447            mMessageResId = messageResId;
448            mMessage = null;
449        }
450
451        protected SinglePressAction(int iconResId, CharSequence message) {
452            mIconResId = iconResId;
453            mMessageResId = 0;
454            mMessage = message;
455        }
456        public boolean isEnabled() {
457            return true;
458        }
459
460        abstract public void onPress();
461
462        public boolean onLongPress() {
463            return false;
464        }
465
466        public View create(
467                Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
468            View v = inflater.inflate(R.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
473            v.findViewById(R.id.status).setVisibility(View.GONE);
474
475            icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
476            if (mMessage != null) {
477                messageView.setText(mMessage);
478            } else {
479                messageView.setText(mMessageResId);
480            }
481
482            return v;
483        }
484    }
485
486    /**
487     * A toggle action knows whether it is on or off, and displays an icon
488     * and status message accordingly.
489     */
490    private static abstract class ToggleAction implements Action {
491
492        enum State {
493            Off(false),
494            TurningOn(true),
495            TurningOff(true),
496            On(false);
497
498            private final boolean inTransition;
499
500            State(boolean intermediate) {
501                inTransition = intermediate;
502            }
503
504            public boolean inTransition() {
505                return inTransition;
506            }
507        }
508
509        protected State mState = State.Off;
510
511        // prefs
512        protected int mEnabledIconResId;
513        protected int mDisabledIconResid;
514        protected int mMessageResId;
515        protected int mEnabledStatusMessageResId;
516        protected int mDisabledStatusMessageResId;
517
518        /**
519         * @param enabledIconResId The icon for when this action is on.
520         * @param disabledIconResid The icon for when this action is off.
521         * @param essage The general information message, e.g 'Silent Mode'
522         * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
523         * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
524         */
525        public ToggleAction(int enabledIconResId,
526                int disabledIconResid,
527                int message,
528                int enabledStatusMessageResId,
529                int disabledStatusMessageResId) {
530            mEnabledIconResId = enabledIconResId;
531            mDisabledIconResid = disabledIconResid;
532            mMessageResId = message;
533            mEnabledStatusMessageResId = enabledStatusMessageResId;
534            mDisabledStatusMessageResId = disabledStatusMessageResId;
535        }
536
537        /**
538         * Override to make changes to resource IDs just before creating the
539         * View.
540         */
541        void willCreate() {
542
543        }
544
545        public View create(Context context, View convertView, ViewGroup parent,
546                LayoutInflater inflater) {
547            willCreate();
548
549            View v = inflater.inflate(R
550                            .layout.global_actions_item, parent, false);
551
552            ImageView icon = (ImageView) v.findViewById(R.id.icon);
553            TextView messageView = (TextView) v.findViewById(R.id.message);
554            TextView statusView = (TextView) v.findViewById(R.id.status);
555            final boolean enabled = isEnabled();
556
557            if (messageView != null) {
558                messageView.setText(mMessageResId);
559                messageView.setEnabled(enabled);
560            }
561
562            boolean on = ((mState == State.On) || (mState == State.TurningOn));
563            if (icon != null) {
564                icon.setImageDrawable(context.getResources().getDrawable(
565                        (on ? mEnabledIconResId : mDisabledIconResid)));
566                icon.setEnabled(enabled);
567            }
568
569            if (statusView != null) {
570                statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
571                statusView.setVisibility(View.VISIBLE);
572                statusView.setEnabled(enabled);
573            }
574            v.setEnabled(enabled);
575
576            return v;
577        }
578
579        public final void onPress() {
580            if (mState.inTransition()) {
581                Log.w(TAG, "shouldn't be able to toggle when in transition");
582                return;
583            }
584
585            final boolean nowOn = !(mState == State.On);
586            onToggle(nowOn);
587            changeStateFromPress(nowOn);
588        }
589
590        public boolean onLongPress() {
591            return false;
592        }
593
594        public boolean isEnabled() {
595            return !mState.inTransition();
596        }
597
598        /**
599         * Implementations may override this if their state can be in on of the intermediate
600         * states until some notification is received (e.g airplane mode is 'turning off' until
601         * we know the wireless connections are back online
602         * @param buttonOn Whether the button was turned on or off
603         */
604        protected void changeStateFromPress(boolean buttonOn) {
605            mState = buttonOn ? State.On : State.Off;
606        }
607
608        abstract void onToggle(boolean on);
609
610        public void updateState(State state) {
611            mState = state;
612        }
613    }
614
615    private class SilentModeToggleAction extends ToggleAction {
616        public SilentModeToggleAction() {
617            super(R.drawable.ic_audio_vol_mute,
618                    R.drawable.ic_audio_vol,
619                    R.string.global_action_toggle_silent_mode,
620                    R.string.global_action_silent_mode_on_status,
621                    R.string.global_action_silent_mode_off_status);
622        }
623
624        void onToggle(boolean on) {
625            if (on) {
626                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
627            } else {
628                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
629            }
630        }
631
632        public boolean showDuringKeyguard() {
633            return true;
634        }
635
636        public boolean showBeforeProvisioning() {
637            return false;
638        }
639    }
640
641    private static class SilentModeTriStateAction implements Action, View.OnClickListener {
642
643        private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
644
645        private final AudioManager mAudioManager;
646        private final Handler mHandler;
647        private final Context mContext;
648
649        SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
650            mAudioManager = audioManager;
651            mHandler = handler;
652            mContext = context;
653        }
654
655        private int ringerModeToIndex(int ringerMode) {
656            // They just happen to coincide
657            return ringerMode;
658        }
659
660        private int indexToRingerMode(int index) {
661            // They just happen to coincide
662            return index;
663        }
664
665        public View create(Context context, View convertView, ViewGroup parent,
666                LayoutInflater inflater) {
667            View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
668
669            int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
670            for (int i = 0; i < 3; i++) {
671                View itemView = v.findViewById(ITEM_IDS[i]);
672                itemView.setSelected(selectedIndex == i);
673                // Set up click handler
674                itemView.setTag(i);
675                itemView.setOnClickListener(this);
676            }
677            return v;
678        }
679
680        public void onPress() {
681        }
682
683        public boolean onLongPress() {
684            return false;
685        }
686
687        public boolean showDuringKeyguard() {
688            return true;
689        }
690
691        public boolean showBeforeProvisioning() {
692            return false;
693        }
694
695        public boolean isEnabled() {
696            return true;
697        }
698
699        void willCreate() {
700        }
701
702        public void onClick(View v) {
703            if (!(v.getTag() instanceof Integer)) return;
704
705            int index = (Integer) v.getTag();
706            mAudioManager.setRingerMode(indexToRingerMode(index));
707            mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
708        }
709    }
710
711    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
712        public void onReceive(Context context, Intent intent) {
713            String action = intent.getAction();
714            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
715                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
716                String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
717                if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
718                    mHandler.sendEmptyMessage(MESSAGE_DISMISS);
719                }
720            } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
721                // Airplane mode can be changed after ECM exits if airplane toggle button
722                // is pressed during ECM mode
723                if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
724                        mIsWaitingForEcmExit) {
725                    mIsWaitingForEcmExit = false;
726                    changeAirplaneModeSystemSetting(true);
727                }
728            }
729        }
730    };
731
732    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
733        @Override
734        public void onServiceStateChanged(ServiceState serviceState) {
735            if (!mHasTelephony) return;
736            final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
737            mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
738            mAirplaneModeOn.updateState(mAirplaneState);
739            mAdapter.notifyDataSetChanged();
740        }
741    };
742
743    private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
744        @Override
745        public void onReceive(Context context, Intent intent) {
746            if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
747                mHandler.sendEmptyMessage(MESSAGE_REFRESH);
748            }
749        }
750    };
751
752    private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
753        @Override
754        public void onChange(boolean selfChange) {
755            onAirplaneModeChanged();
756        }
757    };
758
759    private static final int MESSAGE_DISMISS = 0;
760    private static final int MESSAGE_REFRESH = 1;
761    private static final int MESSAGE_SHOW = 2;
762    private static final int DIALOG_DISMISS_DELAY = 300; // ms
763
764    private Handler mHandler = new Handler() {
765        public void handleMessage(Message msg) {
766            switch (msg.what) {
767            case MESSAGE_DISMISS:
768                if (mDialog != null) {
769                    mDialog.dismiss();
770                }
771                break;
772            case MESSAGE_REFRESH:
773                refreshSilentMode();
774                mAdapter.notifyDataSetChanged();
775                break;
776            case MESSAGE_SHOW:
777                handleShow();
778                break;
779            }
780        }
781    };
782
783    private void onAirplaneModeChanged() {
784        // Let the service state callbacks handle the state.
785        if (mHasTelephony) return;
786
787        boolean airplaneModeOn = Settings.System.getInt(
788                mContext.getContentResolver(),
789                Settings.System.AIRPLANE_MODE_ON,
790                0) == 1;
791        mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
792        mAirplaneModeOn.updateState(mAirplaneState);
793    }
794
795    /**
796     * Change the airplane mode system setting
797     */
798    private void changeAirplaneModeSystemSetting(boolean on) {
799        Settings.System.putInt(
800                mContext.getContentResolver(),
801                Settings.System.AIRPLANE_MODE_ON,
802                on ? 1 : 0);
803        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
804        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
805        intent.putExtra("state", on);
806        mContext.sendBroadcast(intent);
807        if (!mHasTelephony) {
808            mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
809        }
810    }
811
812    private IWindowManager getWindowManager() {
813        if (mIWindowManager == null) {
814            IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
815            mIWindowManager = IWindowManager.Stub.asInterface(b);
816        }
817        return mIWindowManager;
818    }
819}
820