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