blob: 5ef518e21c58cad2dd15977d40bff7ec82582db4 [file] [log] [blame]
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001/*
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
Jorim Jaggib10e33f2015-02-04 21:57:40 +010017package com.android.server.policy;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080018
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070019import com.android.internal.app.AlertController;
20import com.android.internal.app.AlertController.AlertParams;
Felipe Lemed426d12b2016-02-23 10:28:55 -080021import com.android.internal.logging.MetricsLogger;
22import com.android.internal.logging.MetricsProto.MetricsEvent;
Amith Yamasani742a6712011-05-04 14:49:28 -070023import com.android.internal.telephony.TelephonyIntents;
24import com.android.internal.telephony.TelephonyProperties;
25import com.android.internal.R;
Adrian Roosd4d61362014-06-25 23:30:50 +020026import com.android.internal.widget.LockPatternUtils;
Amith Yamasani742a6712011-05-04 14:49:28 -070027
Guang Zhu98a88142014-10-03 12:23:43 -070028import android.app.ActivityManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070029import android.app.ActivityManagerNative;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080030import android.app.AlertDialog;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070031import android.app.Dialog;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080032import android.content.BroadcastReceiver;
33import android.content.Context;
34import android.content.DialogInterface;
35import android.content.Intent;
36import android.content.IntentFilter;
Amith Yamasani742a6712011-05-04 14:49:28 -070037import android.content.pm.UserInfo;
Amith Yamasani7ee994d2012-05-24 13:53:26 -070038import android.database.ContentObserver;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070039import android.graphics.drawable.Drawable;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080040import android.media.AudioManager;
Amith Yamasani7ee994d2012-05-24 13:53:26 -070041import android.net.ConnectivityManager;
John Spurlock887aa002014-06-02 14:10:41 -040042import android.os.Build;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070043import android.os.Bundle;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080044import android.os.Handler;
45import android.os.Message;
Amith Yamasani742a6712011-05-04 14:49:28 -070046import android.os.RemoteException;
John Spurlock542a51c2012-10-03 16:54:23 -040047import android.os.ServiceManager;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070048import android.os.SystemClock;
Jinghui Guofe1fee52009-09-17 14:05:27 -050049import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070050import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070051import android.os.UserManager;
Amith Yamasani13593602012-03-22 16:16:17 -070052import android.os.Vibrator;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080053import android.provider.Settings;
John Spurlock542a51c2012-10-03 16:54:23 -040054import android.service.dreams.DreamService;
55import android.service.dreams.IDreamManager;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080056import android.telephony.PhoneStateListener;
57import android.telephony.ServiceState;
58import android.telephony.TelephonyManager;
John Spurlock887aa002014-06-02 14:10:41 -040059import android.text.TextUtils;
Justin Koh3b9a3cc2014-04-21 17:50:40 -070060import android.util.ArraySet;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080061import android.util.Log;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070062import android.util.TypedValue;
63import android.view.InputDevice;
64import android.view.KeyEvent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080065import android.view.LayoutInflater;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070066import android.view.MotionEvent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080067import android.view.View;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070068import android.view.ViewConfiguration;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080069import android.view.ViewGroup;
70import android.view.WindowManager;
Adrian Roosd4d61362014-06-25 23:30:50 +020071import android.view.WindowManagerGlobal;
Jeff Brown7304c342012-05-11 18:42:42 -070072import android.view.WindowManagerPolicy.WindowManagerFuncs;
Jason Monk30443b12014-09-23 09:09:52 -040073import android.view.accessibility.AccessibilityEvent;
Dianne Hackborn19caadc2012-04-20 17:49:10 -070074import android.widget.AdapterView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080075import android.widget.BaseAdapter;
76import android.widget.ImageView;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070077import android.widget.ImageView.ScaleType;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070078import android.widget.ListView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080079import android.widget.TextView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080080
81import java.util.ArrayList;
Amith Yamasani742a6712011-05-04 14:49:28 -070082import java.util.List;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080083
84/**
85 * Helper to show the global actions dialog. Each item is an {@link Action} that
86 * may show depending on whether the keyguard is showing, and whether the device
87 * is provisioned.
88 */
89class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
90
91 private static final String TAG = "GlobalActions";
92
Amith Yamasani6243edd2011-12-05 19:58:48 -080093 private static final boolean SHOW_SILENT_TOGGLE = true;
Amith Yamasani2ef6f1b2011-12-01 14:01:30 -080094
Justin Koh3b9a3cc2014-04-21 17:50:40 -070095 /* Valid settings for global actions keys.
96 * see config.xml config_globalActionList */
97 private static final String GLOBAL_ACTION_KEY_POWER = "power";
98 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
99 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
100 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
101 private static final String GLOBAL_ACTION_KEY_USERS = "users";
102 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
Adrian Roosd4d61362014-06-25 23:30:50 +0200103 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
Bryce Leecc8b4672015-02-05 18:39:01 -0800104 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
Bryce Leed8379b92015-03-19 00:32:17 +0000105 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700106
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800107 private final Context mContext;
Jeff Brown7304c342012-05-11 18:42:42 -0700108 private final WindowManagerFuncs mWindowManagerFuncs;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800109 private final AudioManager mAudioManager;
John Spurlock542a51c2012-10-03 16:54:23 -0400110 private final IDreamManager mDreamManager;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800111
112 private ArrayList<Action> mItems;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -0700113 private GlobalActionsDialog mDialog;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800114
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700115 private Action mSilentModeAction;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800116 private ToggleAction mAirplaneModeOn;
117
118 private MyAdapter mAdapter;
119
120 private boolean mKeyguardShowing = false;
121 private boolean mDeviceProvisioned = false;
122 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
Jinghui Guofe1fee52009-09-17 14:05:27 -0500123 private boolean mIsWaitingForEcmExit = false;
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700124 private boolean mHasTelephony;
125 private boolean mHasVibrator;
Eric Laurent83a017b2013-03-19 18:15:31 -0700126 private final boolean mShowSilentToggle;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800127
128 /**
129 * @param context everything needs a context :(
130 */
Jeff Brown7304c342012-05-11 18:42:42 -0700131 public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800132 mContext = context;
Jeff Brown7304c342012-05-11 18:42:42 -0700133 mWindowManagerFuncs = windowManagerFuncs;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800134 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
John Spurlock542a51c2012-10-03 16:54:23 -0400135 mDreamManager = IDreamManager.Stub.asInterface(
136 ServiceManager.getService(DreamService.DREAM_SERVICE));
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800137
138 // receive broadcasts
139 IntentFilter filter = new IntentFilter();
140 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
Joe Onorato78866b12009-07-17 13:09:27 -0400141 filter.addAction(Intent.ACTION_SCREEN_OFF);
Jinghui Guofe1fee52009-09-17 14:05:27 -0500142 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800143 context.registerReceiver(mBroadcastReceiver, filter);
144
nagarwc3b7e6f2014-02-20 17:10:46 +0530145 ConnectivityManager cm = (ConnectivityManager)
146 context.getSystemService(Context.CONNECTIVITY_SERVICE);
147 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
148
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800149 // get notified of phone state changes
150 TelephonyManager telephonyManager =
151 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
152 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700153 mContext.getContentResolver().registerContentObserver(
Christopher Tatec09cdce2012-09-10 16:50:14 -0700154 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700155 mAirplaneModeObserver);
156 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
157 mHasVibrator = vibrator != null && vibrator.hasVibrator();
Eric Laurent83a017b2013-03-19 18:15:31 -0700158
159 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
160 com.android.internal.R.bool.config_useFixedVolume);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800161 }
162
163 /**
164 * Show the global actions dialog (creating if necessary)
165 * @param keyguardShowing True if keyguard is showing
166 */
167 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
168 mKeyguardShowing = keyguardShowing;
169 mDeviceProvisioned = isDeviceProvisioned;
Amith Yamasani742a6712011-05-04 14:49:28 -0700170 if (mDialog != null) {
171 mDialog.dismiss();
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700172 mDialog = null;
173 // Show delayed, so that the dismiss of the previous dialog completes
174 mHandler.sendEmptyMessage(MESSAGE_SHOW);
175 } else {
176 handleShow();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800177 }
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700178 }
179
John Spurlock542a51c2012-10-03 16:54:23 -0400180 private void awakenIfNecessary() {
181 if (mDreamManager != null) {
182 try {
183 if (mDreamManager.isDreaming()) {
184 mDreamManager.awaken();
185 }
186 } catch (RemoteException e) {
187 // we tried
188 }
189 }
190 }
191
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700192 private void handleShow() {
John Spurlock542a51c2012-10-03 16:54:23 -0400193 awakenIfNecessary();
Amith Yamasani742a6712011-05-04 14:49:28 -0700194 mDialog = createDialog();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800195 prepareDialog();
196
Justin Koh1c46c2d782014-05-12 16:00:36 -0700197 // If we only have 1 item and it's a simple press action, just do this action.
198 if (mAdapter.getCount() == 1
John Spurlock822e5cc2014-05-30 17:48:54 -0400199 && mAdapter.getItem(0) instanceof SinglePressAction
200 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
Justin Koh1c46c2d782014-05-12 16:00:36 -0700201 ((SinglePressAction) mAdapter.getItem(0)).onPress();
202 } else {
203 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
204 attrs.setTitle("GlobalActions");
205 mDialog.getWindow().setAttributes(attrs);
206 mDialog.show();
207 mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
208 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800209 }
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700210
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800211 /**
212 * Create the global actions dialog.
213 * @return A new dialog.
214 */
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -0700215 private GlobalActionsDialog createDialog() {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700216 // Simple toggle style if there's no vibrator, otherwise use a tri-state
217 if (!mHasVibrator) {
218 mSilentModeAction = new SilentModeToggleAction();
219 } else {
220 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
221 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800222 mAirplaneModeOn = new ToggleAction(
223 R.drawable.ic_lock_airplane_mode,
224 R.drawable.ic_lock_airplane_mode_off,
225 R.string.global_actions_toggle_airplane_mode,
226 R.string.global_actions_airplane_mode_on_status,
227 R.string.global_actions_airplane_mode_off_status) {
228
229 void onToggle(boolean on) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700230 if (mHasTelephony && Boolean.parseBoolean(
Jinghui Guofe1fee52009-09-17 14:05:27 -0500231 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
232 mIsWaitingForEcmExit = true;
233 // Launch ECM exit dialog
234 Intent ecmDialogIntent =
235 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
236 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
237 mContext.startActivity(ecmDialogIntent);
238 } else {
239 changeAirplaneModeSystemSetting(on);
240 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800241 }
242
243 @Override
244 protected void changeStateFromPress(boolean buttonOn) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700245 if (!mHasTelephony) return;
246
Jinghui Guofe1fee52009-09-17 14:05:27 -0500247 // In ECM mode airplane state cannot be changed
248 if (!(Boolean.parseBoolean(
249 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
250 mState = buttonOn ? State.TurningOn : State.TurningOff;
251 mAirplaneState = mState;
252 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800253 }
254
255 public boolean showDuringKeyguard() {
256 return true;
257 }
258
259 public boolean showBeforeProvisioning() {
260 return false;
261 }
262 };
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700263 onAirplaneModeChanged();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800264
Amith Yamasani2ef6f1b2011-12-01 14:01:30 -0800265 mItems = new ArrayList<Action>();
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700266 String[] defaultActions = mContext.getResources().getStringArray(
267 com.android.internal.R.array.config_globalActionsList);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800268
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700269 ArraySet<String> addedKeys = new ArraySet<String>();
270 for (int i = 0; i < defaultActions.length; i++) {
271 String actionKey = defaultActions[i];
272 if (addedKeys.contains(actionKey)) {
273 // If we already have added this, don't add it again.
274 continue;
275 }
276 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
John Spurlock822e5cc2014-05-30 17:48:54 -0400277 mItems.add(new PowerAction());
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700278 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
279 mItems.add(mAirplaneModeOn);
John Spurlock68539a62014-08-20 10:36:33 -0400280 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
281 if (Settings.Global.getInt(mContext.getContentResolver(),
282 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
Felipe Lemee974f7e2015-12-15 14:15:56 -0800283 mItems.add(new BugReportAction());
John Spurlock68539a62014-08-20 10:36:33 -0400284 }
285 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
286 if (mShowSilentToggle) {
287 mItems.add(mSilentModeAction);
288 }
289 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
290 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
291 addUsersToMenu(mItems);
292 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700293 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
294 mItems.add(getSettingsAction());
Adrian Roos4ebcdfd2014-08-12 23:33:49 +0200295 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Adrian Roosd4d61362014-06-25 23:30:50 +0200296 mItems.add(getLockdownAction());
Bryce Leecc8b4672015-02-05 18:39:01 -0800297 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
298 mItems.add(getVoiceAssistAction());
Bryce Leed8379b92015-03-19 00:32:17 +0000299 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
300 mItems.add(getAssistAction());
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700301 } else {
302 Log.e(TAG, "Invalid global action key " + actionKey);
303 }
304 // Add here so we don't add more than one.
305 addedKeys.add(actionKey);
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700306 }
307
308 mAdapter = new MyAdapter();
309
310 AlertParams params = new AlertParams(mContext);
311 params.mAdapter = mAdapter;
312 params.mOnClickListener = this;
313 params.mForceInverseBackground = true;
314
315 GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
316 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
317
318 dialog.getListView().setItemsCanFocus(true);
319 dialog.getListView().setLongClickable(true);
320 dialog.getListView().setOnItemLongClickListener(
321 new AdapterView.OnItemLongClickListener() {
322 @Override
323 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
324 long id) {
John Spurlock822e5cc2014-05-30 17:48:54 -0400325 final Action action = mAdapter.getItem(position);
326 if (action instanceof LongPressAction) {
327 return ((LongPressAction) action).onLongPress();
328 }
329 return false;
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700330 }
331 });
Dianne Hackbornbef0f612012-10-16 18:34:48 -0700332 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700333
334 dialog.setOnDismissListener(this);
335
336 return dialog;
337 }
338
John Spurlock822e5cc2014-05-30 17:48:54 -0400339 private final class PowerAction extends SinglePressAction implements LongPressAction {
340 private PowerAction() {
341 super(com.android.internal.R.drawable.ic_lock_power_off,
342 R.string.global_action_power_off);
343 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700344
John Spurlock822e5cc2014-05-30 17:48:54 -0400345 @Override
346 public boolean onLongPress() {
Benjamin Franzbff46ba2015-03-05 18:33:51 +0000347 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
348 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
349 mWindowManagerFuncs.rebootSafeMode(true);
350 return true;
351 }
352 return false;
John Spurlock822e5cc2014-05-30 17:48:54 -0400353 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700354
John Spurlock822e5cc2014-05-30 17:48:54 -0400355 @Override
356 public boolean showDuringKeyguard() {
357 return true;
358 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700359
John Spurlock822e5cc2014-05-30 17:48:54 -0400360 @Override
361 public boolean showBeforeProvisioning() {
362 return true;
363 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700364
John Spurlock822e5cc2014-05-30 17:48:54 -0400365 @Override
366 public void onPress() {
367 // shutdown by making sure radio and power are handled accordingly.
368 mWindowManagerFuncs.shutdown(false /* confirm */);
369 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700370 }
371
Felipe Lemee974f7e2015-12-15 14:15:56 -0800372 private class BugReportAction extends SinglePressAction implements LongPressAction {
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700373
Felipe Lemee974f7e2015-12-15 14:15:56 -0800374 public BugReportAction() {
375 super(com.android.internal.R.drawable.ic_lock_bugreport, R.string.bugreport_title);
376 }
377
378 @Override
379 public void onPress() {
380 // don't actually trigger the bugreport if we are running stability
381 // tests via monkey
382 if (ActivityManager.isUserAMonkey()) {
383 return;
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700384 }
Felipe Lemee974f7e2015-12-15 14:15:56 -0800385 // Add a little delay before executing, to give the
386 // dialog a chance to go away before it takes a
387 // screenshot.
Felipe Lemee974f7e2015-12-15 14:15:56 -0800388 mHandler.postDelayed(new Runnable() {
389 @Override
390 public void run() {
391 try {
Felipe Leme43b013f2015-12-22 14:09:25 -0800392 // Take an "interactive" bugreport.
Felipe Lemed426d12b2016-02-23 10:28:55 -0800393 MetricsLogger.action(mContext,
Felipe Leme6605bd82016-02-22 15:22:20 -0800394 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
Michal Karpinski3da5c972015-12-11 18:16:30 +0000395 ActivityManagerNative.getDefault().requestBugReport(
396 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
Felipe Lemee974f7e2015-12-15 14:15:56 -0800397 } catch (RemoteException e) {
398 }
399 }
400 }, 500);
401 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700402
Felipe Lemee974f7e2015-12-15 14:15:56 -0800403 @Override
404 public boolean onLongPress() {
405 // don't actually trigger the bugreport if we are running stability
406 // tests via monkey
407 if (ActivityManager.isUserAMonkey()) {
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700408 return false;
409 }
Felipe Lemee974f7e2015-12-15 14:15:56 -0800410 try {
Felipe Leme43b013f2015-12-22 14:09:25 -0800411 // Take a "full" bugreport.
Felipe Lemed426d12b2016-02-23 10:28:55 -0800412 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
Michal Karpinski3da5c972015-12-11 18:16:30 +0000413 ActivityManagerNative.getDefault().requestBugReport(
414 ActivityManager.BUGREPORT_OPTION_FULL);
Felipe Lemee974f7e2015-12-15 14:15:56 -0800415 } catch (RemoteException e) {
John Spurlock887aa002014-06-02 14:10:41 -0400416 }
Felipe Leme43b013f2015-12-22 14:09:25 -0800417 return false;
Felipe Lemee974f7e2015-12-15 14:15:56 -0800418 }
419
420 public boolean showDuringKeyguard() {
421 return true;
422 }
423
424 @Override
425 public boolean showBeforeProvisioning() {
426 return false;
427 }
428
429 @Override
430 public String getStatus() {
431 return mContext.getString(
432 com.android.internal.R.string.bugreport_status,
433 Build.VERSION.RELEASE,
434 Build.ID);
435 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700436 }
437
438 private Action getSettingsAction() {
439 return new SinglePressAction(com.android.internal.R.drawable.ic_settings,
440 R.string.global_action_settings) {
441
442 @Override
443 public void onPress() {
444 Intent intent = new Intent(Settings.ACTION_SETTINGS);
Wei Liu636f3ea2014-09-03 22:10:57 -0700445 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700446 mContext.startActivity(intent);
447 }
448
449 @Override
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700450 public boolean showDuringKeyguard() {
451 return true;
452 }
453
454 @Override
455 public boolean showBeforeProvisioning() {
Justin Koh1c46c2d782014-05-12 16:00:36 -0700456 return true;
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700457 }
458 };
459 }
460
Bryce Leed8379b92015-03-19 00:32:17 +0000461 private Action getAssistAction() {
462 return new SinglePressAction(com.android.internal.R.drawable.ic_action_assist_focused,
463 R.string.global_action_assist) {
464 @Override
465 public void onPress() {
466 Intent intent = new Intent(Intent.ACTION_ASSIST);
467 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
468 mContext.startActivity(intent);
469 }
470
471 @Override
472 public boolean showDuringKeyguard() {
473 return true;
474 }
475
476 @Override
477 public boolean showBeforeProvisioning() {
478 return true;
479 }
480 };
481 }
482
Bryce Leecc8b4672015-02-05 18:39:01 -0800483 private Action getVoiceAssistAction() {
484 return new SinglePressAction(com.android.internal.R.drawable.ic_voice_search,
485 R.string.global_action_voice_assist) {
486 @Override
487 public void onPress() {
488 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
489 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
490 mContext.startActivity(intent);
491 }
492
493 @Override
494 public boolean showDuringKeyguard() {
495 return true;
496 }
497
498 @Override
499 public boolean showBeforeProvisioning() {
500 return true;
501 }
502 };
503 }
504
Adrian Roosd4d61362014-06-25 23:30:50 +0200505 private Action getLockdownAction() {
506 return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock,
507 R.string.global_action_lockdown) {
508
509 @Override
510 public void onPress() {
511 new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
512 try {
513 WindowManagerGlobal.getWindowManagerService().lockNow(null);
514 } catch (RemoteException e) {
515 Log.e(TAG, "Error while trying to lock device.", e);
516 }
517 }
518
519 @Override
520 public boolean showDuringKeyguard() {
521 return true;
522 }
523
524 @Override
525 public boolean showBeforeProvisioning() {
526 return false;
527 }
528 };
529 }
530
Amith Yamasani3df21722013-06-10 13:43:10 -0700531 private UserInfo getCurrentUser() {
532 try {
533 return ActivityManagerNative.getDefault().getCurrentUser();
534 } catch (RemoteException re) {
535 return null;
536 }
537 }
538
539 private boolean isCurrentUserOwner() {
540 UserInfo currentUser = getCurrentUser();
541 return currentUser == null || currentUser.isPrimary();
542 }
543
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700544 private void addUsersToMenu(ArrayList<Action> items) {
Kenny Guy1a447532014-02-20 21:55:32 +0000545 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
546 if (um.isUserSwitcherEnabled()) {
547 List<UserInfo> users = um.getUsers();
Amith Yamasani3df21722013-06-10 13:43:10 -0700548 UserInfo currentUser = getCurrentUser();
Amith Yamasani742a6712011-05-04 14:49:28 -0700549 for (final UserInfo user : users) {
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700550 if (user.supportsSwitchToByUser()) {
Kenny Guy1a447532014-02-20 21:55:32 +0000551 boolean isCurrentUser = currentUser == null
552 ? user.id == 0 : (currentUser.id == user.id);
553 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
554 : null;
555 SinglePressAction switchToUser = new SinglePressAction(
556 com.android.internal.R.drawable.ic_menu_cc, icon,
557 (user.name != null ? user.name : "Primary")
558 + (isCurrentUser ? " \u2714" : "")) {
559 public void onPress() {
560 try {
561 ActivityManagerNative.getDefault().switchUser(user.id);
562 } catch (RemoteException re) {
563 Log.e(TAG, "Couldn't switch user " + re);
564 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700565 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700566
Kenny Guy1a447532014-02-20 21:55:32 +0000567 public boolean showDuringKeyguard() {
568 return true;
569 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700570
Kenny Guy1a447532014-02-20 21:55:32 +0000571 public boolean showBeforeProvisioning() {
572 return false;
573 }
574 };
575 items.add(switchToUser);
576 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700577 }
578 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800579 }
580
581 private void prepareDialog() {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700582 refreshSilentMode();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800583 mAirplaneModeOn.updateState(mAirplaneState);
584 mAdapter.notifyDataSetChanged();
Dianne Hackbornbef0f612012-10-16 18:34:48 -0700585 mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Eric Laurent83a017b2013-03-19 18:15:31 -0700586 if (mShowSilentToggle) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800587 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
588 mContext.registerReceiver(mRingerModeReceiver, filter);
589 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800590 }
591
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700592 private void refreshSilentMode() {
593 if (!mHasVibrator) {
594 final boolean silentModeOn =
595 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
596 ((ToggleAction)mSilentModeAction).updateState(
597 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
598 }
599 }
600
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800601 /** {@inheritDoc} */
602 public void onDismiss(DialogInterface dialog) {
Eric Laurent83a017b2013-03-19 18:15:31 -0700603 if (mShowSilentToggle) {
Masanori Ogino7d4adea2012-10-11 18:51:35 +0900604 try {
605 mContext.unregisterReceiver(mRingerModeReceiver);
606 } catch (IllegalArgumentException ie) {
607 // ignore this
608 Log.w(TAG, ie);
609 }
Amith Yamasani6243edd2011-12-05 19:58:48 -0800610 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800611 }
612
613 /** {@inheritDoc} */
614 public void onClick(DialogInterface dialog, int which) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700615 if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800616 dialog.dismiss();
617 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800618 mAdapter.getItem(which).onPress();
619 }
620
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800621 /**
622 * The adapter used for the list within the global actions dialog, taking
623 * into account whether the keyguard is showing via
624 * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
625 * via {@link GlobalActions#mDeviceProvisioned}.
626 */
627 private class MyAdapter extends BaseAdapter {
628
629 public int getCount() {
630 int count = 0;
631
632 for (int i = 0; i < mItems.size(); i++) {
633 final Action action = mItems.get(i);
634
635 if (mKeyguardShowing && !action.showDuringKeyguard()) {
636 continue;
637 }
638 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
639 continue;
640 }
641 count++;
642 }
643 return count;
644 }
645
646 @Override
647 public boolean isEnabled(int position) {
648 return getItem(position).isEnabled();
649 }
650
651 @Override
652 public boolean areAllItemsEnabled() {
653 return false;
654 }
655
656 public Action getItem(int position) {
657
658 int filteredPos = 0;
659 for (int i = 0; i < mItems.size(); i++) {
660 final Action action = mItems.get(i);
661 if (mKeyguardShowing && !action.showDuringKeyguard()) {
662 continue;
663 }
664 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
665 continue;
666 }
667 if (filteredPos == position) {
668 return action;
669 }
670 filteredPos++;
671 }
672
Bernhard Reutner-Fischerc1a9b032011-04-28 20:06:44 +0200673 throw new IllegalArgumentException("position " + position
674 + " out of range of showable actions"
675 + ", filtered count=" + getCount()
676 + ", keyguardshowing=" + mKeyguardShowing
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800677 + ", provisioned=" + mDeviceProvisioned);
678 }
679
680
681 public long getItemId(int position) {
682 return position;
683 }
684
685 public View getView(int position, View convertView, ViewGroup parent) {
686 Action action = getItem(position);
687 return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
688 }
689 }
690
691 // note: the scheme below made more sense when we were planning on having
692 // 8 different things in the global actions dialog. seems overkill with
693 // only 3 items now, but may as well keep this flexible approach so it will
694 // be easy should someone decide at the last minute to include something
695 // else, such as 'enable wifi', or 'enable bluetooth'
696
697 /**
698 * What each item in the global actions dialog must be able to support.
699 */
700 private interface Action {
Jason Monk30443b12014-09-23 09:09:52 -0400701 /**
702 * @return Text that will be announced when dialog is created. null
703 * for none.
704 */
705 CharSequence getLabelForAccessibility(Context context);
706
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800707 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
708
709 void onPress();
710
711 /**
712 * @return whether this action should appear in the dialog when the keygaurd
713 * is showing.
714 */
715 boolean showDuringKeyguard();
716
717 /**
718 * @return whether this action should appear in the dialog before the
719 * device is provisioned.
720 */
721 boolean showBeforeProvisioning();
722
723 boolean isEnabled();
724 }
725
726 /**
John Spurlock822e5cc2014-05-30 17:48:54 -0400727 * An action that also supports long press.
728 */
729 private interface LongPressAction extends Action {
730 boolean onLongPress();
731 }
732
733 /**
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800734 * A single press action maintains no state, just responds to a press
735 * and takes an action.
736 */
737 private static abstract class SinglePressAction implements Action {
738 private final int mIconResId;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700739 private final Drawable mIcon;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800740 private final int mMessageResId;
Amith Yamasani742a6712011-05-04 14:49:28 -0700741 private final CharSequence mMessage;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800742
743 protected SinglePressAction(int iconResId, int messageResId) {
744 mIconResId = iconResId;
745 mMessageResId = messageResId;
Amith Yamasani742a6712011-05-04 14:49:28 -0700746 mMessage = null;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700747 mIcon = null;
748 }
749
750 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
751 mIconResId = iconResId;
752 mMessageResId = 0;
753 mMessage = message;
754 mIcon = icon;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800755 }
756
757 public boolean isEnabled() {
758 return true;
759 }
760
John Spurlock887aa002014-06-02 14:10:41 -0400761 public String getStatus() {
762 return null;
763 }
764
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800765 abstract public void onPress();
766
Jason Monk30443b12014-09-23 09:09:52 -0400767 public CharSequence getLabelForAccessibility(Context context) {
768 if (mMessage != null) {
769 return mMessage;
770 } else {
771 return context.getString(mMessageResId);
772 }
773 }
774
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800775 public View create(
776 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800777 View v = inflater.inflate(R.layout.global_actions_item, parent, false);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800778
779 ImageView icon = (ImageView) v.findViewById(R.id.icon);
780 TextView messageView = (TextView) v.findViewById(R.id.message);
781
John Spurlock887aa002014-06-02 14:10:41 -0400782 TextView statusView = (TextView) v.findViewById(R.id.status);
783 final String status = getStatus();
784 if (!TextUtils.isEmpty(status)) {
785 statusView.setText(status);
786 } else {
787 statusView.setVisibility(View.GONE);
788 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700789 if (mIcon != null) {
790 icon.setImageDrawable(mIcon);
791 icon.setScaleType(ScaleType.CENTER_CROP);
792 } else if (mIconResId != 0) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800793 icon.setImageDrawable(context.getDrawable(mIconResId));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700794 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700795 if (mMessage != null) {
796 messageView.setText(mMessage);
797 } else {
798 messageView.setText(mMessageResId);
799 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800800
801 return v;
802 }
803 }
804
805 /**
806 * A toggle action knows whether it is on or off, and displays an icon
807 * and status message accordingly.
808 */
809 private static abstract class ToggleAction implements Action {
810
811 enum State {
812 Off(false),
813 TurningOn(true),
814 TurningOff(true),
815 On(false);
816
817 private final boolean inTransition;
818
819 State(boolean intermediate) {
820 inTransition = intermediate;
821 }
822
823 public boolean inTransition() {
824 return inTransition;
825 }
826 }
827
828 protected State mState = State.Off;
829
830 // prefs
Daniel Sandler82d6c682010-02-26 15:16:54 -0500831 protected int mEnabledIconResId;
832 protected int mDisabledIconResid;
833 protected int mMessageResId;
834 protected int mEnabledStatusMessageResId;
835 protected int mDisabledStatusMessageResId;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800836
837 /**
838 * @param enabledIconResId The icon for when this action is on.
839 * @param disabledIconResid The icon for when this action is off.
840 * @param essage The general information message, e.g 'Silent Mode'
841 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
842 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
843 */
844 public ToggleAction(int enabledIconResId,
845 int disabledIconResid,
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700846 int message,
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800847 int enabledStatusMessageResId,
848 int disabledStatusMessageResId) {
849 mEnabledIconResId = enabledIconResId;
850 mDisabledIconResid = disabledIconResid;
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700851 mMessageResId = message;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800852 mEnabledStatusMessageResId = enabledStatusMessageResId;
853 mDisabledStatusMessageResId = disabledStatusMessageResId;
854 }
855
Daniel Sandler82d6c682010-02-26 15:16:54 -0500856 /**
857 * Override to make changes to resource IDs just before creating the
858 * View.
859 */
860 void willCreate() {
861
862 }
863
Jason Monk30443b12014-09-23 09:09:52 -0400864 @Override
865 public CharSequence getLabelForAccessibility(Context context) {
866 return context.getString(mMessageResId);
867 }
868
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800869 public View create(Context context, View convertView, ViewGroup parent,
870 LayoutInflater inflater) {
Daniel Sandler82d6c682010-02-26 15:16:54 -0500871 willCreate();
872
Amith Yamasani6243edd2011-12-05 19:58:48 -0800873 View v = inflater.inflate(R
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800874 .layout.global_actions_item, parent, false);
875
876 ImageView icon = (ImageView) v.findViewById(R.id.icon);
877 TextView messageView = (TextView) v.findViewById(R.id.message);
878 TextView statusView = (TextView) v.findViewById(R.id.status);
Amith Yamasani6243edd2011-12-05 19:58:48 -0800879 final boolean enabled = isEnabled();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800880
Amith Yamasani6243edd2011-12-05 19:58:48 -0800881 if (messageView != null) {
882 messageView.setText(mMessageResId);
883 messageView.setEnabled(enabled);
884 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800885
886 boolean on = ((mState == State.On) || (mState == State.TurningOn));
Amith Yamasani6243edd2011-12-05 19:58:48 -0800887 if (icon != null) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800888 icon.setImageDrawable(context.getDrawable(
Amith Yamasani6243edd2011-12-05 19:58:48 -0800889 (on ? mEnabledIconResId : mDisabledIconResid)));
890 icon.setEnabled(enabled);
891 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800892
Amith Yamasani6243edd2011-12-05 19:58:48 -0800893 if (statusView != null) {
894 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
895 statusView.setVisibility(View.VISIBLE);
896 statusView.setEnabled(enabled);
897 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800898 v.setEnabled(enabled);
899
900 return v;
901 }
902
903 public final void onPress() {
904 if (mState.inTransition()) {
905 Log.w(TAG, "shouldn't be able to toggle when in transition");
906 return;
907 }
908
909 final boolean nowOn = !(mState == State.On);
910 onToggle(nowOn);
911 changeStateFromPress(nowOn);
912 }
913
914 public boolean isEnabled() {
915 return !mState.inTransition();
916 }
917
918 /**
919 * Implementations may override this if their state can be in on of the intermediate
920 * states until some notification is received (e.g airplane mode is 'turning off' until
921 * we know the wireless connections are back online
922 * @param buttonOn Whether the button was turned on or off
923 */
924 protected void changeStateFromPress(boolean buttonOn) {
925 mState = buttonOn ? State.On : State.Off;
926 }
927
928 abstract void onToggle(boolean on);
929
930 public void updateState(State state) {
931 mState = state;
932 }
933 }
934
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700935 private class SilentModeToggleAction extends ToggleAction {
936 public SilentModeToggleAction() {
937 super(R.drawable.ic_audio_vol_mute,
938 R.drawable.ic_audio_vol,
939 R.string.global_action_toggle_silent_mode,
940 R.string.global_action_silent_mode_on_status,
941 R.string.global_action_silent_mode_off_status);
942 }
943
944 void onToggle(boolean on) {
945 if (on) {
946 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
947 } else {
948 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
949 }
950 }
951
952 public boolean showDuringKeyguard() {
953 return true;
954 }
955
956 public boolean showBeforeProvisioning() {
957 return false;
958 }
959 }
960
961 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800962
963 private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
964
965 private final AudioManager mAudioManager;
966 private final Handler mHandler;
Amith Yamasani13593602012-03-22 16:16:17 -0700967 private final Context mContext;
Amith Yamasani6243edd2011-12-05 19:58:48 -0800968
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700969 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800970 mAudioManager = audioManager;
971 mHandler = handler;
Amith Yamasani13593602012-03-22 16:16:17 -0700972 mContext = context;
Amith Yamasani6243edd2011-12-05 19:58:48 -0800973 }
974
975 private int ringerModeToIndex(int ringerMode) {
976 // They just happen to coincide
977 return ringerMode;
978 }
979
980 private int indexToRingerMode(int index) {
981 // They just happen to coincide
982 return index;
983 }
984
Jason Monk30443b12014-09-23 09:09:52 -0400985 @Override
986 public CharSequence getLabelForAccessibility(Context context) {
987 return null;
988 }
989
Amith Yamasani6243edd2011-12-05 19:58:48 -0800990 public View create(Context context, View convertView, ViewGroup parent,
991 LayoutInflater inflater) {
992 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
Amith Yamasani6243edd2011-12-05 19:58:48 -0800993
994 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
995 for (int i = 0; i < 3; i++) {
996 View itemView = v.findViewById(ITEM_IDS[i]);
997 itemView.setSelected(selectedIndex == i);
998 // Set up click handler
999 itemView.setTag(i);
1000 itemView.setOnClickListener(this);
1001 }
1002 return v;
1003 }
1004
1005 public void onPress() {
1006 }
1007
1008 public boolean showDuringKeyguard() {
1009 return true;
1010 }
1011
1012 public boolean showBeforeProvisioning() {
1013 return false;
1014 }
1015
1016 public boolean isEnabled() {
1017 return true;
1018 }
1019
1020 void willCreate() {
1021 }
1022
1023 public void onClick(View v) {
1024 if (!(v.getTag() instanceof Integer)) return;
1025
1026 int index = (Integer) v.getTag();
1027 mAudioManager.setRingerMode(indexToRingerMode(index));
1028 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1029 }
1030 }
1031
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001032 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1033 public void onReceive(Context context, Intent intent) {
1034 String action = intent.getAction();
Joe Onorato78866b12009-07-17 13:09:27 -04001035 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1036 || Intent.ACTION_SCREEN_OFF.equals(action)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001037 String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
Joe Onorato78866b12009-07-17 13:09:27 -04001038 if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001039 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
1040 }
Jinghui Guofe1fee52009-09-17 14:05:27 -05001041 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1042 // Airplane mode can be changed after ECM exits if airplane toggle button
1043 // is pressed during ECM mode
1044 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1045 mIsWaitingForEcmExit) {
1046 mIsWaitingForEcmExit = false;
1047 changeAirplaneModeSystemSetting(true);
1048 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001049 }
1050 }
1051 };
1052
1053 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1054 @Override
1055 public void onServiceStateChanged(ServiceState serviceState) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001056 if (!mHasTelephony) return;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001057 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1058 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1059 mAirplaneModeOn.updateState(mAirplaneState);
1060 mAdapter.notifyDataSetChanged();
1061 }
1062 };
1063
Amith Yamasani6243edd2011-12-05 19:58:48 -08001064 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1065 @Override
1066 public void onReceive(Context context, Intent intent) {
1067 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1068 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1069 }
1070 }
1071 };
1072
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001073 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1074 @Override
1075 public void onChange(boolean selfChange) {
1076 onAirplaneModeChanged();
1077 }
1078 };
1079
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001080 private static final int MESSAGE_DISMISS = 0;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001081 private static final int MESSAGE_REFRESH = 1;
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001082 private static final int MESSAGE_SHOW = 2;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001083 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1084
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001085 private Handler mHandler = new Handler() {
1086 public void handleMessage(Message msg) {
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001087 switch (msg.what) {
1088 case MESSAGE_DISMISS:
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001089 if (mDialog != null) {
1090 mDialog.dismiss();
Henrik Baardae155942013-07-23 09:31:57 +02001091 mDialog = null;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001092 }
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001093 break;
1094 case MESSAGE_REFRESH:
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001095 refreshSilentMode();
Amith Yamasani6243edd2011-12-05 19:58:48 -08001096 mAdapter.notifyDataSetChanged();
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001097 break;
1098 case MESSAGE_SHOW:
1099 handleShow();
1100 break;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001101 }
1102 }
1103 };
Jinghui Guofe1fee52009-09-17 14:05:27 -05001104
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001105 private void onAirplaneModeChanged() {
1106 // Let the service state callbacks handle the state.
1107 if (mHasTelephony) return;
1108
Christopher Tatec09cdce2012-09-10 16:50:14 -07001109 boolean airplaneModeOn = Settings.Global.getInt(
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001110 mContext.getContentResolver(),
Christopher Tatec09cdce2012-09-10 16:50:14 -07001111 Settings.Global.AIRPLANE_MODE_ON,
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001112 0) == 1;
1113 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1114 mAirplaneModeOn.updateState(mAirplaneState);
1115 }
1116
Jinghui Guofe1fee52009-09-17 14:05:27 -05001117 /**
1118 * Change the airplane mode system setting
1119 */
1120 private void changeAirplaneModeSystemSetting(boolean on) {
Christopher Tatec09cdce2012-09-10 16:50:14 -07001121 Settings.Global.putInt(
Jinghui Guofe1fee52009-09-17 14:05:27 -05001122 mContext.getContentResolver(),
Christopher Tatec09cdce2012-09-10 16:50:14 -07001123 Settings.Global.AIRPLANE_MODE_ON,
Jinghui Guofe1fee52009-09-17 14:05:27 -05001124 on ? 1 : 0);
1125 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Dianne Hackborn818ba112009-12-11 11:12:02 -08001126 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Jinghui Guofe1fee52009-09-17 14:05:27 -05001127 intent.putExtra("state", on);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001128 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001129 if (!mHasTelephony) {
1130 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1131 }
Jinghui Guofe1fee52009-09-17 14:05:27 -05001132 }
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001133
1134 private static final class GlobalActionsDialog extends Dialog implements DialogInterface {
1135 private final Context mContext;
1136 private final int mWindowTouchSlop;
1137 private final AlertController mAlert;
Jason Monk30443b12014-09-23 09:09:52 -04001138 private final MyAdapter mAdapter;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001139
1140 private EnableAccessibilityController mEnableAccessibilityController;
1141
1142 private boolean mIntercepted;
1143 private boolean mCancelOnUp;
1144
1145 public GlobalActionsDialog(Context context, AlertParams params) {
1146 super(context, getDialogTheme(context));
Alan Viverettee54d2452015-05-06 10:41:43 -07001147 mContext = getContext();
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001148 mAlert = new AlertController(mContext, this, getWindow());
Jason Monk30443b12014-09-23 09:09:52 -04001149 mAdapter = (MyAdapter) params.mAdapter;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001150 mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
1151 params.apply(mAlert);
1152 }
1153
1154 private static int getDialogTheme(Context context) {
1155 TypedValue outValue = new TypedValue();
1156 context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
1157 outValue, true);
1158 return outValue.resourceId;
1159 }
1160
1161 @Override
1162 protected void onStart() {
1163 // If global accessibility gesture can be performed, we will take care
1164 // of dismissing the dialog on touch outside. This is because the dialog
1165 // is dismissed on the first down while the global gesture is a long press
1166 // with two fingers anywhere on the screen.
1167 if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
Svetoslavdc6d1a92014-08-28 18:40:57 -07001168 mEnableAccessibilityController = new EnableAccessibilityController(mContext,
1169 new Runnable() {
1170 @Override
1171 public void run() {
1172 dismiss();
1173 }
1174 });
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001175 super.setCanceledOnTouchOutside(false);
1176 } else {
1177 mEnableAccessibilityController = null;
1178 super.setCanceledOnTouchOutside(true);
1179 }
Justin Koh1c46c2d782014-05-12 16:00:36 -07001180
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001181 super.onStart();
1182 }
1183
1184 @Override
1185 protected void onStop() {
1186 if (mEnableAccessibilityController != null) {
1187 mEnableAccessibilityController.onDestroy();
1188 }
1189 super.onStop();
1190 }
1191
1192 @Override
1193 public boolean dispatchTouchEvent(MotionEvent event) {
1194 if (mEnableAccessibilityController != null) {
1195 final int action = event.getActionMasked();
1196 if (action == MotionEvent.ACTION_DOWN) {
1197 View decor = getWindow().getDecorView();
1198 final int eventX = (int) event.getX();
1199 final int eventY = (int) event.getY();
1200 if (eventX < -mWindowTouchSlop
1201 || eventY < -mWindowTouchSlop
1202 || eventX >= decor.getWidth() + mWindowTouchSlop
1203 || eventY >= decor.getHeight() + mWindowTouchSlop) {
1204 mCancelOnUp = true;
1205 }
1206 }
1207 try {
1208 if (!mIntercepted) {
1209 mIntercepted = mEnableAccessibilityController.onInterceptTouchEvent(event);
1210 if (mIntercepted) {
1211 final long now = SystemClock.uptimeMillis();
1212 event = MotionEvent.obtain(now, now,
1213 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1214 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1215 mCancelOnUp = true;
1216 }
1217 } else {
1218 return mEnableAccessibilityController.onTouchEvent(event);
1219 }
1220 } finally {
1221 if (action == MotionEvent.ACTION_UP) {
1222 if (mCancelOnUp) {
1223 cancel();
1224 }
1225 mCancelOnUp = false;
1226 mIntercepted = false;
1227 }
1228 }
1229 }
1230 return super.dispatchTouchEvent(event);
1231 }
1232
1233 public ListView getListView() {
1234 return mAlert.getListView();
1235 }
1236
1237 @Override
1238 protected void onCreate(Bundle savedInstanceState) {
1239 super.onCreate(savedInstanceState);
1240 mAlert.installContent();
1241 }
1242
1243 @Override
Jason Monk30443b12014-09-23 09:09:52 -04001244 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1245 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1246 for (int i = 0; i < mAdapter.getCount(); ++i) {
1247 CharSequence label =
1248 mAdapter.getItem(i).getLabelForAccessibility(getContext());
1249 if (label != null) {
1250 event.getText().add(label);
1251 }
1252 }
1253 }
1254 return super.dispatchPopulateAccessibilityEvent(event);
1255 }
1256
1257 @Override
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001258 public boolean onKeyDown(int keyCode, KeyEvent event) {
1259 if (mAlert.onKeyDown(keyCode, event)) {
1260 return true;
1261 }
1262 return super.onKeyDown(keyCode, event);
1263 }
1264
1265 @Override
1266 public boolean onKeyUp(int keyCode, KeyEvent event) {
1267 if (mAlert.onKeyUp(keyCode, event)) {
1268 return true;
1269 }
1270 return super.onKeyUp(keyCode, event);
1271 }
1272 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001273}