blob: d4adcc4b83839516245a0e42d9d6b91d1ee65e65 [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;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010022import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek705442f2016-09-13 16:02:33 -070023import com.android.internal.policy.EmergencyAffordanceManager;
Amith Yamasani742a6712011-05-04 14:49:28 -070024import com.android.internal.telephony.TelephonyIntents;
25import com.android.internal.telephony.TelephonyProperties;
26import com.android.internal.R;
Adrian Roosd4d61362014-06-25 23:30:50 +020027import com.android.internal.widget.LockPatternUtils;
Amith Yamasani742a6712011-05-04 14:49:28 -070028
Guang Zhu98a88142014-10-03 12:23:43 -070029import android.app.ActivityManager;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070030import android.app.Dialog;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080031import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.DialogInterface;
34import android.content.Intent;
35import android.content.IntentFilter;
Amith Yamasani742a6712011-05-04 14:49:28 -070036import android.content.pm.UserInfo;
Amith Yamasani7ee994d2012-05-24 13:53:26 -070037import android.database.ContentObserver;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070038import android.graphics.drawable.Drawable;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080039import android.media.AudioManager;
Amith Yamasani7ee994d2012-05-24 13:53:26 -070040import android.net.ConnectivityManager;
John Spurlock887aa002014-06-02 14:10:41 -040041import android.os.Build;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070042import android.os.Bundle;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080043import android.os.Handler;
44import android.os.Message;
Amith Yamasani742a6712011-05-04 14:49:28 -070045import android.os.RemoteException;
John Spurlock542a51c2012-10-03 16:54:23 -040046import android.os.ServiceManager;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070047import android.os.SystemClock;
Jinghui Guofe1fee52009-09-17 14:05:27 -050048import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070049import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070050import android.os.UserManager;
Amith Yamasani13593602012-03-22 16:16:17 -070051import android.os.Vibrator;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080052import android.provider.Settings;
John Spurlock542a51c2012-10-03 16:54:23 -040053import android.service.dreams.DreamService;
54import android.service.dreams.IDreamManager;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080055import android.telephony.PhoneStateListener;
56import android.telephony.ServiceState;
57import android.telephony.TelephonyManager;
John Spurlock887aa002014-06-02 14:10:41 -040058import android.text.TextUtils;
Justin Koh3b9a3cc2014-04-21 17:50:40 -070059import android.util.ArraySet;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080060import android.util.Log;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070061import android.util.TypedValue;
62import android.view.InputDevice;
63import android.view.KeyEvent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080064import android.view.LayoutInflater;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070065import android.view.MotionEvent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080066import android.view.View;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070067import android.view.ViewConfiguration;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080068import android.view.ViewGroup;
69import android.view.WindowManager;
Adrian Roosd4d61362014-06-25 23:30:50 +020070import android.view.WindowManagerGlobal;
Jeff Brown7304c342012-05-11 18:42:42 -070071import android.view.WindowManagerPolicy.WindowManagerFuncs;
Jason Monk30443b12014-09-23 09:09:52 -040072import android.view.accessibility.AccessibilityEvent;
Dianne Hackborn19caadc2012-04-20 17:49:10 -070073import android.widget.AdapterView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080074import android.widget.BaseAdapter;
75import android.widget.ImageView;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070076import android.widget.ImageView.ScaleType;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -070077import android.widget.ListView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080078import android.widget.TextView;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080079
80import java.util.ArrayList;
Amith Yamasani742a6712011-05-04 14:49:28 -070081import java.util.List;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080082
83/**
84 * Helper to show the global actions dialog. Each item is an {@link Action} that
85 * may show depending on whether the keyguard is showing, and whether the device
86 * is provisioned.
87 */
88class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
89
90 private static final String TAG = "GlobalActions";
91
Amith Yamasani6243edd2011-12-05 19:58:48 -080092 private static final boolean SHOW_SILENT_TOGGLE = true;
Amith Yamasani2ef6f1b2011-12-01 14:01:30 -080093
Justin Koh3b9a3cc2014-04-21 17:50:40 -070094 /* Valid settings for global actions keys.
95 * see config.xml config_globalActionList */
96 private static final String GLOBAL_ACTION_KEY_POWER = "power";
97 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
98 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
99 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
100 private static final String GLOBAL_ACTION_KEY_USERS = "users";
101 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
Adrian Roosd4d61362014-06-25 23:30:50 +0200102 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
Bryce Leecc8b4672015-02-05 18:39:01 -0800103 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
Bryce Leed8379b92015-03-19 00:32:17 +0000104 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
Alison Cichowlasf8eca402016-08-05 14:50:27 -0400105 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
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;
Selim Cinek705442f2016-09-13 16:02:33 -0700127 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800128
129 /**
130 * @param context everything needs a context :(
131 */
Jeff Brown7304c342012-05-11 18:42:42 -0700132 public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800133 mContext = context;
Jeff Brown7304c342012-05-11 18:42:42 -0700134 mWindowManagerFuncs = windowManagerFuncs;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800135 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
John Spurlock542a51c2012-10-03 16:54:23 -0400136 mDreamManager = IDreamManager.Stub.asInterface(
137 ServiceManager.getService(DreamService.DREAM_SERVICE));
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800138
139 // receive broadcasts
140 IntentFilter filter = new IntentFilter();
141 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
Joe Onorato78866b12009-07-17 13:09:27 -0400142 filter.addAction(Intent.ACTION_SCREEN_OFF);
Jinghui Guofe1fee52009-09-17 14:05:27 -0500143 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800144 context.registerReceiver(mBroadcastReceiver, filter);
145
nagarwc3b7e6f2014-02-20 17:10:46 +0530146 ConnectivityManager cm = (ConnectivityManager)
147 context.getSystemService(Context.CONNECTIVITY_SERVICE);
148 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
149
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800150 // get notified of phone state changes
151 TelephonyManager telephonyManager =
152 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
153 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700154 mContext.getContentResolver().registerContentObserver(
Christopher Tatec09cdce2012-09-10 16:50:14 -0700155 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700156 mAirplaneModeObserver);
157 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
158 mHasVibrator = vibrator != null && vibrator.hasVibrator();
Eric Laurent83a017b2013-03-19 18:15:31 -0700159
160 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
161 com.android.internal.R.bool.config_useFixedVolume);
Selim Cinek705442f2016-09-13 16:02:33 -0700162
163 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800164 }
165
166 /**
167 * Show the global actions dialog (creating if necessary)
168 * @param keyguardShowing True if keyguard is showing
169 */
170 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
171 mKeyguardShowing = keyguardShowing;
172 mDeviceProvisioned = isDeviceProvisioned;
Amith Yamasani742a6712011-05-04 14:49:28 -0700173 if (mDialog != null) {
174 mDialog.dismiss();
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700175 mDialog = null;
176 // Show delayed, so that the dismiss of the previous dialog completes
177 mHandler.sendEmptyMessage(MESSAGE_SHOW);
178 } else {
179 handleShow();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800180 }
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700181 }
182
John Spurlock542a51c2012-10-03 16:54:23 -0400183 private void awakenIfNecessary() {
184 if (mDreamManager != null) {
185 try {
186 if (mDreamManager.isDreaming()) {
187 mDreamManager.awaken();
188 }
189 } catch (RemoteException e) {
190 // we tried
191 }
192 }
193 }
194
Amith Yamasani2efb6a92012-04-25 17:31:03 -0700195 private void handleShow() {
John Spurlock542a51c2012-10-03 16:54:23 -0400196 awakenIfNecessary();
Amith Yamasani742a6712011-05-04 14:49:28 -0700197 mDialog = createDialog();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800198 prepareDialog();
199
Justin Koh1c46c2d782014-05-12 16:00:36 -0700200 // If we only have 1 item and it's a simple press action, just do this action.
201 if (mAdapter.getCount() == 1
John Spurlock822e5cc2014-05-30 17:48:54 -0400202 && mAdapter.getItem(0) instanceof SinglePressAction
203 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
Justin Koh1c46c2d782014-05-12 16:00:36 -0700204 ((SinglePressAction) mAdapter.getItem(0)).onPress();
205 } else {
206 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
207 attrs.setTitle("GlobalActions");
208 mDialog.getWindow().setAttributes(attrs);
209 mDialog.show();
210 mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
211 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800212 }
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700213
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800214 /**
215 * Create the global actions dialog.
216 * @return A new dialog.
217 */
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -0700218 private GlobalActionsDialog createDialog() {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700219 // Simple toggle style if there's no vibrator, otherwise use a tri-state
220 if (!mHasVibrator) {
221 mSilentModeAction = new SilentModeToggleAction();
222 } else {
223 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
224 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800225 mAirplaneModeOn = new ToggleAction(
226 R.drawable.ic_lock_airplane_mode,
227 R.drawable.ic_lock_airplane_mode_off,
228 R.string.global_actions_toggle_airplane_mode,
229 R.string.global_actions_airplane_mode_on_status,
230 R.string.global_actions_airplane_mode_off_status) {
231
232 void onToggle(boolean on) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700233 if (mHasTelephony && Boolean.parseBoolean(
Jinghui Guofe1fee52009-09-17 14:05:27 -0500234 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
235 mIsWaitingForEcmExit = true;
236 // Launch ECM exit dialog
237 Intent ecmDialogIntent =
238 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
239 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
240 mContext.startActivity(ecmDialogIntent);
241 } else {
242 changeAirplaneModeSystemSetting(on);
243 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800244 }
245
246 @Override
247 protected void changeStateFromPress(boolean buttonOn) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700248 if (!mHasTelephony) return;
249
Jinghui Guofe1fee52009-09-17 14:05:27 -0500250 // In ECM mode airplane state cannot be changed
251 if (!(Boolean.parseBoolean(
252 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
253 mState = buttonOn ? State.TurningOn : State.TurningOff;
254 mAirplaneState = mState;
255 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800256 }
257
258 public boolean showDuringKeyguard() {
259 return true;
260 }
261
262 public boolean showBeforeProvisioning() {
263 return false;
264 }
265 };
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700266 onAirplaneModeChanged();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800267
Amith Yamasani2ef6f1b2011-12-01 14:01:30 -0800268 mItems = new ArrayList<Action>();
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700269 String[] defaultActions = mContext.getResources().getStringArray(
270 com.android.internal.R.array.config_globalActionsList);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800271
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700272 ArraySet<String> addedKeys = new ArraySet<String>();
273 for (int i = 0; i < defaultActions.length; i++) {
274 String actionKey = defaultActions[i];
275 if (addedKeys.contains(actionKey)) {
276 // If we already have added this, don't add it again.
277 continue;
278 }
279 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
John Spurlock822e5cc2014-05-30 17:48:54 -0400280 mItems.add(new PowerAction());
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700281 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
282 mItems.add(mAirplaneModeOn);
John Spurlock68539a62014-08-20 10:36:33 -0400283 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
284 if (Settings.Global.getInt(mContext.getContentResolver(),
285 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
Felipe Lemee974f7e2015-12-15 14:15:56 -0800286 mItems.add(new BugReportAction());
John Spurlock68539a62014-08-20 10:36:33 -0400287 }
288 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
289 if (mShowSilentToggle) {
290 mItems.add(mSilentModeAction);
291 }
292 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
293 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
294 addUsersToMenu(mItems);
295 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700296 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
297 mItems.add(getSettingsAction());
Adrian Roos4ebcdfd2014-08-12 23:33:49 +0200298 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Adrian Roosd4d61362014-06-25 23:30:50 +0200299 mItems.add(getLockdownAction());
Bryce Leecc8b4672015-02-05 18:39:01 -0800300 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
301 mItems.add(getVoiceAssistAction());
Bryce Leed8379b92015-03-19 00:32:17 +0000302 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
303 mItems.add(getAssistAction());
Alison Cichowlasf8eca402016-08-05 14:50:27 -0400304 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
305 mItems.add(new RestartAction());
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700306 } else {
307 Log.e(TAG, "Invalid global action key " + actionKey);
308 }
309 // Add here so we don't add more than one.
310 addedKeys.add(actionKey);
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700311 }
312
Selim Cinek705442f2016-09-13 16:02:33 -0700313 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
314 mItems.add(getEmergencyAction());
315 }
316
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700317 mAdapter = new MyAdapter();
318
319 AlertParams params = new AlertParams(mContext);
320 params.mAdapter = mAdapter;
321 params.mOnClickListener = this;
322 params.mForceInverseBackground = true;
323
324 GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
325 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
326
327 dialog.getListView().setItemsCanFocus(true);
328 dialog.getListView().setLongClickable(true);
329 dialog.getListView().setOnItemLongClickListener(
330 new AdapterView.OnItemLongClickListener() {
331 @Override
332 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
333 long id) {
John Spurlock822e5cc2014-05-30 17:48:54 -0400334 final Action action = mAdapter.getItem(position);
335 if (action instanceof LongPressAction) {
336 return ((LongPressAction) action).onLongPress();
337 }
338 return false;
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700339 }
340 });
Dianne Hackbornbef0f612012-10-16 18:34:48 -0700341 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700342
343 dialog.setOnDismissListener(this);
344
345 return dialog;
346 }
347
John Spurlock822e5cc2014-05-30 17:48:54 -0400348 private final class PowerAction extends SinglePressAction implements LongPressAction {
349 private PowerAction() {
350 super(com.android.internal.R.drawable.ic_lock_power_off,
351 R.string.global_action_power_off);
352 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700353
John Spurlock822e5cc2014-05-30 17:48:54 -0400354 @Override
355 public boolean onLongPress() {
Benjamin Franzbff46ba2015-03-05 18:33:51 +0000356 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
357 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
358 mWindowManagerFuncs.rebootSafeMode(true);
359 return true;
360 }
361 return false;
John Spurlock822e5cc2014-05-30 17:48:54 -0400362 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700363
John Spurlock822e5cc2014-05-30 17:48:54 -0400364 @Override
365 public boolean showDuringKeyguard() {
366 return true;
367 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700368
John Spurlock822e5cc2014-05-30 17:48:54 -0400369 @Override
370 public boolean showBeforeProvisioning() {
371 return true;
372 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700373
John Spurlock822e5cc2014-05-30 17:48:54 -0400374 @Override
375 public void onPress() {
376 // shutdown by making sure radio and power are handled accordingly.
377 mWindowManagerFuncs.shutdown(false /* confirm */);
378 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700379 }
380
Alison Cichowlasf8eca402016-08-05 14:50:27 -0400381 private final class RestartAction extends SinglePressAction implements LongPressAction {
382 private RestartAction() {
383 super(R.drawable.ic_restart, R.string.global_action_restart);
384 }
385
386 @Override
387 public boolean onLongPress() {
388 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
389 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
390 mWindowManagerFuncs.rebootSafeMode(true);
391 return true;
392 }
393 return false;
394 }
395
396 @Override
397 public boolean showDuringKeyguard() {
398 return true;
399 }
400
401 @Override
402 public boolean showBeforeProvisioning() {
403 return true;
404 }
405
406 @Override
407 public void onPress() {
408 mWindowManagerFuncs.reboot(false /* confirm */);
409 }
410 }
411
412
Felipe Lemee974f7e2015-12-15 14:15:56 -0800413 private class BugReportAction extends SinglePressAction implements LongPressAction {
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700414
Felipe Lemee974f7e2015-12-15 14:15:56 -0800415 public BugReportAction() {
416 super(com.android.internal.R.drawable.ic_lock_bugreport, R.string.bugreport_title);
417 }
418
419 @Override
420 public void onPress() {
421 // don't actually trigger the bugreport if we are running stability
422 // tests via monkey
423 if (ActivityManager.isUserAMonkey()) {
424 return;
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700425 }
Felipe Lemee974f7e2015-12-15 14:15:56 -0800426 // Add a little delay before executing, to give the
427 // dialog a chance to go away before it takes a
428 // screenshot.
Felipe Lemee974f7e2015-12-15 14:15:56 -0800429 mHandler.postDelayed(new Runnable() {
430 @Override
431 public void run() {
432 try {
Felipe Leme43b013f2015-12-22 14:09:25 -0800433 // Take an "interactive" bugreport.
Felipe Lemed426d12b2016-02-23 10:28:55 -0800434 MetricsLogger.action(mContext,
Felipe Leme6605bd82016-02-22 15:22:20 -0800435 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800436 ActivityManager.getService().requestBugReport(
Michal Karpinski3da5c972015-12-11 18:16:30 +0000437 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
Felipe Lemee974f7e2015-12-15 14:15:56 -0800438 } catch (RemoteException e) {
439 }
440 }
441 }, 500);
442 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700443
Felipe Lemee974f7e2015-12-15 14:15:56 -0800444 @Override
445 public boolean onLongPress() {
446 // don't actually trigger the bugreport if we are running stability
447 // tests via monkey
448 if (ActivityManager.isUserAMonkey()) {
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700449 return false;
450 }
Felipe Lemee974f7e2015-12-15 14:15:56 -0800451 try {
Felipe Leme43b013f2015-12-22 14:09:25 -0800452 // Take a "full" bugreport.
Felipe Lemed426d12b2016-02-23 10:28:55 -0800453 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800454 ActivityManager.getService().requestBugReport(
Michal Karpinski3da5c972015-12-11 18:16:30 +0000455 ActivityManager.BUGREPORT_OPTION_FULL);
Felipe Lemee974f7e2015-12-15 14:15:56 -0800456 } catch (RemoteException e) {
John Spurlock887aa002014-06-02 14:10:41 -0400457 }
Felipe Leme43b013f2015-12-22 14:09:25 -0800458 return false;
Felipe Lemee974f7e2015-12-15 14:15:56 -0800459 }
460
461 public boolean showDuringKeyguard() {
462 return true;
463 }
464
465 @Override
466 public boolean showBeforeProvisioning() {
467 return false;
468 }
469
470 @Override
471 public String getStatus() {
472 return mContext.getString(
473 com.android.internal.R.string.bugreport_status,
474 Build.VERSION.RELEASE,
475 Build.ID);
476 }
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700477 }
478
479 private Action getSettingsAction() {
480 return new SinglePressAction(com.android.internal.R.drawable.ic_settings,
481 R.string.global_action_settings) {
482
483 @Override
484 public void onPress() {
485 Intent intent = new Intent(Settings.ACTION_SETTINGS);
Wei Liu636f3ea2014-09-03 22:10:57 -0700486 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700487 mContext.startActivity(intent);
488 }
489
490 @Override
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700491 public boolean showDuringKeyguard() {
492 return true;
493 }
494
495 @Override
496 public boolean showBeforeProvisioning() {
Justin Koh1c46c2d782014-05-12 16:00:36 -0700497 return true;
Justin Koh3b9a3cc2014-04-21 17:50:40 -0700498 }
499 };
500 }
501
Selim Cinek705442f2016-09-13 16:02:33 -0700502 private Action getEmergencyAction() {
503 return new SinglePressAction(com.android.internal.R.drawable.emergency_icon,
504 R.string.global_action_emergency) {
505 @Override
506 public void onPress() {
507 mEmergencyAffordanceManager.performEmergencyCall();
508 }
509
510 @Override
511 public boolean showDuringKeyguard() {
512 return true;
513 }
514
515 @Override
516 public boolean showBeforeProvisioning() {
517 return true;
518 }
519 };
520 }
521
Bryce Leed8379b92015-03-19 00:32:17 +0000522 private Action getAssistAction() {
523 return new SinglePressAction(com.android.internal.R.drawable.ic_action_assist_focused,
524 R.string.global_action_assist) {
525 @Override
526 public void onPress() {
527 Intent intent = new Intent(Intent.ACTION_ASSIST);
528 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
529 mContext.startActivity(intent);
530 }
531
532 @Override
533 public boolean showDuringKeyguard() {
534 return true;
535 }
536
537 @Override
538 public boolean showBeforeProvisioning() {
539 return true;
540 }
541 };
542 }
543
Bryce Leecc8b4672015-02-05 18:39:01 -0800544 private Action getVoiceAssistAction() {
545 return new SinglePressAction(com.android.internal.R.drawable.ic_voice_search,
546 R.string.global_action_voice_assist) {
547 @Override
548 public void onPress() {
549 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
550 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
551 mContext.startActivity(intent);
552 }
553
554 @Override
555 public boolean showDuringKeyguard() {
556 return true;
557 }
558
559 @Override
560 public boolean showBeforeProvisioning() {
561 return true;
562 }
563 };
564 }
565
Adrian Roosd4d61362014-06-25 23:30:50 +0200566 private Action getLockdownAction() {
567 return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock,
568 R.string.global_action_lockdown) {
569
570 @Override
571 public void onPress() {
572 new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
573 try {
574 WindowManagerGlobal.getWindowManagerService().lockNow(null);
575 } catch (RemoteException e) {
576 Log.e(TAG, "Error while trying to lock device.", e);
577 }
578 }
579
580 @Override
581 public boolean showDuringKeyguard() {
582 return true;
583 }
584
585 @Override
586 public boolean showBeforeProvisioning() {
587 return false;
588 }
589 };
590 }
591
Amith Yamasani3df21722013-06-10 13:43:10 -0700592 private UserInfo getCurrentUser() {
593 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800594 return ActivityManager.getService().getCurrentUser();
Amith Yamasani3df21722013-06-10 13:43:10 -0700595 } catch (RemoteException re) {
596 return null;
597 }
598 }
599
600 private boolean isCurrentUserOwner() {
601 UserInfo currentUser = getCurrentUser();
602 return currentUser == null || currentUser.isPrimary();
603 }
604
Amith Yamasanibc4ba822012-09-28 10:14:01 -0700605 private void addUsersToMenu(ArrayList<Action> items) {
Kenny Guy1a447532014-02-20 21:55:32 +0000606 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
607 if (um.isUserSwitcherEnabled()) {
608 List<UserInfo> users = um.getUsers();
Amith Yamasani3df21722013-06-10 13:43:10 -0700609 UserInfo currentUser = getCurrentUser();
Amith Yamasani742a6712011-05-04 14:49:28 -0700610 for (final UserInfo user : users) {
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700611 if (user.supportsSwitchToByUser()) {
Kenny Guy1a447532014-02-20 21:55:32 +0000612 boolean isCurrentUser = currentUser == null
613 ? user.id == 0 : (currentUser.id == user.id);
614 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
615 : null;
616 SinglePressAction switchToUser = new SinglePressAction(
617 com.android.internal.R.drawable.ic_menu_cc, icon,
618 (user.name != null ? user.name : "Primary")
619 + (isCurrentUser ? " \u2714" : "")) {
620 public void onPress() {
621 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800622 ActivityManager.getService().switchUser(user.id);
Kenny Guy1a447532014-02-20 21:55:32 +0000623 } catch (RemoteException re) {
624 Log.e(TAG, "Couldn't switch user " + re);
625 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700626 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700627
Kenny Guy1a447532014-02-20 21:55:32 +0000628 public boolean showDuringKeyguard() {
629 return true;
630 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700631
Kenny Guy1a447532014-02-20 21:55:32 +0000632 public boolean showBeforeProvisioning() {
633 return false;
634 }
635 };
636 items.add(switchToUser);
637 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700638 }
639 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800640 }
641
642 private void prepareDialog() {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700643 refreshSilentMode();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800644 mAirplaneModeOn.updateState(mAirplaneState);
645 mAdapter.notifyDataSetChanged();
Dianne Hackbornbef0f612012-10-16 18:34:48 -0700646 mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Eric Laurent83a017b2013-03-19 18:15:31 -0700647 if (mShowSilentToggle) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800648 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
649 mContext.registerReceiver(mRingerModeReceiver, filter);
650 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800651 }
652
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700653 private void refreshSilentMode() {
654 if (!mHasVibrator) {
655 final boolean silentModeOn =
656 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
657 ((ToggleAction)mSilentModeAction).updateState(
658 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
659 }
660 }
661
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800662 /** {@inheritDoc} */
663 public void onDismiss(DialogInterface dialog) {
Eric Laurent83a017b2013-03-19 18:15:31 -0700664 if (mShowSilentToggle) {
Masanori Ogino7d4adea2012-10-11 18:51:35 +0900665 try {
666 mContext.unregisterReceiver(mRingerModeReceiver);
667 } catch (IllegalArgumentException ie) {
668 // ignore this
669 Log.w(TAG, ie);
670 }
Amith Yamasani6243edd2011-12-05 19:58:48 -0800671 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800672 }
673
674 /** {@inheritDoc} */
675 public void onClick(DialogInterface dialog, int which) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700676 if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800677 dialog.dismiss();
678 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800679 mAdapter.getItem(which).onPress();
680 }
681
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800682 /**
683 * The adapter used for the list within the global actions dialog, taking
684 * into account whether the keyguard is showing via
685 * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
686 * via {@link GlobalActions#mDeviceProvisioned}.
687 */
688 private class MyAdapter extends BaseAdapter {
689
690 public int getCount() {
691 int count = 0;
692
693 for (int i = 0; i < mItems.size(); i++) {
694 final Action action = mItems.get(i);
695
696 if (mKeyguardShowing && !action.showDuringKeyguard()) {
697 continue;
698 }
699 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
700 continue;
701 }
702 count++;
703 }
704 return count;
705 }
706
707 @Override
708 public boolean isEnabled(int position) {
709 return getItem(position).isEnabled();
710 }
711
712 @Override
713 public boolean areAllItemsEnabled() {
714 return false;
715 }
716
717 public Action getItem(int position) {
718
719 int filteredPos = 0;
720 for (int i = 0; i < mItems.size(); i++) {
721 final Action action = mItems.get(i);
722 if (mKeyguardShowing && !action.showDuringKeyguard()) {
723 continue;
724 }
725 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
726 continue;
727 }
728 if (filteredPos == position) {
729 return action;
730 }
731 filteredPos++;
732 }
733
Bernhard Reutner-Fischerc1a9b032011-04-28 20:06:44 +0200734 throw new IllegalArgumentException("position " + position
735 + " out of range of showable actions"
736 + ", filtered count=" + getCount()
737 + ", keyguardshowing=" + mKeyguardShowing
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800738 + ", provisioned=" + mDeviceProvisioned);
739 }
740
741
742 public long getItemId(int position) {
743 return position;
744 }
745
746 public View getView(int position, View convertView, ViewGroup parent) {
747 Action action = getItem(position);
748 return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
749 }
750 }
751
752 // note: the scheme below made more sense when we were planning on having
753 // 8 different things in the global actions dialog. seems overkill with
754 // only 3 items now, but may as well keep this flexible approach so it will
755 // be easy should someone decide at the last minute to include something
756 // else, such as 'enable wifi', or 'enable bluetooth'
757
758 /**
759 * What each item in the global actions dialog must be able to support.
760 */
761 private interface Action {
Jason Monk30443b12014-09-23 09:09:52 -0400762 /**
763 * @return Text that will be announced when dialog is created. null
764 * for none.
765 */
766 CharSequence getLabelForAccessibility(Context context);
767
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800768 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
769
770 void onPress();
771
772 /**
773 * @return whether this action should appear in the dialog when the keygaurd
774 * is showing.
775 */
776 boolean showDuringKeyguard();
777
778 /**
779 * @return whether this action should appear in the dialog before the
780 * device is provisioned.
781 */
782 boolean showBeforeProvisioning();
783
784 boolean isEnabled();
785 }
786
787 /**
John Spurlock822e5cc2014-05-30 17:48:54 -0400788 * An action that also supports long press.
789 */
790 private interface LongPressAction extends Action {
791 boolean onLongPress();
792 }
793
794 /**
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800795 * A single press action maintains no state, just responds to a press
796 * and takes an action.
797 */
798 private static abstract class SinglePressAction implements Action {
799 private final int mIconResId;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700800 private final Drawable mIcon;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800801 private final int mMessageResId;
Amith Yamasani742a6712011-05-04 14:49:28 -0700802 private final CharSequence mMessage;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800803
804 protected SinglePressAction(int iconResId, int messageResId) {
805 mIconResId = iconResId;
806 mMessageResId = messageResId;
Amith Yamasani742a6712011-05-04 14:49:28 -0700807 mMessage = null;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700808 mIcon = null;
809 }
810
811 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
812 mIconResId = iconResId;
813 mMessageResId = 0;
814 mMessage = message;
815 mIcon = icon;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800816 }
817
818 public boolean isEnabled() {
819 return true;
820 }
821
John Spurlock887aa002014-06-02 14:10:41 -0400822 public String getStatus() {
823 return null;
824 }
825
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800826 abstract public void onPress();
827
Jason Monk30443b12014-09-23 09:09:52 -0400828 public CharSequence getLabelForAccessibility(Context context) {
829 if (mMessage != null) {
830 return mMessage;
831 } else {
832 return context.getString(mMessageResId);
833 }
834 }
835
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800836 public View create(
837 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Amith Yamasani6243edd2011-12-05 19:58:48 -0800838 View v = inflater.inflate(R.layout.global_actions_item, parent, false);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800839
840 ImageView icon = (ImageView) v.findViewById(R.id.icon);
841 TextView messageView = (TextView) v.findViewById(R.id.message);
842
John Spurlock887aa002014-06-02 14:10:41 -0400843 TextView statusView = (TextView) v.findViewById(R.id.status);
844 final String status = getStatus();
845 if (!TextUtils.isEmpty(status)) {
846 statusView.setText(status);
847 } else {
848 statusView.setVisibility(View.GONE);
849 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700850 if (mIcon != null) {
851 icon.setImageDrawable(mIcon);
852 icon.setScaleType(ScaleType.CENTER_CROP);
853 } else if (mIconResId != 0) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800854 icon.setImageDrawable(context.getDrawable(mIconResId));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700855 }
Amith Yamasani742a6712011-05-04 14:49:28 -0700856 if (mMessage != null) {
857 messageView.setText(mMessage);
858 } else {
859 messageView.setText(mMessageResId);
860 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800861
862 return v;
863 }
864 }
865
866 /**
867 * A toggle action knows whether it is on or off, and displays an icon
868 * and status message accordingly.
869 */
870 private static abstract class ToggleAction implements Action {
871
872 enum State {
873 Off(false),
874 TurningOn(true),
875 TurningOff(true),
876 On(false);
877
878 private final boolean inTransition;
879
880 State(boolean intermediate) {
881 inTransition = intermediate;
882 }
883
884 public boolean inTransition() {
885 return inTransition;
886 }
887 }
888
889 protected State mState = State.Off;
890
891 // prefs
Daniel Sandler82d6c682010-02-26 15:16:54 -0500892 protected int mEnabledIconResId;
893 protected int mDisabledIconResid;
894 protected int mMessageResId;
895 protected int mEnabledStatusMessageResId;
896 protected int mDisabledStatusMessageResId;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800897
898 /**
899 * @param enabledIconResId The icon for when this action is on.
900 * @param disabledIconResid The icon for when this action is off.
901 * @param essage The general information message, e.g 'Silent Mode'
902 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
903 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
904 */
905 public ToggleAction(int enabledIconResId,
906 int disabledIconResid,
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700907 int message,
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800908 int enabledStatusMessageResId,
909 int disabledStatusMessageResId) {
910 mEnabledIconResId = enabledIconResId;
911 mDisabledIconResid = disabledIconResid;
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700912 mMessageResId = message;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800913 mEnabledStatusMessageResId = enabledStatusMessageResId;
914 mDisabledStatusMessageResId = disabledStatusMessageResId;
915 }
916
Daniel Sandler82d6c682010-02-26 15:16:54 -0500917 /**
918 * Override to make changes to resource IDs just before creating the
919 * View.
920 */
921 void willCreate() {
922
923 }
924
Jason Monk30443b12014-09-23 09:09:52 -0400925 @Override
926 public CharSequence getLabelForAccessibility(Context context) {
927 return context.getString(mMessageResId);
928 }
929
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800930 public View create(Context context, View convertView, ViewGroup parent,
931 LayoutInflater inflater) {
Daniel Sandler82d6c682010-02-26 15:16:54 -0500932 willCreate();
933
Amith Yamasani6243edd2011-12-05 19:58:48 -0800934 View v = inflater.inflate(R
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800935 .layout.global_actions_item, parent, false);
936
937 ImageView icon = (ImageView) v.findViewById(R.id.icon);
938 TextView messageView = (TextView) v.findViewById(R.id.message);
939 TextView statusView = (TextView) v.findViewById(R.id.status);
Amith Yamasani6243edd2011-12-05 19:58:48 -0800940 final boolean enabled = isEnabled();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800941
Amith Yamasani6243edd2011-12-05 19:58:48 -0800942 if (messageView != null) {
943 messageView.setText(mMessageResId);
944 messageView.setEnabled(enabled);
945 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800946
947 boolean on = ((mState == State.On) || (mState == State.TurningOn));
Amith Yamasani6243edd2011-12-05 19:58:48 -0800948 if (icon != null) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800949 icon.setImageDrawable(context.getDrawable(
Amith Yamasani6243edd2011-12-05 19:58:48 -0800950 (on ? mEnabledIconResId : mDisabledIconResid)));
951 icon.setEnabled(enabled);
952 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800953
Amith Yamasani6243edd2011-12-05 19:58:48 -0800954 if (statusView != null) {
955 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
956 statusView.setVisibility(View.VISIBLE);
957 statusView.setEnabled(enabled);
958 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800959 v.setEnabled(enabled);
960
961 return v;
962 }
963
964 public final void onPress() {
965 if (mState.inTransition()) {
966 Log.w(TAG, "shouldn't be able to toggle when in transition");
967 return;
968 }
969
970 final boolean nowOn = !(mState == State.On);
971 onToggle(nowOn);
972 changeStateFromPress(nowOn);
973 }
974
975 public boolean isEnabled() {
976 return !mState.inTransition();
977 }
978
979 /**
980 * Implementations may override this if their state can be in on of the intermediate
981 * states until some notification is received (e.g airplane mode is 'turning off' until
982 * we know the wireless connections are back online
983 * @param buttonOn Whether the button was turned on or off
984 */
985 protected void changeStateFromPress(boolean buttonOn) {
986 mState = buttonOn ? State.On : State.Off;
987 }
988
989 abstract void onToggle(boolean on);
990
991 public void updateState(State state) {
992 mState = state;
993 }
994 }
995
Amith Yamasani7ee994d2012-05-24 13:53:26 -0700996 private class SilentModeToggleAction extends ToggleAction {
997 public SilentModeToggleAction() {
998 super(R.drawable.ic_audio_vol_mute,
999 R.drawable.ic_audio_vol,
1000 R.string.global_action_toggle_silent_mode,
1001 R.string.global_action_silent_mode_on_status,
1002 R.string.global_action_silent_mode_off_status);
1003 }
1004
1005 void onToggle(boolean on) {
1006 if (on) {
1007 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1008 } else {
1009 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1010 }
1011 }
1012
1013 public boolean showDuringKeyguard() {
1014 return true;
1015 }
1016
1017 public boolean showBeforeProvisioning() {
1018 return false;
1019 }
1020 }
1021
1022 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
Amith Yamasani6243edd2011-12-05 19:58:48 -08001023
1024 private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
1025
1026 private final AudioManager mAudioManager;
1027 private final Handler mHandler;
Amith Yamasani13593602012-03-22 16:16:17 -07001028 private final Context mContext;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001029
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001030 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
Amith Yamasani6243edd2011-12-05 19:58:48 -08001031 mAudioManager = audioManager;
1032 mHandler = handler;
Amith Yamasani13593602012-03-22 16:16:17 -07001033 mContext = context;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001034 }
1035
1036 private int ringerModeToIndex(int ringerMode) {
1037 // They just happen to coincide
1038 return ringerMode;
1039 }
1040
1041 private int indexToRingerMode(int index) {
1042 // They just happen to coincide
1043 return index;
1044 }
1045
Jason Monk30443b12014-09-23 09:09:52 -04001046 @Override
1047 public CharSequence getLabelForAccessibility(Context context) {
1048 return null;
1049 }
1050
Amith Yamasani6243edd2011-12-05 19:58:48 -08001051 public View create(Context context, View convertView, ViewGroup parent,
1052 LayoutInflater inflater) {
1053 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
Amith Yamasani6243edd2011-12-05 19:58:48 -08001054
1055 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1056 for (int i = 0; i < 3; i++) {
1057 View itemView = v.findViewById(ITEM_IDS[i]);
1058 itemView.setSelected(selectedIndex == i);
1059 // Set up click handler
1060 itemView.setTag(i);
1061 itemView.setOnClickListener(this);
1062 }
1063 return v;
1064 }
1065
1066 public void onPress() {
1067 }
1068
1069 public boolean showDuringKeyguard() {
1070 return true;
1071 }
1072
1073 public boolean showBeforeProvisioning() {
1074 return false;
1075 }
1076
1077 public boolean isEnabled() {
1078 return true;
1079 }
1080
1081 void willCreate() {
1082 }
1083
1084 public void onClick(View v) {
1085 if (!(v.getTag() instanceof Integer)) return;
1086
1087 int index = (Integer) v.getTag();
1088 mAudioManager.setRingerMode(indexToRingerMode(index));
1089 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1090 }
1091 }
1092
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001093 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1094 public void onReceive(Context context, Intent intent) {
1095 String action = intent.getAction();
Joe Onorato78866b12009-07-17 13:09:27 -04001096 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1097 || Intent.ACTION_SCREEN_OFF.equals(action)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001098 String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
Joe Onorato78866b12009-07-17 13:09:27 -04001099 if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001100 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
1101 }
Jinghui Guofe1fee52009-09-17 14:05:27 -05001102 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1103 // Airplane mode can be changed after ECM exits if airplane toggle button
1104 // is pressed during ECM mode
1105 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1106 mIsWaitingForEcmExit) {
1107 mIsWaitingForEcmExit = false;
1108 changeAirplaneModeSystemSetting(true);
1109 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001110 }
1111 }
1112 };
1113
1114 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1115 @Override
1116 public void onServiceStateChanged(ServiceState serviceState) {
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001117 if (!mHasTelephony) return;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001118 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1119 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1120 mAirplaneModeOn.updateState(mAirplaneState);
1121 mAdapter.notifyDataSetChanged();
1122 }
1123 };
1124
Amith Yamasani6243edd2011-12-05 19:58:48 -08001125 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1126 @Override
1127 public void onReceive(Context context, Intent intent) {
1128 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1129 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1130 }
1131 }
1132 };
1133
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001134 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1135 @Override
1136 public void onChange(boolean selfChange) {
1137 onAirplaneModeChanged();
1138 }
1139 };
1140
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001141 private static final int MESSAGE_DISMISS = 0;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001142 private static final int MESSAGE_REFRESH = 1;
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001143 private static final int MESSAGE_SHOW = 2;
Amith Yamasani6243edd2011-12-05 19:58:48 -08001144 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1145
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001146 private Handler mHandler = new Handler() {
1147 public void handleMessage(Message msg) {
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001148 switch (msg.what) {
1149 case MESSAGE_DISMISS:
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001150 if (mDialog != null) {
1151 mDialog.dismiss();
Henrik Baardae155942013-07-23 09:31:57 +02001152 mDialog = null;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001153 }
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001154 break;
1155 case MESSAGE_REFRESH:
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001156 refreshSilentMode();
Amith Yamasani6243edd2011-12-05 19:58:48 -08001157 mAdapter.notifyDataSetChanged();
Amith Yamasani2efb6a92012-04-25 17:31:03 -07001158 break;
1159 case MESSAGE_SHOW:
1160 handleShow();
1161 break;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001162 }
1163 }
1164 };
Jinghui Guofe1fee52009-09-17 14:05:27 -05001165
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001166 private void onAirplaneModeChanged() {
1167 // Let the service state callbacks handle the state.
1168 if (mHasTelephony) return;
1169
Christopher Tatec09cdce2012-09-10 16:50:14 -07001170 boolean airplaneModeOn = Settings.Global.getInt(
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001171 mContext.getContentResolver(),
Christopher Tatec09cdce2012-09-10 16:50:14 -07001172 Settings.Global.AIRPLANE_MODE_ON,
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001173 0) == 1;
1174 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1175 mAirplaneModeOn.updateState(mAirplaneState);
1176 }
1177
Jinghui Guofe1fee52009-09-17 14:05:27 -05001178 /**
1179 * Change the airplane mode system setting
1180 */
1181 private void changeAirplaneModeSystemSetting(boolean on) {
Christopher Tatec09cdce2012-09-10 16:50:14 -07001182 Settings.Global.putInt(
Jinghui Guofe1fee52009-09-17 14:05:27 -05001183 mContext.getContentResolver(),
Christopher Tatec09cdce2012-09-10 16:50:14 -07001184 Settings.Global.AIRPLANE_MODE_ON,
Jinghui Guofe1fee52009-09-17 14:05:27 -05001185 on ? 1 : 0);
1186 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Dianne Hackborn818ba112009-12-11 11:12:02 -08001187 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Jinghui Guofe1fee52009-09-17 14:05:27 -05001188 intent.putExtra("state", on);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001189 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
Amith Yamasani7ee994d2012-05-24 13:53:26 -07001190 if (!mHasTelephony) {
1191 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1192 }
Jinghui Guofe1fee52009-09-17 14:05:27 -05001193 }
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001194
1195 private static final class GlobalActionsDialog extends Dialog implements DialogInterface {
1196 private final Context mContext;
1197 private final int mWindowTouchSlop;
1198 private final AlertController mAlert;
Jason Monk30443b12014-09-23 09:09:52 -04001199 private final MyAdapter mAdapter;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001200
1201 private EnableAccessibilityController mEnableAccessibilityController;
1202
1203 private boolean mIntercepted;
1204 private boolean mCancelOnUp;
1205
1206 public GlobalActionsDialog(Context context, AlertParams params) {
1207 super(context, getDialogTheme(context));
Alan Viverettee54d2452015-05-06 10:41:43 -07001208 mContext = getContext();
Michael Kwancc6e6f02016-05-16 12:54:57 -07001209 mAlert = AlertController.create(mContext, this, getWindow());
Jason Monk30443b12014-09-23 09:09:52 -04001210 mAdapter = (MyAdapter) params.mAdapter;
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001211 mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
1212 params.apply(mAlert);
1213 }
1214
1215 private static int getDialogTheme(Context context) {
1216 TypedValue outValue = new TypedValue();
1217 context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
1218 outValue, true);
1219 return outValue.resourceId;
1220 }
1221
1222 @Override
1223 protected void onStart() {
1224 // If global accessibility gesture can be performed, we will take care
1225 // of dismissing the dialog on touch outside. This is because the dialog
1226 // is dismissed on the first down while the global gesture is a long press
1227 // with two fingers anywhere on the screen.
1228 if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
Svetoslavdc6d1a92014-08-28 18:40:57 -07001229 mEnableAccessibilityController = new EnableAccessibilityController(mContext,
1230 new Runnable() {
1231 @Override
1232 public void run() {
1233 dismiss();
1234 }
1235 });
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001236 super.setCanceledOnTouchOutside(false);
1237 } else {
1238 mEnableAccessibilityController = null;
1239 super.setCanceledOnTouchOutside(true);
1240 }
Justin Koh1c46c2d782014-05-12 16:00:36 -07001241
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001242 super.onStart();
1243 }
1244
1245 @Override
1246 protected void onStop() {
1247 if (mEnableAccessibilityController != null) {
1248 mEnableAccessibilityController.onDestroy();
1249 }
1250 super.onStop();
1251 }
1252
1253 @Override
1254 public boolean dispatchTouchEvent(MotionEvent event) {
1255 if (mEnableAccessibilityController != null) {
1256 final int action = event.getActionMasked();
1257 if (action == MotionEvent.ACTION_DOWN) {
1258 View decor = getWindow().getDecorView();
1259 final int eventX = (int) event.getX();
1260 final int eventY = (int) event.getY();
1261 if (eventX < -mWindowTouchSlop
1262 || eventY < -mWindowTouchSlop
1263 || eventX >= decor.getWidth() + mWindowTouchSlop
1264 || eventY >= decor.getHeight() + mWindowTouchSlop) {
1265 mCancelOnUp = true;
1266 }
1267 }
1268 try {
1269 if (!mIntercepted) {
1270 mIntercepted = mEnableAccessibilityController.onInterceptTouchEvent(event);
1271 if (mIntercepted) {
1272 final long now = SystemClock.uptimeMillis();
1273 event = MotionEvent.obtain(now, now,
1274 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1275 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1276 mCancelOnUp = true;
1277 }
1278 } else {
1279 return mEnableAccessibilityController.onTouchEvent(event);
1280 }
1281 } finally {
1282 if (action == MotionEvent.ACTION_UP) {
1283 if (mCancelOnUp) {
1284 cancel();
1285 }
1286 mCancelOnUp = false;
1287 mIntercepted = false;
1288 }
1289 }
1290 }
1291 return super.dispatchTouchEvent(event);
1292 }
1293
1294 public ListView getListView() {
1295 return mAlert.getListView();
1296 }
1297
1298 @Override
1299 protected void onCreate(Bundle savedInstanceState) {
1300 super.onCreate(savedInstanceState);
1301 mAlert.installContent();
1302 }
1303
1304 @Override
Jason Monk30443b12014-09-23 09:09:52 -04001305 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1306 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1307 for (int i = 0; i < mAdapter.getCount(); ++i) {
1308 CharSequence label =
1309 mAdapter.getItem(i).getLabelForAccessibility(getContext());
1310 if (label != null) {
1311 event.getText().add(label);
1312 }
1313 }
1314 }
1315 return super.dispatchPopulateAccessibilityEvent(event);
1316 }
1317
1318 @Override
Svetoslav Ganov7befb7d2012-09-27 16:49:23 -07001319 public boolean onKeyDown(int keyCode, KeyEvent event) {
1320 if (mAlert.onKeyDown(keyCode, event)) {
1321 return true;
1322 }
1323 return super.onKeyDown(keyCode, event);
1324 }
1325
1326 @Override
1327 public boolean onKeyUp(int keyCode, KeyEvent event) {
1328 if (mAlert.onKeyUp(keyCode, event)) {
1329 return true;
1330 }
1331 return super.onKeyUp(keyCode, event);
1332 }
1333 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001334}