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