GlobalActions.java revision c5d93b3b6a0ba4831903f8e8d1664c4470cf15d7
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.R;
20import com.google.android.collect.Lists;
21
22import android.app.AlertDialog;
23import android.app.StatusBarManager;
24import android.content.Context;
25import android.content.BroadcastReceiver;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.DialogInterface;
29import android.media.AudioManager;
30import android.os.LocalPowerManager;
31import android.os.Handler;
32import android.os.Message;
33import android.os.SystemClock;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.view.WindowManager;
38import android.widget.BaseAdapter;
39import android.widget.ImageView;
40import android.widget.LinearLayout;
41import android.widget.TextView;
42
43import java.util.ArrayList;
44
45/**
46 * Helper to show the global actions dialog.  Each item is an {@link Action} that
47 * may show depending on whether the keyguard is showing, and whether the device
48 * is provisioned.
49 */
50class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener  {
51
52    private StatusBarManager mStatusBar;
53
54    private final Context mContext;
55    private final LocalPowerManager mPowerManager;
56    private final AudioManager mAudioManager;
57    private ArrayList<Action> mItems;
58    private AlertDialog mDialog;
59
60    private ToggleAction mSilentModeToggle;
61
62    private MyAdapter mAdapter;
63
64    private boolean mKeyguardShowing = false;
65    private boolean mDeviceProvisioned = false;
66
67    /**
68     * @param context everything needs a context :)
69     * @param powerManager used to turn the screen off (the lock action).
70     */
71    public GlobalActions(Context context, LocalPowerManager powerManager) {
72        mContext = context;
73        mPowerManager = powerManager;
74        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
75
76        // receive broadcasts
77        IntentFilter filter = new IntentFilter();
78        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
79        context.registerReceiver(mBroadcastReceiver, filter);
80    }
81
82    /**
83     * Show the global actions dialog (creating if necessary)
84     * @param keyguardShowing True if keyguard is showing
85     */
86    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
87        mKeyguardShowing = keyguardShowing;
88        mDeviceProvisioned = isDeviceProvisioned;
89        if (mDialog == null) {
90            mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
91            mDialog = createDialog();
92        }
93        prepareDialog();
94
95        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
96        mDialog.show();
97    }
98
99    /**
100     * Create the global actions dialog.
101     * @return A new dialog.
102     */
103    private AlertDialog createDialog() {
104
105        mSilentModeToggle = new ToggleAction(
106                R.drawable.ic_lock_silent_mode,
107                R.drawable.ic_lock_silent_mode_off,
108                R.string.global_action_toggle_silent_mode,
109                R.string.global_action_silent_mode_on_status,
110                R.string.global_action_silent_mode_off_status) {
111
112            void onToggle(boolean on) {
113                mAudioManager.setRingerMode(on ? AudioManager.RINGER_MODE_SILENT
114                        : AudioManager.RINGER_MODE_NORMAL);
115            }
116
117            public boolean showDuringKeyguard() {
118                return true;
119            }
120
121            public boolean showBeforeProvisioning() {
122                return false;
123            }
124        };
125
126        mItems = Lists.newArrayList(
127                // first: lock screen
128                new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock, R.string.global_action_lock) {
129
130                    public void onPress() {
131                        mPowerManager.goToSleep(SystemClock.uptimeMillis() + 1);
132                    }
133
134                    public boolean showDuringKeyguard() {
135                        return false;
136                    }
137
138                    public boolean showBeforeProvisioning() {
139                        return false;
140                    }
141                },
142                // next: silent mode
143                mSilentModeToggle,
144                // last: power off
145                new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off, R.string.global_action_power_off) {
146
147                    public void onPress() {
148                        // shutdown by making sure radio and power are handled accordingly.
149                        ShutdownThread.shutdownAfterDisablingRadio(mContext, true);
150                    }
151
152                    public boolean showDuringKeyguard() {
153                        return true;
154                    }
155
156                    public boolean showBeforeProvisioning() {
157                        return true;
158                    }
159                });
160
161        mAdapter = new MyAdapter();
162
163        final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
164
165        ab.setAdapter(mAdapter, this)
166                .setInverseBackgroundForced(true)
167                .setTitle(R.string.global_actions);
168
169        final AlertDialog dialog = ab.create();
170        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
171        dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
172                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
173
174        dialog.setOnDismissListener(this);
175
176        return dialog;
177    }
178
179    private void prepareDialog() {
180        // TODO: May need another 'Vibrate' toggle button, but for now treat them the same
181        final boolean silentModeOn =
182                mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
183        mSilentModeToggle.updateState(silentModeOn);
184        mAdapter.notifyDataSetChanged();
185        if (mKeyguardShowing) {
186            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
187        } else {
188            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
189        }
190    }
191
192    /** {@inheritDoc} */
193    public void onDismiss(DialogInterface dialog) {
194        mStatusBar.disable(StatusBarManager.DISABLE_NONE);
195    }
196
197    /** {@inheritDoc} */
198    public void onClick(DialogInterface dialog, int which) {
199        dialog.dismiss();
200        mAdapter.getItem(which).onPress();
201    }
202
203
204    /**
205     * The adapter used for the list within the global actions dialog, taking
206     * into account whether the keyguard is showing via
207     * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
208     * via {@link GlobalActions#mDeviceProvisioned}.
209     */
210    private class MyAdapter extends BaseAdapter {
211
212        public int getCount() {
213            int count = 0;
214
215            for (int i = 0; i < mItems.size(); i++) {
216                final Action action = mItems.get(i);
217
218                if (mKeyguardShowing && !action.showDuringKeyguard()) {
219                    continue;
220                }
221                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
222                    continue;
223                }
224                count++;
225            }
226            return count;
227        }
228
229        public Action getItem(int position) {
230
231            int filteredPos = 0;
232            for (int i = 0; i < mItems.size(); i++) {
233                final Action action = mItems.get(i);
234                if (mKeyguardShowing && !action.showDuringKeyguard()) {
235                    continue;
236                }
237                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
238                    continue;
239                }
240                if (filteredPos == position) {
241                    return action;
242                }
243                filteredPos++;
244            }
245
246            throw new IllegalArgumentException("position " + position + " out of "
247                    + "range of showable actions, filtered count = "
248                    + "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing
249                    + ", provisioned=" + mDeviceProvisioned);
250        }
251
252
253        public long getItemId(int position) {
254            return position;
255        }
256
257        public View getView(int position, View convertView, ViewGroup parent) {
258            Action action = getItem(position);
259            return action.create(mContext, (LinearLayout) convertView, LayoutInflater.from(mContext));
260        }
261    }
262
263    // note: the scheme below made more sense when we were planning on having
264    // 8 different things in the global actions dialog.  seems overkill with
265    // only 3 items now, but may as well keep this flexible approach so it will
266    // be easy should someone decide at the last minute to include something
267    // else, such as 'enable wifi', or 'enable bluetooth'
268
269    /**
270     * What each item in the global actions dialog must be able to support.
271     */
272    private interface Action {
273        LinearLayout create(Context context, LinearLayout convertView, LayoutInflater inflater);
274
275        void onPress();
276
277        /**
278         * @return whether this action should appear in the dialog when the keygaurd
279         *    is showing.
280         */
281        boolean showDuringKeyguard();
282
283        /**
284         * @return whether this action should appear in the dialog before the
285         *   device is provisioned.
286         */
287        boolean showBeforeProvisioning();
288    }
289
290    /**
291     * A single press action maintains no state, just responds to a press
292     * and takes an action.
293     */
294    private static abstract class SinglePressAction implements Action {
295        private final int mIconResId;
296        private final int mMessageResId;
297
298        protected SinglePressAction(int iconResId, int messageResId) {
299            mIconResId = iconResId;
300            mMessageResId = messageResId;
301        }
302
303        abstract public void onPress();
304
305        public LinearLayout create(Context context, LinearLayout convertView, LayoutInflater inflater) {
306            LinearLayout v = (LinearLayout) ((convertView != null) ?
307                    convertView :
308                    inflater.inflate(R.layout.global_actions_item, null));
309
310            ImageView icon = (ImageView) v.findViewById(R.id.icon);
311            TextView messageView = (TextView) v.findViewById(R.id.message);
312
313            v.findViewById(R.id.status).setVisibility(View.GONE);
314
315            icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
316            messageView.setText(mMessageResId);
317
318            return v;
319        }
320    }
321
322    /**
323     * A toggle action knows whether it is on or off, and displays an icon
324     * and status message accordingly.
325     */
326    static abstract class ToggleAction implements Action {
327
328        private boolean mOn = false;
329
330        // prefs
331        private final int mEnabledIconResId;
332        private final int mDisabledIconResid;
333        private final int mMessageResId;
334        private final int mEnabledStatusMessageResId;
335        private final int mDisabledStatusMessageResId;
336
337        /**
338         * @param enabledIconResId The icon for when this action is on.
339         * @param disabledIconResid The icon for when this action is off.
340         * @param essage The general information message, e.g 'Silent Mode'
341         * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
342         * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
343         */
344        public ToggleAction(int enabledIconResId,
345                int disabledIconResid,
346                int essage,
347                int enabledStatusMessageResId,
348                int disabledStatusMessageResId) {
349            mEnabledIconResId = enabledIconResId;
350            mDisabledIconResid = disabledIconResid;
351            mMessageResId = essage;
352            mEnabledStatusMessageResId = enabledStatusMessageResId;
353            mDisabledStatusMessageResId = disabledStatusMessageResId;
354        }
355
356        public LinearLayout create(Context context, LinearLayout convertView,
357                LayoutInflater inflater) {
358            LinearLayout v = (LinearLayout) ((convertView != null) ?
359                    convertView :
360                    inflater.inflate(R
361                            .layout.global_actions_item, null));
362
363            ImageView icon = (ImageView) v.findViewById(R.id.icon);
364            TextView messageView = (TextView) v.findViewById(R.id.message);
365            TextView statusView = (TextView) v.findViewById(R.id.status);
366
367            messageView.setText(mMessageResId);
368
369            icon.setImageDrawable(context.getResources().getDrawable(
370                    (mOn ? mEnabledIconResId : mDisabledIconResid)));
371            statusView.setText(mOn ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
372            statusView.setVisibility(View.VISIBLE);
373
374            return v;
375        }
376
377        public void onPress() {
378            updateState(!mOn);
379            onToggle(mOn);
380        }
381
382        abstract void onToggle(boolean on);
383
384        public void updateState(boolean on) {
385            mOn = on;
386        }
387    }
388
389    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
390        public void onReceive(Context context, Intent intent) {
391            String action = intent.getAction();
392            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
393                String reason = intent.getStringExtra(MidWindowManager.SYSTEM_DIALOG_REASON_KEY);
394                if (! MidWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
395                    mHandler.sendEmptyMessage(MESSAGE_DISMISS);
396                }
397            }
398        }
399    };
400
401    private static final int MESSAGE_DISMISS = 0;
402    private Handler mHandler = new Handler() {
403        public void handleMessage(Message msg) {
404            if (msg.what == MESSAGE_DISMISS) {
405                if (mDialog != null) {
406                    mDialog.dismiss();
407                }
408            }
409        }
410    };
411}
412