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