blob: 201f40e40f26cdb4aa5e0068ca4e8ad57a911993 [file] [log] [blame]
Jason Monk361915c2017-03-21 20:33:59 -04001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.globalactions;
16
Adrian Roosedfab3b2018-03-08 18:39:20 +010017import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Adrian Roos2f05bb32018-02-19 16:42:27 +010018import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
19
Chad Brubakerf4075fe2018-01-03 13:23:22 -080020import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
21import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -070022import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
23
Jason Monk361915c2017-03-21 20:33:59 -040024import android.app.ActivityManager;
25import android.app.Dialog;
Chad Brubakerf4075fe2018-01-03 13:23:22 -080026import android.app.KeyguardManager;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070027import android.app.WallpaperManager;
Alex Chau04458852017-11-27 18:21:23 +000028import android.app.admin.DevicePolicyManager;
Pavel Grafov059021b2018-05-02 13:44:46 +010029import android.app.trust.TrustManager;
Jason Monk361915c2017-03-21 20:33:59 -040030import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.pm.UserInfo;
36import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070037import android.graphics.Point;
Jason Monk361915c2017-03-21 20:33:59 -040038import android.graphics.drawable.Drawable;
39import android.media.AudioManager;
40import android.net.ConnectivityManager;
41import android.os.Build;
Jason Monk361915c2017-03-21 20:33:59 -040042import android.os.Handler;
43import android.os.Message;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.SystemProperties;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.os.Vibrator;
50import android.provider.Settings;
51import android.service.dreams.DreamService;
52import android.service.dreams.IDreamManager;
53import android.telephony.PhoneStateListener;
54import android.telephony.ServiceState;
55import android.telephony.TelephonyManager;
56import android.text.TextUtils;
57import android.util.ArraySet;
58import android.util.Log;
Lucas Dupin448786c2017-07-24 17:44:25 -070059import android.view.ContextThemeWrapper;
Jason Monk361915c2017-03-21 20:33:59 -040060import android.view.LayoutInflater;
61import android.view.View;
62import android.view.ViewGroup;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070063import android.view.Window;
Jason Monk361915c2017-03-21 20:33:59 -040064import android.view.WindowManager;
65import android.view.WindowManagerGlobal;
66import android.view.accessibility.AccessibilityEvent;
Jason Monk16fbd9d2017-04-27 14:28:49 -040067import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040068import android.widget.BaseAdapter;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +080069import android.widget.FrameLayout;
Jason Monk361915c2017-03-21 20:33:59 -040070import android.widget.ImageView;
71import android.widget.ImageView.ScaleType;
Jason Monk16fbd9d2017-04-27 14:28:49 -040072import android.widget.LinearLayout;
Jason Monk361915c2017-03-21 20:33:59 -040073import android.widget.TextView;
74
Charles He9851a8d2017-10-10 17:31:30 +010075import com.android.internal.R;
76import com.android.internal.colorextraction.ColorExtractor;
77import com.android.internal.colorextraction.ColorExtractor.GradientColors;
Lucas Dupine2292a92017-07-06 14:35:30 -070078import com.android.internal.colorextraction.drawable.GradientDrawable;
Charles He9851a8d2017-10-10 17:31:30 +010079import com.android.internal.logging.MetricsLogger;
80import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
81import com.android.internal.telephony.TelephonyIntents;
82import com.android.internal.telephony.TelephonyProperties;
83import com.android.internal.util.EmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050084import com.android.internal.util.ScreenshotHelper;
Charles He9851a8d2017-10-10 17:31:30 +010085import com.android.internal.widget.LockPatternUtils;
86import com.android.systemui.Dependency;
87import com.android.systemui.HardwareUiLayout;
88import com.android.systemui.Interpolators;
89import com.android.systemui.colorextraction.SysuiColorExtractor;
90import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
91import com.android.systemui.statusbar.phone.ScrimController;
Julia Reynolds42411922017-11-08 11:19:09 -050092import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070093
Jason Monk361915c2017-03-21 20:33:59 -040094import java.util.ArrayList;
95import java.util.List;
96
97/**
98 * Helper to show the global actions dialog. Each item is an {@link Action} that
99 * may show depending on whether the keyguard is showing, and whether the device
100 * is provisioned.
101 */
Charles He9851a8d2017-10-10 17:31:30 +0100102class GlobalActionsDialog implements DialogInterface.OnDismissListener,
103 DialogInterface.OnClickListener {
Jason Monk361915c2017-03-21 20:33:59 -0400104
105 static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
106 static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700107 static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
Jason Monk361915c2017-03-21 20:33:59 -0400108
109 private static final String TAG = "GlobalActionsDialog";
110
111 private static final boolean SHOW_SILENT_TOGGLE = true;
112
113 /* Valid settings for global actions keys.
114 * see config.xml config_globalActionList */
115 private static final String GLOBAL_ACTION_KEY_POWER = "power";
116 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
117 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
118 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
119 private static final String GLOBAL_ACTION_KEY_USERS = "users";
120 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
121 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
122 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
123 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
124 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
Alex Chau04458852017-11-27 18:21:23 +0000125 private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800126 private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500127 private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
Jason Monk361915c2017-03-21 20:33:59 -0400128
129 private final Context mContext;
130 private final GlobalActionsManager mWindowManagerFuncs;
131 private final AudioManager mAudioManager;
132 private final IDreamManager mDreamManager;
Alex Chau04458852017-11-27 18:21:23 +0000133 private final DevicePolicyManager mDevicePolicyManager;
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800134 private final LockPatternUtils mLockPatternUtils;
135 private final KeyguardManager mKeyguardManager;
Jason Monk361915c2017-03-21 20:33:59 -0400136
137 private ArrayList<Action> mItems;
138 private ActionsDialog mDialog;
139
140 private Action mSilentModeAction;
141 private ToggleAction mAirplaneModeOn;
142
143 private MyAdapter mAdapter;
144
145 private boolean mKeyguardShowing = false;
146 private boolean mDeviceProvisioned = false;
147 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
148 private boolean mIsWaitingForEcmExit = false;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800149 private boolean mHasFasterEmergencyButton;
Jason Monk361915c2017-03-21 20:33:59 -0400150 private boolean mHasTelephony;
151 private boolean mHasVibrator;
Alex Chau04458852017-11-27 18:21:23 +0000152 private boolean mHasLogoutButton;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800153 private boolean mHasLockdownButton;
Jason Monk361915c2017-03-21 20:33:59 -0400154 private final boolean mShowSilentToggle;
155 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500156 private final ScreenshotHelper mScreenshotHelper;
Jason Monk361915c2017-03-21 20:33:59 -0400157
158 /**
159 * @param context everything needs a context :(
160 */
161 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
Lucas Dupin448786c2017-07-24 17:44:25 -0700162 mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
Jason Monk361915c2017-03-21 20:33:59 -0400163 mWindowManagerFuncs = windowManagerFuncs;
164 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
165 mDreamManager = IDreamManager.Stub.asInterface(
166 ServiceManager.getService(DreamService.DREAM_SERVICE));
Alex Chau04458852017-11-27 18:21:23 +0000167 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
168 Context.DEVICE_POLICY_SERVICE);
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800169 mLockPatternUtils = new LockPatternUtils(mContext);
170 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Jason Monk361915c2017-03-21 20:33:59 -0400171
172 // receive broadcasts
173 IntentFilter filter = new IntentFilter();
174 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
175 filter.addAction(Intent.ACTION_SCREEN_OFF);
176 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
177 context.registerReceiver(mBroadcastReceiver, filter);
178
179 ConnectivityManager cm = (ConnectivityManager)
180 context.getSystemService(Context.CONNECTIVITY_SERVICE);
181 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
182
183 // get notified of phone state changes
184 TelephonyManager telephonyManager =
185 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
186 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
187 mContext.getContentResolver().registerContentObserver(
188 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
189 mAirplaneModeObserver);
190 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
191 mHasVibrator = vibrator != null && vibrator.hasVibrator();
192
193 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
194 R.bool.config_useFixedVolume);
195
196 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500197 mScreenshotHelper = new ScreenshotHelper(context);
Jason Monk361915c2017-03-21 20:33:59 -0400198 }
199
200 /**
201 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400202 *
Jason Monk361915c2017-03-21 20:33:59 -0400203 * @param keyguardShowing True if keyguard is showing
204 */
205 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
206 mKeyguardShowing = keyguardShowing;
207 mDeviceProvisioned = isDeviceProvisioned;
208 if (mDialog != null) {
209 mDialog.dismiss();
210 mDialog = null;
211 // Show delayed, so that the dismiss of the previous dialog completes
212 mHandler.sendEmptyMessage(MESSAGE_SHOW);
213 } else {
214 handleShow();
215 }
216 }
217
Charles He9851a8d2017-10-10 17:31:30 +0100218 /**
219 * Dismiss the global actions dialog, if it's currently shown
220 */
221 public void dismissDialog() {
222 mHandler.removeMessages(MESSAGE_DISMISS);
223 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
224 }
225
Jason Monk361915c2017-03-21 20:33:59 -0400226 private void awakenIfNecessary() {
227 if (mDreamManager != null) {
228 try {
229 if (mDreamManager.isDreaming()) {
230 mDreamManager.awaken();
231 }
232 } catch (RemoteException e) {
233 // we tried
234 }
235 }
236 }
237
238 private void handleShow() {
239 awakenIfNecessary();
240 mDialog = createDialog();
241 prepareDialog();
242
243 // If we only have 1 item and it's a simple press action, just do this action.
244 if (mAdapter.getCount() == 1
245 && mAdapter.getItem(0) instanceof SinglePressAction
246 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
247 ((SinglePressAction) mAdapter.getItem(0)).onPress();
248 } else {
249 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
250 attrs.setTitle("ActionsDialog");
Adrian Roos2f05bb32018-02-19 16:42:27 +0100251 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Jason Monk361915c2017-03-21 20:33:59 -0400252 mDialog.getWindow().setAttributes(attrs);
253 mDialog.show();
254 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400255 }
256 }
257
258 /**
259 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400260 *
Jason Monk361915c2017-03-21 20:33:59 -0400261 * @return A new dialog.
262 */
263 private ActionsDialog createDialog() {
264 // Simple toggle style if there's no vibrator, otherwise use a tri-state
265 if (!mHasVibrator) {
266 mSilentModeAction = new SilentModeToggleAction();
267 } else {
Lucas Dupin40ec6b782018-06-05 19:07:16 -0700268 mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
Jason Monk361915c2017-03-21 20:33:59 -0400269 }
270 mAirplaneModeOn = new ToggleAction(
271 R.drawable.ic_lock_airplane_mode,
272 R.drawable.ic_lock_airplane_mode_off,
273 R.string.global_actions_toggle_airplane_mode,
274 R.string.global_actions_airplane_mode_on_status,
275 R.string.global_actions_airplane_mode_off_status) {
276
277 void onToggle(boolean on) {
278 if (mHasTelephony && Boolean.parseBoolean(
279 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
280 mIsWaitingForEcmExit = true;
281 // Launch ECM exit dialog
282 Intent ecmDialogIntent =
283 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
284 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
285 mContext.startActivity(ecmDialogIntent);
286 } else {
287 changeAirplaneModeSystemSetting(on);
288 }
289 }
290
291 @Override
292 protected void changeStateFromPress(boolean buttonOn) {
293 if (!mHasTelephony) return;
294
295 // In ECM mode airplane state cannot be changed
296 if (!(Boolean.parseBoolean(
297 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
298 mState = buttonOn ? State.TurningOn : State.TurningOff;
299 mAirplaneState = mState;
300 }
301 }
302
303 public boolean showDuringKeyguard() {
304 return true;
305 }
306
307 public boolean showBeforeProvisioning() {
308 return false;
309 }
310 };
311 onAirplaneModeChanged();
312
313 mItems = new ArrayList<Action>();
314 String[] defaultActions = mContext.getResources().getStringArray(
315 R.array.config_globalActionsList);
316
317 ArraySet<String> addedKeys = new ArraySet<String>();
Alex Chau04458852017-11-27 18:21:23 +0000318 mHasLogoutButton = false;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800319 mHasLockdownButton = false;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800320 mHasFasterEmergencyButton = false;
Jason Monk361915c2017-03-21 20:33:59 -0400321 for (int i = 0; i < defaultActions.length; i++) {
322 String actionKey = defaultActions[i];
323 if (addedKeys.contains(actionKey)) {
324 // If we already have added this, don't add it again.
325 continue;
326 }
327 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
328 mItems.add(new PowerAction());
329 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
330 mItems.add(mAirplaneModeOn);
331 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
332 if (Settings.Global.getInt(mContext.getContentResolver(),
333 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
334 mItems.add(new BugReportAction());
335 }
336 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
337 if (mShowSilentToggle) {
338 mItems.add(mSilentModeAction);
339 }
340 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
341 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
342 addUsersToMenu(mItems);
343 }
344 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
345 mItems.add(getSettingsAction());
346 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Chad Brubaker02cd6cf2018-05-01 14:59:33 -0700347 if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
348 Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800349 && shouldDisplayLockdown()) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700350 mItems.add(getLockdownAction());
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800351 mHasLockdownButton = true;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700352 }
Jason Monk361915c2017-03-21 20:33:59 -0400353 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
354 mItems.add(getVoiceAssistAction());
355 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
356 mItems.add(getAssistAction());
357 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
358 mItems.add(new RestartAction());
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800359 } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
360 if (Settings.Global.getInt(mContext.getContentResolver(),
361 Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0
362 && !mEmergencyAffordanceManager.needsEmergencyAffordance()) {
363 mItems.add(new EmergencyAction());
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800364 mHasFasterEmergencyButton = true;
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800365 }
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500366 } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
367 mItems.add(new ScreenshotAction());
Alex Chau04458852017-11-27 18:21:23 +0000368 } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
Alex Chaud7958272017-12-08 11:30:52 +0000369 if (mDevicePolicyManager.isLogoutEnabled()
Alex Chau04458852017-11-27 18:21:23 +0000370 && getCurrentUser().id != UserHandle.USER_SYSTEM) {
371 mItems.add(new LogoutAction());
372 mHasLogoutButton = true;
373 }
Jason Monk361915c2017-03-21 20:33:59 -0400374 } else {
375 Log.e(TAG, "Invalid global action key " + actionKey);
376 }
377 // Add here so we don't add more than one.
378 addedKeys.add(actionKey);
379 }
380
381 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
382 mItems.add(getEmergencyAction());
383 }
384
385 mAdapter = new MyAdapter();
386
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700387 OnItemLongClickListener onItemLongClickListener = (parent, view, position, id) -> {
388 final Action action = mAdapter.getItem(position);
389 if (action instanceof LongPressAction) {
390 mDialog.dismiss();
391 return ((LongPressAction) action).onLongPress();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400392 }
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700393 return false;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400394 };
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800395 ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener,
396 mHasFasterEmergencyButton);
Jason Monk361915c2017-03-21 20:33:59 -0400397 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700398 dialog.setKeyguardShowing(mKeyguardShowing);
Jason Monk361915c2017-03-21 20:33:59 -0400399
400 dialog.setOnDismissListener(this);
401
402 return dialog;
403 }
404
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800405 private boolean shouldDisplayLockdown() {
406 int userId = getCurrentUser().id;
407 // Lockdown is meaningless without a place to go.
408 if (!mKeyguardManager.isDeviceSecure(userId)) {
409 return false;
410 }
411
412 // Only show the lockdown button if the device isn't locked down (for whatever reason).
413 int state = mLockPatternUtils.getStrongAuthForUser(userId);
414 return (state == STRONG_AUTH_NOT_REQUIRED
415 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
416 }
417
Jason Monk361915c2017-03-21 20:33:59 -0400418 private final class PowerAction extends SinglePressAction implements LongPressAction {
419 private PowerAction() {
420 super(R.drawable.ic_lock_power_off,
Jason Monk16fbd9d2017-04-27 14:28:49 -0400421 R.string.global_action_power_off);
Jason Monk361915c2017-03-21 20:33:59 -0400422 }
423
424 @Override
425 public boolean onLongPress() {
426 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
427 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
428 mWindowManagerFuncs.reboot(true);
429 return true;
430 }
431 return false;
432 }
433
434 @Override
435 public boolean showDuringKeyguard() {
436 return true;
437 }
438
439 @Override
440 public boolean showBeforeProvisioning() {
441 return true;
442 }
443
444 @Override
445 public void onPress() {
446 // shutdown by making sure radio and power are handled accordingly.
447 mWindowManagerFuncs.shutdown();
448 }
449 }
450
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800451 private class EmergencyAction extends SinglePressAction {
452 private static final String ACTION_EMERGENCY_DIALER_DIAL =
453 "com.android.phone.EmergencyDialer.DIAL";
454
455 private EmergencyAction() {
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800456 super(com.android.systemui.R.drawable.faster_emergency_icon,
457 R.string.global_action_emergency);
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800458 }
459
460 @Override
461 public void onPress() {
462 Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
463 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
464 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
465 }
466
467 @Override
468 public boolean showDuringKeyguard() {
469 return true;
470 }
471
472 @Override
473 public boolean showBeforeProvisioning() {
474 return true;
475 }
476 }
477
Jason Monk361915c2017-03-21 20:33:59 -0400478 private final class RestartAction extends SinglePressAction implements LongPressAction {
479 private RestartAction() {
480 super(R.drawable.ic_restart, R.string.global_action_restart);
481 }
482
483 @Override
484 public boolean onLongPress() {
485 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
486 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
487 mWindowManagerFuncs.reboot(true);
488 return true;
489 }
490 return false;
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 @Override
504 public void onPress() {
505 mWindowManagerFuncs.reboot(false);
506 }
507 }
508
509
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500510 private class ScreenshotAction extends SinglePressAction {
511 public ScreenshotAction() {
512 super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
513 }
514
515 @Override
516 public void onPress() {
517 // Add a little delay before executing, to give the
518 // dialog a chance to go away before it takes a
519 // screenshot.
520 // TODO: instead, omit global action dialog layer
521 mHandler.postDelayed(new Runnable() {
522 @Override
523 public void run() {
524 mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
525 MetricsLogger.action(mContext,
526 MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
527 }
528 }, 500);
529 }
530
531 @Override
532 public boolean showDuringKeyguard() {
533 return true;
534 }
535
536 @Override
537 public boolean showBeforeProvisioning() {
538 return false;
539 }
540 }
541
Jason Monk361915c2017-03-21 20:33:59 -0400542 private class BugReportAction extends SinglePressAction implements LongPressAction {
543
544 public BugReportAction() {
545 super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
546 }
547
548 @Override
549 public void onPress() {
550 // don't actually trigger the bugreport if we are running stability
551 // tests via monkey
552 if (ActivityManager.isUserAMonkey()) {
553 return;
554 }
555 // Add a little delay before executing, to give the
556 // dialog a chance to go away before it takes a
557 // screenshot.
558 mHandler.postDelayed(new Runnable() {
559 @Override
560 public void run() {
561 try {
562 // Take an "interactive" bugreport.
563 MetricsLogger.action(mContext,
564 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
565 ActivityManager.getService().requestBugReport(
566 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
567 } catch (RemoteException e) {
568 }
569 }
570 }, 500);
571 }
572
573 @Override
574 public boolean onLongPress() {
575 // don't actually trigger the bugreport if we are running stability
576 // tests via monkey
577 if (ActivityManager.isUserAMonkey()) {
578 return false;
579 }
580 try {
581 // Take a "full" bugreport.
582 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
583 ActivityManager.getService().requestBugReport(
584 ActivityManager.BUGREPORT_OPTION_FULL);
585 } catch (RemoteException e) {
586 }
587 return false;
588 }
589
590 public boolean showDuringKeyguard() {
591 return true;
592 }
593
594 @Override
595 public boolean showBeforeProvisioning() {
596 return false;
597 }
598
599 @Override
600 public String getStatus() {
601 return mContext.getString(
602 R.string.bugreport_status,
603 Build.VERSION.RELEASE,
604 Build.ID);
605 }
606 }
607
Alex Chau04458852017-11-27 18:21:23 +0000608 private final class LogoutAction extends SinglePressAction {
609 private LogoutAction() {
610 super(R.drawable.ic_logout, R.string.global_action_logout);
611 }
612
613 @Override
614 public boolean showDuringKeyguard() {
615 return true;
616 }
617
618 @Override
619 public boolean showBeforeProvisioning() {
620 return false;
621 }
622
623 @Override
624 public void onPress() {
625 // Add a little delay before executing, to give the dialog a chance to go away before
626 // switching user
627 mHandler.postDelayed(() -> {
628 try {
Alex Chauedb6a012018-01-26 12:52:43 +0000629 int currentUserId = getCurrentUser().id;
Alex Chau04458852017-11-27 18:21:23 +0000630 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
Alex Chauedb6a012018-01-26 12:52:43 +0000631 ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
Alex Chau04458852017-11-27 18:21:23 +0000632 } catch (RemoteException re) {
633 Log.e(TAG, "Couldn't logout user " + re);
634 }
635 }, 500);
636 }
637 }
638
Jason Monk361915c2017-03-21 20:33:59 -0400639 private Action getSettingsAction() {
640 return new SinglePressAction(R.drawable.ic_settings,
641 R.string.global_action_settings) {
642
643 @Override
644 public void onPress() {
645 Intent intent = new Intent(Settings.ACTION_SETTINGS);
646 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
647 mContext.startActivity(intent);
648 }
649
650 @Override
651 public boolean showDuringKeyguard() {
652 return true;
653 }
654
655 @Override
656 public boolean showBeforeProvisioning() {
657 return true;
658 }
659 };
660 }
661
662 private Action getEmergencyAction() {
663 return new SinglePressAction(R.drawable.emergency_icon,
664 R.string.global_action_emergency) {
665 @Override
666 public void onPress() {
667 mEmergencyAffordanceManager.performEmergencyCall();
668 }
669
670 @Override
671 public boolean showDuringKeyguard() {
672 return true;
673 }
674
675 @Override
676 public boolean showBeforeProvisioning() {
677 return true;
678 }
679 };
680 }
681
682 private Action getAssistAction() {
683 return new SinglePressAction(R.drawable.ic_action_assist_focused,
684 R.string.global_action_assist) {
685 @Override
686 public void onPress() {
687 Intent intent = new Intent(Intent.ACTION_ASSIST);
688 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
689 mContext.startActivity(intent);
690 }
691
692 @Override
693 public boolean showDuringKeyguard() {
694 return true;
695 }
696
697 @Override
698 public boolean showBeforeProvisioning() {
699 return true;
700 }
701 };
702 }
703
704 private Action getVoiceAssistAction() {
705 return new SinglePressAction(R.drawable.ic_voice_search,
706 R.string.global_action_voice_assist) {
707 @Override
708 public void onPress() {
709 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
710 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
711 mContext.startActivity(intent);
712 }
713
714 @Override
715 public boolean showDuringKeyguard() {
716 return true;
717 }
718
719 @Override
720 public boolean showBeforeProvisioning() {
721 return true;
722 }
723 };
724 }
725
726 private Action getLockdownAction() {
Alison Cichowlas21125432018-05-16 15:40:45 -0400727 return new SinglePressAction(R.drawable.ic_lock_lockdown,
Jason Monk361915c2017-03-21 20:33:59 -0400728 R.string.global_action_lockdown) {
729
730 @Override
731 public void onPress() {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700732 new LockPatternUtils(mContext)
733 .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
734 UserHandle.USER_ALL);
Jason Monk361915c2017-03-21 20:33:59 -0400735 try {
736 WindowManagerGlobal.getWindowManagerService().lockNow(null);
Pavel Grafov059021b2018-05-02 13:44:46 +0100737 // Lock profiles (if any) on the background thread.
738 final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
739 bgHandler.post(() -> lockProfiles());
Jason Monk361915c2017-03-21 20:33:59 -0400740 } catch (RemoteException e) {
741 Log.e(TAG, "Error while trying to lock device.", e);
742 }
743 }
744
745 @Override
746 public boolean showDuringKeyguard() {
747 return true;
748 }
749
750 @Override
751 public boolean showBeforeProvisioning() {
752 return false;
753 }
754 };
755 }
756
Pavel Grafov059021b2018-05-02 13:44:46 +0100757 private void lockProfiles() {
758 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
759 final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
760 final int currentUserId = getCurrentUser().id;
761 final int[] profileIds = um.getEnabledProfileIds(currentUserId);
762 for (final int id : profileIds) {
763 if (id != currentUserId) {
764 tm.setDeviceLockedForUser(id, true);
765 }
766 }
767 }
768
Jason Monk361915c2017-03-21 20:33:59 -0400769 private UserInfo getCurrentUser() {
770 try {
771 return ActivityManager.getService().getCurrentUser();
772 } catch (RemoteException re) {
773 return null;
774 }
775 }
776
777 private boolean isCurrentUserOwner() {
778 UserInfo currentUser = getCurrentUser();
779 return currentUser == null || currentUser.isPrimary();
780 }
781
782 private void addUsersToMenu(ArrayList<Action> items) {
783 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
784 if (um.isUserSwitcherEnabled()) {
785 List<UserInfo> users = um.getUsers();
786 UserInfo currentUser = getCurrentUser();
787 for (final UserInfo user : users) {
788 if (user.supportsSwitchToByUser()) {
789 boolean isCurrentUser = currentUser == null
790 ? user.id == 0 : (currentUser.id == user.id);
791 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
792 : null;
793 SinglePressAction switchToUser = new SinglePressAction(
794 R.drawable.ic_menu_cc, icon,
795 (user.name != null ? user.name : "Primary")
Jason Monk16fbd9d2017-04-27 14:28:49 -0400796 + (isCurrentUser ? " \u2714" : "")) {
Jason Monk361915c2017-03-21 20:33:59 -0400797 public void onPress() {
798 try {
799 ActivityManager.getService().switchUser(user.id);
800 } catch (RemoteException re) {
801 Log.e(TAG, "Couldn't switch user " + re);
802 }
803 }
804
805 public boolean showDuringKeyguard() {
806 return true;
807 }
808
809 public boolean showBeforeProvisioning() {
810 return false;
811 }
812 };
813 items.add(switchToUser);
814 }
815 }
816 }
817 }
818
819 private void prepareDialog() {
820 refreshSilentMode();
821 mAirplaneModeOn.updateState(mAirplaneState);
822 mAdapter.notifyDataSetChanged();
Jason Monk361915c2017-03-21 20:33:59 -0400823 if (mShowSilentToggle) {
824 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
825 mContext.registerReceiver(mRingerModeReceiver, filter);
826 }
827 }
828
829 private void refreshSilentMode() {
830 if (!mHasVibrator) {
831 final boolean silentModeOn =
832 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400833 ((ToggleAction) mSilentModeAction).updateState(
Jason Monk361915c2017-03-21 20:33:59 -0400834 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
835 }
836 }
837
838 /** {@inheritDoc} */
839 public void onDismiss(DialogInterface dialog) {
840 mWindowManagerFuncs.onGlobalActionsHidden();
841 if (mShowSilentToggle) {
842 try {
843 mContext.unregisterReceiver(mRingerModeReceiver);
844 } catch (IllegalArgumentException ie) {
845 // ignore this
846 Log.w(TAG, ie);
847 }
848 }
849 }
850
851 /** {@inheritDoc} */
852 public void onClick(DialogInterface dialog, int which) {
Jason Monkfd279662017-06-29 19:37:48 -0400853 Action item = mAdapter.getItem(which);
Jason Monkb4302182017-08-04 13:39:17 -0400854 if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400855 dialog.dismiss();
856 }
Jason Monkfd279662017-06-29 19:37:48 -0400857 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400858 }
859
860 /**
861 * The adapter used for the list within the global actions dialog, taking
862 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400863 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
864 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400865 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
866 */
867 private class MyAdapter extends BaseAdapter {
868
869 public int getCount() {
870 int count = 0;
871
872 for (int i = 0; i < mItems.size(); i++) {
873 final Action action = mItems.get(i);
874
875 if (mKeyguardShowing && !action.showDuringKeyguard()) {
876 continue;
877 }
878 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
879 continue;
880 }
881 count++;
882 }
883 return count;
884 }
885
886 @Override
887 public boolean isEnabled(int position) {
888 return getItem(position).isEnabled();
889 }
890
891 @Override
892 public boolean areAllItemsEnabled() {
893 return false;
894 }
895
896 public Action getItem(int position) {
897
898 int filteredPos = 0;
899 for (int i = 0; i < mItems.size(); i++) {
900 final Action action = mItems.get(i);
901 if (mKeyguardShowing && !action.showDuringKeyguard()) {
902 continue;
903 }
904 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
905 continue;
906 }
907 if (filteredPos == position) {
908 return action;
909 }
910 filteredPos++;
911 }
912
913 throw new IllegalArgumentException("position " + position
914 + " out of range of showable actions"
915 + ", filtered count=" + getCount()
916 + ", keyguardshowing=" + mKeyguardShowing
917 + ", provisioned=" + mDeviceProvisioned);
918 }
919
920
921 public long getItemId(int position) {
922 return position;
923 }
924
925 public View getView(int position, View convertView, ViewGroup parent) {
926 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400927 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
Alison Cichowlas3be52db2018-03-06 19:48:06 -0500928 // Everything but screenshot, the last item, gets white background.
929 if (position == getCount() - 1) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400930 HardwareUiLayout.get(parent).setDivisionView(view);
931 }
932 return view;
Jason Monk361915c2017-03-21 20:33:59 -0400933 }
934 }
935
936 // note: the scheme below made more sense when we were planning on having
937 // 8 different things in the global actions dialog. seems overkill with
938 // only 3 items now, but may as well keep this flexible approach so it will
939 // be easy should someone decide at the last minute to include something
940 // else, such as 'enable wifi', or 'enable bluetooth'
941
942 /**
943 * What each item in the global actions dialog must be able to support.
944 */
945 private interface Action {
946 /**
947 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -0400948 * for none.
Jason Monk361915c2017-03-21 20:33:59 -0400949 */
950 CharSequence getLabelForAccessibility(Context context);
951
952 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
953
954 void onPress();
955
956 /**
957 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -0400958 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -0400959 */
960 boolean showDuringKeyguard();
961
962 /**
963 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -0400964 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -0400965 */
966 boolean showBeforeProvisioning();
967
968 boolean isEnabled();
969 }
970
971 /**
972 * An action that also supports long press.
973 */
974 private interface LongPressAction extends Action {
975 boolean onLongPress();
976 }
977
978 /**
979 * A single press action maintains no state, just responds to a press
980 * and takes an action.
981 */
982 private static abstract class SinglePressAction implements Action {
983 private final int mIconResId;
984 private final Drawable mIcon;
985 private final int mMessageResId;
986 private final CharSequence mMessage;
987
988 protected SinglePressAction(int iconResId, int messageResId) {
989 mIconResId = iconResId;
990 mMessageResId = messageResId;
991 mMessage = null;
992 mIcon = null;
993 }
994
995 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
996 mIconResId = iconResId;
997 mMessageResId = 0;
998 mMessage = message;
999 mIcon = icon;
1000 }
1001
1002 public boolean isEnabled() {
1003 return true;
1004 }
1005
1006 public String getStatus() {
1007 return null;
1008 }
1009
1010 abstract public void onPress();
1011
1012 public CharSequence getLabelForAccessibility(Context context) {
1013 if (mMessage != null) {
1014 return mMessage;
1015 } else {
1016 return context.getString(mMessageResId);
1017 }
1018 }
1019
1020 public View create(
1021 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001022 View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent,
1023 false);
Jason Monk361915c2017-03-21 20:33:59 -04001024
1025 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1026 TextView messageView = (TextView) v.findViewById(R.id.message);
1027
1028 TextView statusView = (TextView) v.findViewById(R.id.status);
1029 final String status = getStatus();
1030 if (!TextUtils.isEmpty(status)) {
1031 statusView.setText(status);
1032 } else {
1033 statusView.setVisibility(View.GONE);
1034 }
1035 if (mIcon != null) {
1036 icon.setImageDrawable(mIcon);
1037 icon.setScaleType(ScaleType.CENTER_CROP);
1038 } else if (mIconResId != 0) {
1039 icon.setImageDrawable(context.getDrawable(mIconResId));
1040 }
1041 if (mMessage != null) {
1042 messageView.setText(mMessage);
1043 } else {
1044 messageView.setText(mMessageResId);
1045 }
1046
1047 return v;
1048 }
1049 }
1050
1051 /**
1052 * A toggle action knows whether it is on or off, and displays an icon
1053 * and status message accordingly.
1054 */
1055 private static abstract class ToggleAction implements Action {
1056
1057 enum State {
1058 Off(false),
1059 TurningOn(true),
1060 TurningOff(true),
1061 On(false);
1062
1063 private final boolean inTransition;
1064
1065 State(boolean intermediate) {
1066 inTransition = intermediate;
1067 }
1068
1069 public boolean inTransition() {
1070 return inTransition;
1071 }
1072 }
1073
1074 protected State mState = State.Off;
1075
1076 // prefs
1077 protected int mEnabledIconResId;
1078 protected int mDisabledIconResid;
1079 protected int mMessageResId;
1080 protected int mEnabledStatusMessageResId;
1081 protected int mDisabledStatusMessageResId;
1082
1083 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -04001084 * @param enabledIconResId The icon for when this action is on.
1085 * @param disabledIconResid The icon for when this action is off.
1086 * @param message The general information message, e.g 'Silent Mode'
1087 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -04001088 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
1089 */
1090 public ToggleAction(int enabledIconResId,
1091 int disabledIconResid,
1092 int message,
1093 int enabledStatusMessageResId,
1094 int disabledStatusMessageResId) {
1095 mEnabledIconResId = enabledIconResId;
1096 mDisabledIconResid = disabledIconResid;
1097 mMessageResId = message;
1098 mEnabledStatusMessageResId = enabledStatusMessageResId;
1099 mDisabledStatusMessageResId = disabledStatusMessageResId;
1100 }
1101
1102 /**
1103 * Override to make changes to resource IDs just before creating the
1104 * View.
1105 */
1106 void willCreate() {
1107
1108 }
1109
1110 @Override
1111 public CharSequence getLabelForAccessibility(Context context) {
1112 return context.getString(mMessageResId);
1113 }
1114
1115 public View create(Context context, View convertView, ViewGroup parent,
1116 LayoutInflater inflater) {
1117 willCreate();
1118
1119 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -04001120 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -04001121
1122 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1123 TextView messageView = (TextView) v.findViewById(R.id.message);
1124 TextView statusView = (TextView) v.findViewById(R.id.status);
1125 final boolean enabled = isEnabled();
1126
1127 if (messageView != null) {
1128 messageView.setText(mMessageResId);
1129 messageView.setEnabled(enabled);
1130 }
1131
1132 boolean on = ((mState == State.On) || (mState == State.TurningOn));
1133 if (icon != null) {
1134 icon.setImageDrawable(context.getDrawable(
1135 (on ? mEnabledIconResId : mDisabledIconResid)));
1136 icon.setEnabled(enabled);
1137 }
1138
1139 if (statusView != null) {
1140 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
1141 statusView.setVisibility(View.VISIBLE);
1142 statusView.setEnabled(enabled);
1143 }
1144 v.setEnabled(enabled);
1145
1146 return v;
1147 }
1148
1149 public final void onPress() {
1150 if (mState.inTransition()) {
1151 Log.w(TAG, "shouldn't be able to toggle when in transition");
1152 return;
1153 }
1154
1155 final boolean nowOn = !(mState == State.On);
1156 onToggle(nowOn);
1157 changeStateFromPress(nowOn);
1158 }
1159
1160 public boolean isEnabled() {
1161 return !mState.inTransition();
1162 }
1163
1164 /**
1165 * Implementations may override this if their state can be in on of the intermediate
1166 * states until some notification is received (e.g airplane mode is 'turning off' until
1167 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001168 *
Jason Monk361915c2017-03-21 20:33:59 -04001169 * @param buttonOn Whether the button was turned on or off
1170 */
1171 protected void changeStateFromPress(boolean buttonOn) {
1172 mState = buttonOn ? State.On : State.Off;
1173 }
1174
1175 abstract void onToggle(boolean on);
1176
1177 public void updateState(State state) {
1178 mState = state;
1179 }
1180 }
1181
1182 private class SilentModeToggleAction extends ToggleAction {
1183 public SilentModeToggleAction() {
1184 super(R.drawable.ic_audio_vol_mute,
1185 R.drawable.ic_audio_vol,
1186 R.string.global_action_toggle_silent_mode,
1187 R.string.global_action_silent_mode_on_status,
1188 R.string.global_action_silent_mode_off_status);
1189 }
1190
1191 void onToggle(boolean on) {
1192 if (on) {
1193 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1194 } else {
1195 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1196 }
1197 }
1198
1199 public boolean showDuringKeyguard() {
1200 return true;
1201 }
1202
1203 public boolean showBeforeProvisioning() {
1204 return false;
1205 }
1206 }
1207
1208 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1209
Jason Monk16fbd9d2017-04-27 14:28:49 -04001210 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001211
1212 private final AudioManager mAudioManager;
1213 private final Handler mHandler;
Jason Monk361915c2017-03-21 20:33:59 -04001214
Lucas Dupin40ec6b782018-06-05 19:07:16 -07001215 SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
Jason Monk361915c2017-03-21 20:33:59 -04001216 mAudioManager = audioManager;
1217 mHandler = handler;
Jason Monk361915c2017-03-21 20:33:59 -04001218 }
1219
1220 private int ringerModeToIndex(int ringerMode) {
1221 // They just happen to coincide
1222 return ringerMode;
1223 }
1224
1225 private int indexToRingerMode(int index) {
1226 // They just happen to coincide
1227 return index;
1228 }
1229
1230 @Override
1231 public CharSequence getLabelForAccessibility(Context context) {
1232 return null;
1233 }
1234
1235 public View create(Context context, View convertView, ViewGroup parent,
1236 LayoutInflater inflater) {
1237 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1238
1239 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1240 for (int i = 0; i < 3; i++) {
1241 View itemView = v.findViewById(ITEM_IDS[i]);
1242 itemView.setSelected(selectedIndex == i);
1243 // Set up click handler
1244 itemView.setTag(i);
1245 itemView.setOnClickListener(this);
1246 }
1247 return v;
1248 }
1249
1250 public void onPress() {
1251 }
1252
1253 public boolean showDuringKeyguard() {
1254 return true;
1255 }
1256
1257 public boolean showBeforeProvisioning() {
1258 return false;
1259 }
1260
1261 public boolean isEnabled() {
1262 return true;
1263 }
1264
1265 void willCreate() {
1266 }
1267
1268 public void onClick(View v) {
1269 if (!(v.getTag() instanceof Integer)) return;
1270
1271 int index = (Integer) v.getTag();
1272 mAudioManager.setRingerMode(indexToRingerMode(index));
1273 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1274 }
1275 }
1276
1277 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1278 public void onReceive(Context context, Intent intent) {
1279 String action = intent.getAction();
1280 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1281 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1282 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1283 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001284 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISMISS, reason));
Jason Monk361915c2017-03-21 20:33:59 -04001285 }
1286 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1287 // Airplane mode can be changed after ECM exits if airplane toggle button
1288 // is pressed during ECM mode
1289 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1290 mIsWaitingForEcmExit) {
1291 mIsWaitingForEcmExit = false;
1292 changeAirplaneModeSystemSetting(true);
1293 }
1294 }
1295 }
1296 };
1297
1298 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1299 @Override
1300 public void onServiceStateChanged(ServiceState serviceState) {
1301 if (!mHasTelephony) return;
1302 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1303 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1304 mAirplaneModeOn.updateState(mAirplaneState);
1305 mAdapter.notifyDataSetChanged();
1306 }
1307 };
1308
1309 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1310 @Override
1311 public void onReceive(Context context, Intent intent) {
1312 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1313 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1314 }
1315 }
1316 };
1317
1318 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1319 @Override
1320 public void onChange(boolean selfChange) {
1321 onAirplaneModeChanged();
1322 }
1323 };
1324
1325 private static final int MESSAGE_DISMISS = 0;
1326 private static final int MESSAGE_REFRESH = 1;
1327 private static final int MESSAGE_SHOW = 2;
1328 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1329
1330 private Handler mHandler = new Handler() {
1331 public void handleMessage(Message msg) {
1332 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001333 case MESSAGE_DISMISS:
1334 if (mDialog != null) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001335 if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
1336 mDialog.dismissImmediately();
1337 } else {
1338 mDialog.dismiss();
1339 }
Jason Monk16fbd9d2017-04-27 14:28:49 -04001340 mDialog = null;
1341 }
1342 break;
1343 case MESSAGE_REFRESH:
1344 refreshSilentMode();
1345 mAdapter.notifyDataSetChanged();
1346 break;
1347 case MESSAGE_SHOW:
1348 handleShow();
1349 break;
Jason Monk361915c2017-03-21 20:33:59 -04001350 }
1351 }
1352 };
1353
1354 private void onAirplaneModeChanged() {
1355 // Let the service state callbacks handle the state.
1356 if (mHasTelephony) return;
1357
1358 boolean airplaneModeOn = Settings.Global.getInt(
1359 mContext.getContentResolver(),
1360 Settings.Global.AIRPLANE_MODE_ON,
1361 0) == 1;
1362 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1363 mAirplaneModeOn.updateState(mAirplaneState);
1364 }
1365
1366 /**
1367 * Change the airplane mode system setting
1368 */
1369 private void changeAirplaneModeSystemSetting(boolean on) {
1370 Settings.Global.putInt(
1371 mContext.getContentResolver(),
1372 Settings.Global.AIRPLANE_MODE_ON,
1373 on ? 1 : 0);
1374 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1375 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1376 intent.putExtra("state", on);
1377 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1378 if (!mHasTelephony) {
1379 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1380 }
1381 }
1382
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001383 private static final class ActionsDialog extends Dialog implements DialogInterface,
1384 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001385
Jason Monk16fbd9d2017-04-27 14:28:49 -04001386 private final Context mContext;
1387 private final MyAdapter mAdapter;
1388 private final LinearLayout mListView;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001389 private final FrameLayout mSeparatedView;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001390 private final HardwareUiLayout mHardwareLayout;
1391 private final OnClickListener mClickListener;
1392 private final OnItemLongClickListener mLongClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001393 private final GradientDrawable mGradientDrawable;
1394 private final ColorExtractor mColorExtractor;
1395 private boolean mKeyguardShowing;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001396 private boolean mShouldDisplaySeparatedButton;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001397
1398 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001399 OnItemLongClickListener longClickListener, boolean shouldDisplaySeparatedButton) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001400 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Lucas Dupin448786c2017-07-24 17:44:25 -07001401 mContext = context;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001402 mAdapter = adapter;
1403 mClickListener = clickListener;
1404 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001405 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001406 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001407 mShouldDisplaySeparatedButton = shouldDisplaySeparatedButton;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001408
1409 // Window initialization
1410 Window window = getWindow();
1411 window.requestFeature(Window.FEATURE_NO_TITLE);
Adrian Roosedfab3b2018-03-08 18:39:20 +01001412 // Inflate the decor view, so the attributes below are not overwritten by the theme.
1413 window.getDecorView();
1414 window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
1415 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
1416 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
1417 window.setLayout(MATCH_PARENT, MATCH_PARENT);
1418 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
Alison Cichowlas4f19f4a2017-07-25 10:56:16 -04001419 window.addFlags(
1420 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001421 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Adrian Roosedfab3b2018-03-08 18:39:20 +01001422 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001423 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1424 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1425 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1426 window.setBackgroundDrawable(mGradientDrawable);
1427 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1428
Jason Monk16fbd9d2017-04-27 14:28:49 -04001429 setContentView(com.android.systemui.R.layout.global_actions_wrapped);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001430 mListView = findViewById(android.R.id.list);
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001431 mSeparatedView = findViewById(com.android.systemui.R.id.separated_button);
1432 if (!mShouldDisplaySeparatedButton) {
1433 mSeparatedView.setVisibility(View.GONE);
1434 }
Jason Monk16fbd9d2017-04-27 14:28:49 -04001435 mHardwareLayout = HardwareUiLayout.get(mListView);
1436 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001437 mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton);
Phil Weaver8583ae82018-02-13 11:01:24 -08001438 setTitle(R.string.global_actions);
Phil Weaver9054e092018-04-27 16:28:50 -07001439 mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
1440 @Override
1441 public boolean dispatchPopulateAccessibilityEvent(
1442 View host, AccessibilityEvent event) {
1443 // Populate the title here, just as Activity does
1444 event.getText().add(mContext.getString(R.string.global_actions));
1445 return true;
1446 }
1447 });
Jason Monk361915c2017-03-21 20:33:59 -04001448 }
1449
Jason Monk16fbd9d2017-04-27 14:28:49 -04001450 private void updateList() {
1451 mListView.removeAllViews();
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001452 mSeparatedView.removeAllViews();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001453 for (int i = 0; i < mAdapter.getCount(); i++) {
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001454 ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1
1455 ? mSeparatedView : mListView;
1456 View v = mAdapter.getView(i, null, parentView);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001457 final int pos = i;
1458 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1459 v.setOnLongClickListener(view ->
1460 mLongClickListener.onItemLongClick(null, v, pos, 0));
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001461 parentView.addView(v);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001462 }
Jason Monk361915c2017-03-21 20:33:59 -04001463 }
1464
1465 @Override
1466 protected void onStart() {
1467 super.setCanceledOnTouchOutside(true);
1468 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001469 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001470
1471 Point displaySize = new Point();
1472 mContext.getDisplay().getRealSize(displaySize);
1473 mColorExtractor.addOnColorsChangedListener(this);
1474 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1475 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1476 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001477 updateColors(colors, false /* animate */);
1478 }
1479
1480 /**
1481 * Updates background and system bars according to current GradientColors.
1482 * @param colors Colors and hints to use.
1483 * @param animate Interpolates gradient if true, just sets otherwise.
1484 */
1485 private void updateColors(GradientColors colors, boolean animate) {
1486 mGradientDrawable.setColors(colors, animate);
1487 View decorView = getWindow().getDecorView();
1488 if (colors.supportsDarkText()) {
1489 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
1490 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
1491 } else {
1492 decorView.setSystemUiVisibility(0);
1493 }
Jason Monk361915c2017-03-21 20:33:59 -04001494 }
1495
1496 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001497 protected void onStop() {
1498 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001499 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001500 }
1501
1502 @Override
1503 public void show() {
1504 super.show();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001505 mGradientDrawable.setAlpha(0);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001506 mHardwareLayout.setTranslationX(getAnimTranslation());
1507 mHardwareLayout.setAlpha(0);
1508 mHardwareLayout.animate()
1509 .alpha(1)
1510 .translationX(0)
1511 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001512 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001513 .setUpdateListener(animation -> {
1514 int alpha = (int) ((Float) animation.getAnimatedValue()
1515 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1516 mGradientDrawable.setAlpha(alpha);
1517 })
Jason Monka7af3b62017-07-07 11:35:13 -04001518 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
Jason Monk16fbd9d2017-04-27 14:28:49 -04001519 .start();
1520 }
1521
1522 @Override
1523 public void dismiss() {
1524 mHardwareLayout.setTranslationX(0);
1525 mHardwareLayout.setAlpha(1);
1526 mHardwareLayout.animate()
1527 .alpha(0)
1528 .translationX(getAnimTranslation())
1529 .setDuration(300)
1530 .withEndAction(() -> super.dismiss())
1531 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001532 .setUpdateListener(animation -> {
1533 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1534 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1535 mGradientDrawable.setAlpha(alpha);
1536 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001537 .start();
1538 }
1539
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001540 void dismissImmediately() {
1541 super.dismiss();
1542 }
1543
Jason Monk16fbd9d2017-04-27 14:28:49 -04001544 private float getAnimTranslation() {
1545 return getContext().getResources().getDimension(
1546 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001547 }
1548
1549 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001550 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001551 if (mKeyguardShowing) {
1552 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001553 updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
1554 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001555 }
1556 } else {
1557 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001558 updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
1559 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001560 }
1561 }
1562 }
1563
1564 public void setKeyguardShowing(boolean keyguardShowing) {
1565 mKeyguardShowing = keyguardShowing;
1566 }
Jason Monk361915c2017-03-21 20:33:59 -04001567 }
1568}