blob: 92674d456bcd2c6449b02f2fec90576a538379aa [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;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050031import android.content.ComponentName;
Jason Monk361915c2017-03-21 20:33:59 -040032import android.content.Context;
33import android.content.DialogInterface;
34import android.content.Intent;
35import android.content.IntentFilter;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050036import android.content.ServiceConnection;
Jason Monk361915c2017-03-21 20:33:59 -040037import android.content.pm.UserInfo;
38import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070039import android.graphics.Point;
Jason Monk361915c2017-03-21 20:33:59 -040040import android.graphics.drawable.Drawable;
41import android.media.AudioManager;
42import android.net.ConnectivityManager;
43import android.os.Build;
Jason Monk361915c2017-03-21 20:33:59 -040044import android.os.Handler;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050045import android.os.IBinder;
Jason Monk361915c2017-03-21 20:33:59 -040046import android.os.Message;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050047import android.os.Messenger;
Jason Monk361915c2017-03-21 20:33:59 -040048import android.os.RemoteException;
49import android.os.ServiceManager;
50import android.os.SystemProperties;
51import android.os.UserHandle;
52import android.os.UserManager;
53import android.os.Vibrator;
54import android.provider.Settings;
55import android.service.dreams.DreamService;
56import android.service.dreams.IDreamManager;
57import android.telephony.PhoneStateListener;
58import android.telephony.ServiceState;
59import android.telephony.TelephonyManager;
60import android.text.TextUtils;
61import android.util.ArraySet;
62import android.util.Log;
Lucas Dupin448786c2017-07-24 17:44:25 -070063import android.view.ContextThemeWrapper;
Jason Monk361915c2017-03-21 20:33:59 -040064import android.view.LayoutInflater;
65import android.view.View;
66import android.view.ViewGroup;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070067import android.view.Window;
Jason Monk361915c2017-03-21 20:33:59 -040068import android.view.WindowManager;
69import android.view.WindowManagerGlobal;
70import android.view.accessibility.AccessibilityEvent;
71import android.widget.AdapterView;
Jason Monk16fbd9d2017-04-27 14:28:49 -040072import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040073import android.widget.BaseAdapter;
74import android.widget.ImageView;
75import android.widget.ImageView.ScaleType;
Jason Monk16fbd9d2017-04-27 14:28:49 -040076import android.widget.LinearLayout;
Jason Monk361915c2017-03-21 20:33:59 -040077import android.widget.TextView;
78
Charles He9851a8d2017-10-10 17:31:30 +010079import com.android.internal.R;
80import com.android.internal.colorextraction.ColorExtractor;
81import com.android.internal.colorextraction.ColorExtractor.GradientColors;
Lucas Dupine2292a92017-07-06 14:35:30 -070082import com.android.internal.colorextraction.drawable.GradientDrawable;
Charles He9851a8d2017-10-10 17:31:30 +010083import com.android.internal.logging.MetricsLogger;
84import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
85import com.android.internal.telephony.TelephonyIntents;
86import com.android.internal.telephony.TelephonyProperties;
87import com.android.internal.util.EmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050088import com.android.internal.util.ScreenshotHelper;
Charles He9851a8d2017-10-10 17:31:30 +010089import com.android.internal.widget.LockPatternUtils;
90import com.android.systemui.Dependency;
91import com.android.systemui.HardwareUiLayout;
92import com.android.systemui.Interpolators;
93import com.android.systemui.colorextraction.SysuiColorExtractor;
94import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
95import com.android.systemui.statusbar.phone.ScrimController;
Julia Reynolds42411922017-11-08 11:19:09 -050096import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070097
Jason Monk361915c2017-03-21 20:33:59 -040098import java.util.ArrayList;
99import java.util.List;
100
101/**
102 * Helper to show the global actions dialog. Each item is an {@link Action} that
103 * may show depending on whether the keyguard is showing, and whether the device
104 * is provisioned.
105 */
Charles He9851a8d2017-10-10 17:31:30 +0100106class GlobalActionsDialog implements DialogInterface.OnDismissListener,
107 DialogInterface.OnClickListener {
Jason Monk361915c2017-03-21 20:33:59 -0400108
109 static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
110 static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700111 static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
Jason Monk361915c2017-03-21 20:33:59 -0400112
113 private static final String TAG = "GlobalActionsDialog";
114
115 private static final boolean SHOW_SILENT_TOGGLE = true;
116
117 /* Valid settings for global actions keys.
118 * see config.xml config_globalActionList */
119 private static final String GLOBAL_ACTION_KEY_POWER = "power";
120 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
121 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
122 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
123 private static final String GLOBAL_ACTION_KEY_USERS = "users";
124 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
125 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
126 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
127 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
128 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
Alex Chau04458852017-11-27 18:21:23 +0000129 private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800130 private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500131 private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
Jason Monk361915c2017-03-21 20:33:59 -0400132
133 private final Context mContext;
134 private final GlobalActionsManager mWindowManagerFuncs;
135 private final AudioManager mAudioManager;
136 private final IDreamManager mDreamManager;
Alex Chau04458852017-11-27 18:21:23 +0000137 private final DevicePolicyManager mDevicePolicyManager;
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800138 private final LockPatternUtils mLockPatternUtils;
139 private final KeyguardManager mKeyguardManager;
Jason Monk361915c2017-03-21 20:33:59 -0400140
141 private ArrayList<Action> mItems;
142 private ActionsDialog mDialog;
143
144 private Action mSilentModeAction;
145 private ToggleAction mAirplaneModeOn;
146
147 private MyAdapter mAdapter;
148
149 private boolean mKeyguardShowing = false;
150 private boolean mDeviceProvisioned = false;
151 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
152 private boolean mIsWaitingForEcmExit = false;
153 private boolean mHasTelephony;
154 private boolean mHasVibrator;
Alex Chau04458852017-11-27 18:21:23 +0000155 private boolean mHasLogoutButton;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800156 private boolean mHasLockdownButton;
Jason Monk361915c2017-03-21 20:33:59 -0400157 private final boolean mShowSilentToggle;
158 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500159 private final ScreenshotHelper mScreenshotHelper;
Jason Monk361915c2017-03-21 20:33:59 -0400160
161 /**
162 * @param context everything needs a context :(
163 */
164 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
Lucas Dupin448786c2017-07-24 17:44:25 -0700165 mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
Jason Monk361915c2017-03-21 20:33:59 -0400166 mWindowManagerFuncs = windowManagerFuncs;
167 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
168 mDreamManager = IDreamManager.Stub.asInterface(
169 ServiceManager.getService(DreamService.DREAM_SERVICE));
Alex Chau04458852017-11-27 18:21:23 +0000170 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
171 Context.DEVICE_POLICY_SERVICE);
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800172 mLockPatternUtils = new LockPatternUtils(mContext);
173 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Jason Monk361915c2017-03-21 20:33:59 -0400174
175 // receive broadcasts
176 IntentFilter filter = new IntentFilter();
177 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
178 filter.addAction(Intent.ACTION_SCREEN_OFF);
179 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
180 context.registerReceiver(mBroadcastReceiver, filter);
181
182 ConnectivityManager cm = (ConnectivityManager)
183 context.getSystemService(Context.CONNECTIVITY_SERVICE);
184 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
185
186 // get notified of phone state changes
187 TelephonyManager telephonyManager =
188 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
189 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
190 mContext.getContentResolver().registerContentObserver(
191 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
192 mAirplaneModeObserver);
193 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
194 mHasVibrator = vibrator != null && vibrator.hasVibrator();
195
196 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
197 R.bool.config_useFixedVolume);
198
199 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500200 mScreenshotHelper = new ScreenshotHelper(context);
Jason Monk361915c2017-03-21 20:33:59 -0400201 }
202
203 /**
204 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400205 *
Jason Monk361915c2017-03-21 20:33:59 -0400206 * @param keyguardShowing True if keyguard is showing
207 */
208 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
209 mKeyguardShowing = keyguardShowing;
210 mDeviceProvisioned = isDeviceProvisioned;
211 if (mDialog != null) {
212 mDialog.dismiss();
213 mDialog = null;
214 // Show delayed, so that the dismiss of the previous dialog completes
215 mHandler.sendEmptyMessage(MESSAGE_SHOW);
216 } else {
217 handleShow();
218 }
219 }
220
Charles He9851a8d2017-10-10 17:31:30 +0100221 /**
222 * Dismiss the global actions dialog, if it's currently shown
223 */
224 public void dismissDialog() {
225 mHandler.removeMessages(MESSAGE_DISMISS);
226 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
227 }
228
Jason Monk361915c2017-03-21 20:33:59 -0400229 private void awakenIfNecessary() {
230 if (mDreamManager != null) {
231 try {
232 if (mDreamManager.isDreaming()) {
233 mDreamManager.awaken();
234 }
235 } catch (RemoteException e) {
236 // we tried
237 }
238 }
239 }
240
241 private void handleShow() {
242 awakenIfNecessary();
243 mDialog = createDialog();
244 prepareDialog();
245
246 // If we only have 1 item and it's a simple press action, just do this action.
247 if (mAdapter.getCount() == 1
248 && mAdapter.getItem(0) instanceof SinglePressAction
249 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
250 ((SinglePressAction) mAdapter.getItem(0)).onPress();
251 } else {
252 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
253 attrs.setTitle("ActionsDialog");
Adrian Roos2f05bb32018-02-19 16:42:27 +0100254 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Jason Monk361915c2017-03-21 20:33:59 -0400255 mDialog.getWindow().setAttributes(attrs);
256 mDialog.show();
257 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400258 }
259 }
260
261 /**
262 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400263 *
Jason Monk361915c2017-03-21 20:33:59 -0400264 * @return A new dialog.
265 */
266 private ActionsDialog createDialog() {
267 // Simple toggle style if there's no vibrator, otherwise use a tri-state
268 if (!mHasVibrator) {
269 mSilentModeAction = new SilentModeToggleAction();
270 } else {
271 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
272 }
273 mAirplaneModeOn = new ToggleAction(
274 R.drawable.ic_lock_airplane_mode,
275 R.drawable.ic_lock_airplane_mode_off,
276 R.string.global_actions_toggle_airplane_mode,
277 R.string.global_actions_airplane_mode_on_status,
278 R.string.global_actions_airplane_mode_off_status) {
279
280 void onToggle(boolean on) {
281 if (mHasTelephony && Boolean.parseBoolean(
282 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
283 mIsWaitingForEcmExit = true;
284 // Launch ECM exit dialog
285 Intent ecmDialogIntent =
286 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
287 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
288 mContext.startActivity(ecmDialogIntent);
289 } else {
290 changeAirplaneModeSystemSetting(on);
291 }
292 }
293
294 @Override
295 protected void changeStateFromPress(boolean buttonOn) {
296 if (!mHasTelephony) return;
297
298 // In ECM mode airplane state cannot be changed
299 if (!(Boolean.parseBoolean(
300 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
301 mState = buttonOn ? State.TurningOn : State.TurningOff;
302 mAirplaneState = mState;
303 }
304 }
305
306 public boolean showDuringKeyguard() {
307 return true;
308 }
309
310 public boolean showBeforeProvisioning() {
311 return false;
312 }
313 };
314 onAirplaneModeChanged();
315
316 mItems = new ArrayList<Action>();
317 String[] defaultActions = mContext.getResources().getStringArray(
318 R.array.config_globalActionsList);
319
320 ArraySet<String> addedKeys = new ArraySet<String>();
Alex Chau04458852017-11-27 18:21:23 +0000321 mHasLogoutButton = false;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800322 mHasLockdownButton = false;
Jason Monk361915c2017-03-21 20:33:59 -0400323 for (int i = 0; i < defaultActions.length; i++) {
324 String actionKey = defaultActions[i];
325 if (addedKeys.contains(actionKey)) {
326 // If we already have added this, don't add it again.
327 continue;
328 }
329 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
330 mItems.add(new PowerAction());
331 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
332 mItems.add(mAirplaneModeOn);
333 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
334 if (Settings.Global.getInt(mContext.getContentResolver(),
335 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
336 mItems.add(new BugReportAction());
337 }
338 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
339 if (mShowSilentToggle) {
340 mItems.add(mSilentModeAction);
341 }
342 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
343 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
344 addUsersToMenu(mItems);
345 }
346 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
347 mItems.add(getSettingsAction());
348 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Chad Brubaker02cd6cf2018-05-01 14:59:33 -0700349 if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
350 Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800351 && shouldDisplayLockdown()) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700352 mItems.add(getLockdownAction());
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800353 mHasLockdownButton = true;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700354 }
Jason Monk361915c2017-03-21 20:33:59 -0400355 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
356 mItems.add(getVoiceAssistAction());
357 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
358 mItems.add(getAssistAction());
359 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
360 mItems.add(new RestartAction());
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800361 } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
362 if (Settings.Global.getInt(mContext.getContentResolver(),
363 Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0
364 && !mEmergencyAffordanceManager.needsEmergencyAffordance()) {
365 mItems.add(new EmergencyAction());
366 }
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500367 } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
368 mItems.add(new ScreenshotAction());
Alex Chau04458852017-11-27 18:21:23 +0000369 } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
Alex Chaud7958272017-12-08 11:30:52 +0000370 if (mDevicePolicyManager.isLogoutEnabled()
Alex Chau04458852017-11-27 18:21:23 +0000371 && getCurrentUser().id != UserHandle.USER_SYSTEM) {
372 mItems.add(new LogoutAction());
373 mHasLogoutButton = true;
374 }
Jason Monk361915c2017-03-21 20:33:59 -0400375 } else {
376 Log.e(TAG, "Invalid global action key " + actionKey);
377 }
378 // Add here so we don't add more than one.
379 addedKeys.add(actionKey);
380 }
381
382 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
383 mItems.add(getEmergencyAction());
384 }
385
386 mAdapter = new MyAdapter();
387
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700388 OnItemLongClickListener onItemLongClickListener = (parent, view, position, id) -> {
389 final Action action = mAdapter.getItem(position);
390 if (action instanceof LongPressAction) {
391 mDialog.dismiss();
392 return ((LongPressAction) action).onLongPress();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400393 }
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700394 return false;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400395 };
396 ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener);
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() {
456 super(R.drawable.emergency_icon, R.string.global_action_emergency);
457 }
458
459 @Override
460 public void onPress() {
461 Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
462 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
463 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
464 }
465
466 @Override
467 public boolean showDuringKeyguard() {
468 return true;
469 }
470
471 @Override
472 public boolean showBeforeProvisioning() {
473 return true;
474 }
475 }
476
Jason Monk361915c2017-03-21 20:33:59 -0400477 private final class RestartAction extends SinglePressAction implements LongPressAction {
478 private RestartAction() {
479 super(R.drawable.ic_restart, R.string.global_action_restart);
480 }
481
482 @Override
483 public boolean onLongPress() {
484 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
485 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
486 mWindowManagerFuncs.reboot(true);
487 return true;
488 }
489 return false;
490 }
491
492 @Override
493 public boolean showDuringKeyguard() {
494 return true;
495 }
496
497 @Override
498 public boolean showBeforeProvisioning() {
499 return true;
500 }
501
502 @Override
503 public void onPress() {
504 mWindowManagerFuncs.reboot(false);
505 }
506 }
507
508
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500509 private class ScreenshotAction extends SinglePressAction {
510 public ScreenshotAction() {
511 super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
512 }
513
514 @Override
515 public void onPress() {
516 // Add a little delay before executing, to give the
517 // dialog a chance to go away before it takes a
518 // screenshot.
519 // TODO: instead, omit global action dialog layer
520 mHandler.postDelayed(new Runnable() {
521 @Override
522 public void run() {
523 mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
524 MetricsLogger.action(mContext,
525 MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
526 }
527 }, 500);
528 }
529
530 @Override
531 public boolean showDuringKeyguard() {
532 return true;
533 }
534
535 @Override
536 public boolean showBeforeProvisioning() {
537 return false;
538 }
539 }
540
Jason Monk361915c2017-03-21 20:33:59 -0400541 private class BugReportAction extends SinglePressAction implements LongPressAction {
542
543 public BugReportAction() {
544 super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
545 }
546
547 @Override
548 public void onPress() {
549 // don't actually trigger the bugreport if we are running stability
550 // tests via monkey
551 if (ActivityManager.isUserAMonkey()) {
552 return;
553 }
554 // Add a little delay before executing, to give the
555 // dialog a chance to go away before it takes a
556 // screenshot.
557 mHandler.postDelayed(new Runnable() {
558 @Override
559 public void run() {
560 try {
561 // Take an "interactive" bugreport.
562 MetricsLogger.action(mContext,
563 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
564 ActivityManager.getService().requestBugReport(
565 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
566 } catch (RemoteException e) {
567 }
568 }
569 }, 500);
570 }
571
572 @Override
573 public boolean onLongPress() {
574 // don't actually trigger the bugreport if we are running stability
575 // tests via monkey
576 if (ActivityManager.isUserAMonkey()) {
577 return false;
578 }
579 try {
580 // Take a "full" bugreport.
581 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
582 ActivityManager.getService().requestBugReport(
583 ActivityManager.BUGREPORT_OPTION_FULL);
584 } catch (RemoteException e) {
585 }
586 return false;
587 }
588
589 public boolean showDuringKeyguard() {
590 return true;
591 }
592
593 @Override
594 public boolean showBeforeProvisioning() {
595 return false;
596 }
597
598 @Override
599 public String getStatus() {
600 return mContext.getString(
601 R.string.bugreport_status,
602 Build.VERSION.RELEASE,
603 Build.ID);
604 }
605 }
606
Alex Chau04458852017-11-27 18:21:23 +0000607 private final class LogoutAction extends SinglePressAction {
608 private LogoutAction() {
609 super(R.drawable.ic_logout, R.string.global_action_logout);
610 }
611
612 @Override
613 public boolean showDuringKeyguard() {
614 return true;
615 }
616
617 @Override
618 public boolean showBeforeProvisioning() {
619 return false;
620 }
621
622 @Override
623 public void onPress() {
624 // Add a little delay before executing, to give the dialog a chance to go away before
625 // switching user
626 mHandler.postDelayed(() -> {
627 try {
Alex Chauedb6a012018-01-26 12:52:43 +0000628 int currentUserId = getCurrentUser().id;
Alex Chau04458852017-11-27 18:21:23 +0000629 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
Alex Chauedb6a012018-01-26 12:52:43 +0000630 ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
Alex Chau04458852017-11-27 18:21:23 +0000631 } catch (RemoteException re) {
632 Log.e(TAG, "Couldn't logout user " + re);
633 }
634 }, 500);
635 }
636 }
637
Jason Monk361915c2017-03-21 20:33:59 -0400638 private Action getSettingsAction() {
639 return new SinglePressAction(R.drawable.ic_settings,
640 R.string.global_action_settings) {
641
642 @Override
643 public void onPress() {
644 Intent intent = new Intent(Settings.ACTION_SETTINGS);
645 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
646 mContext.startActivity(intent);
647 }
648
649 @Override
650 public boolean showDuringKeyguard() {
651 return true;
652 }
653
654 @Override
655 public boolean showBeforeProvisioning() {
656 return true;
657 }
658 };
659 }
660
661 private Action getEmergencyAction() {
662 return new SinglePressAction(R.drawable.emergency_icon,
663 R.string.global_action_emergency) {
664 @Override
665 public void onPress() {
666 mEmergencyAffordanceManager.performEmergencyCall();
667 }
668
669 @Override
670 public boolean showDuringKeyguard() {
671 return true;
672 }
673
674 @Override
675 public boolean showBeforeProvisioning() {
676 return true;
677 }
678 };
679 }
680
681 private Action getAssistAction() {
682 return new SinglePressAction(R.drawable.ic_action_assist_focused,
683 R.string.global_action_assist) {
684 @Override
685 public void onPress() {
686 Intent intent = new Intent(Intent.ACTION_ASSIST);
687 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
688 mContext.startActivity(intent);
689 }
690
691 @Override
692 public boolean showDuringKeyguard() {
693 return true;
694 }
695
696 @Override
697 public boolean showBeforeProvisioning() {
698 return true;
699 }
700 };
701 }
702
703 private Action getVoiceAssistAction() {
704 return new SinglePressAction(R.drawable.ic_voice_search,
705 R.string.global_action_voice_assist) {
706 @Override
707 public void onPress() {
708 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
709 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
710 mContext.startActivity(intent);
711 }
712
713 @Override
714 public boolean showDuringKeyguard() {
715 return true;
716 }
717
718 @Override
719 public boolean showBeforeProvisioning() {
720 return true;
721 }
722 };
723 }
724
725 private Action getLockdownAction() {
Alison Cichowlas21125432018-05-16 15:40:45 -0400726 return new SinglePressAction(R.drawable.ic_lock_lockdown,
Jason Monk361915c2017-03-21 20:33:59 -0400727 R.string.global_action_lockdown) {
728
729 @Override
730 public void onPress() {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700731 new LockPatternUtils(mContext)
732 .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
733 UserHandle.USER_ALL);
Jason Monk361915c2017-03-21 20:33:59 -0400734 try {
735 WindowManagerGlobal.getWindowManagerService().lockNow(null);
Pavel Grafov059021b2018-05-02 13:44:46 +0100736 // Lock profiles (if any) on the background thread.
737 final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
738 bgHandler.post(() -> lockProfiles());
Jason Monk361915c2017-03-21 20:33:59 -0400739 } catch (RemoteException e) {
740 Log.e(TAG, "Error while trying to lock device.", e);
741 }
742 }
743
744 @Override
745 public boolean showDuringKeyguard() {
746 return true;
747 }
748
749 @Override
750 public boolean showBeforeProvisioning() {
751 return false;
752 }
753 };
754 }
755
Pavel Grafov059021b2018-05-02 13:44:46 +0100756 private void lockProfiles() {
757 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
758 final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
759 final int currentUserId = getCurrentUser().id;
760 final int[] profileIds = um.getEnabledProfileIds(currentUserId);
761 for (final int id : profileIds) {
762 if (id != currentUserId) {
763 tm.setDeviceLockedForUser(id, true);
764 }
765 }
766 }
767
Jason Monk361915c2017-03-21 20:33:59 -0400768 private UserInfo getCurrentUser() {
769 try {
770 return ActivityManager.getService().getCurrentUser();
771 } catch (RemoteException re) {
772 return null;
773 }
774 }
775
776 private boolean isCurrentUserOwner() {
777 UserInfo currentUser = getCurrentUser();
778 return currentUser == null || currentUser.isPrimary();
779 }
780
781 private void addUsersToMenu(ArrayList<Action> items) {
782 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
783 if (um.isUserSwitcherEnabled()) {
784 List<UserInfo> users = um.getUsers();
785 UserInfo currentUser = getCurrentUser();
786 for (final UserInfo user : users) {
787 if (user.supportsSwitchToByUser()) {
788 boolean isCurrentUser = currentUser == null
789 ? user.id == 0 : (currentUser.id == user.id);
790 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
791 : null;
792 SinglePressAction switchToUser = new SinglePressAction(
793 R.drawable.ic_menu_cc, icon,
794 (user.name != null ? user.name : "Primary")
Jason Monk16fbd9d2017-04-27 14:28:49 -0400795 + (isCurrentUser ? " \u2714" : "")) {
Jason Monk361915c2017-03-21 20:33:59 -0400796 public void onPress() {
797 try {
798 ActivityManager.getService().switchUser(user.id);
799 } catch (RemoteException re) {
800 Log.e(TAG, "Couldn't switch user " + re);
801 }
802 }
803
804 public boolean showDuringKeyguard() {
805 return true;
806 }
807
808 public boolean showBeforeProvisioning() {
809 return false;
810 }
811 };
812 items.add(switchToUser);
813 }
814 }
815 }
816 }
817
818 private void prepareDialog() {
819 refreshSilentMode();
820 mAirplaneModeOn.updateState(mAirplaneState);
821 mAdapter.notifyDataSetChanged();
Jason Monk361915c2017-03-21 20:33:59 -0400822 if (mShowSilentToggle) {
823 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
824 mContext.registerReceiver(mRingerModeReceiver, filter);
825 }
826 }
827
828 private void refreshSilentMode() {
829 if (!mHasVibrator) {
830 final boolean silentModeOn =
831 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400832 ((ToggleAction) mSilentModeAction).updateState(
Jason Monk361915c2017-03-21 20:33:59 -0400833 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
834 }
835 }
836
837 /** {@inheritDoc} */
838 public void onDismiss(DialogInterface dialog) {
839 mWindowManagerFuncs.onGlobalActionsHidden();
840 if (mShowSilentToggle) {
841 try {
842 mContext.unregisterReceiver(mRingerModeReceiver);
843 } catch (IllegalArgumentException ie) {
844 // ignore this
845 Log.w(TAG, ie);
846 }
847 }
848 }
849
850 /** {@inheritDoc} */
851 public void onClick(DialogInterface dialog, int which) {
Jason Monkfd279662017-06-29 19:37:48 -0400852 Action item = mAdapter.getItem(which);
Jason Monkb4302182017-08-04 13:39:17 -0400853 if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400854 dialog.dismiss();
855 }
Jason Monkfd279662017-06-29 19:37:48 -0400856 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400857 }
858
859 /**
860 * The adapter used for the list within the global actions dialog, taking
861 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400862 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
863 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400864 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
865 */
866 private class MyAdapter extends BaseAdapter {
867
868 public int getCount() {
869 int count = 0;
870
871 for (int i = 0; i < mItems.size(); i++) {
872 final Action action = mItems.get(i);
873
874 if (mKeyguardShowing && !action.showDuringKeyguard()) {
875 continue;
876 }
877 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
878 continue;
879 }
880 count++;
881 }
882 return count;
883 }
884
885 @Override
886 public boolean isEnabled(int position) {
887 return getItem(position).isEnabled();
888 }
889
890 @Override
891 public boolean areAllItemsEnabled() {
892 return false;
893 }
894
895 public Action getItem(int position) {
896
897 int filteredPos = 0;
898 for (int i = 0; i < mItems.size(); i++) {
899 final Action action = mItems.get(i);
900 if (mKeyguardShowing && !action.showDuringKeyguard()) {
901 continue;
902 }
903 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
904 continue;
905 }
906 if (filteredPos == position) {
907 return action;
908 }
909 filteredPos++;
910 }
911
912 throw new IllegalArgumentException("position " + position
913 + " out of range of showable actions"
914 + ", filtered count=" + getCount()
915 + ", keyguardshowing=" + mKeyguardShowing
916 + ", provisioned=" + mDeviceProvisioned);
917 }
918
919
920 public long getItemId(int position) {
921 return position;
922 }
923
924 public View getView(int position, View convertView, ViewGroup parent) {
925 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400926 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
Alison Cichowlas3be52db2018-03-06 19:48:06 -0500927 // Everything but screenshot, the last item, gets white background.
928 if (position == getCount() - 1) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400929 HardwareUiLayout.get(parent).setDivisionView(view);
930 }
931 return view;
Jason Monk361915c2017-03-21 20:33:59 -0400932 }
933 }
934
935 // note: the scheme below made more sense when we were planning on having
936 // 8 different things in the global actions dialog. seems overkill with
937 // only 3 items now, but may as well keep this flexible approach so it will
938 // be easy should someone decide at the last minute to include something
939 // else, such as 'enable wifi', or 'enable bluetooth'
940
941 /**
942 * What each item in the global actions dialog must be able to support.
943 */
944 private interface Action {
945 /**
946 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -0400947 * for none.
Jason Monk361915c2017-03-21 20:33:59 -0400948 */
949 CharSequence getLabelForAccessibility(Context context);
950
951 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
952
953 void onPress();
954
955 /**
956 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -0400957 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -0400958 */
959 boolean showDuringKeyguard();
960
961 /**
962 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -0400963 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -0400964 */
965 boolean showBeforeProvisioning();
966
967 boolean isEnabled();
968 }
969
970 /**
971 * An action that also supports long press.
972 */
973 private interface LongPressAction extends Action {
974 boolean onLongPress();
975 }
976
977 /**
978 * A single press action maintains no state, just responds to a press
979 * and takes an action.
980 */
981 private static abstract class SinglePressAction implements Action {
982 private final int mIconResId;
983 private final Drawable mIcon;
984 private final int mMessageResId;
985 private final CharSequence mMessage;
986
987 protected SinglePressAction(int iconResId, int messageResId) {
988 mIconResId = iconResId;
989 mMessageResId = messageResId;
990 mMessage = null;
991 mIcon = null;
992 }
993
994 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
995 mIconResId = iconResId;
996 mMessageResId = 0;
997 mMessage = message;
998 mIcon = icon;
999 }
1000
1001 public boolean isEnabled() {
1002 return true;
1003 }
1004
1005 public String getStatus() {
1006 return null;
1007 }
1008
1009 abstract public void onPress();
1010
1011 public CharSequence getLabelForAccessibility(Context context) {
1012 if (mMessage != null) {
1013 return mMessage;
1014 } else {
1015 return context.getString(mMessageResId);
1016 }
1017 }
1018
1019 public View create(
1020 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001021 View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent,
1022 false);
Jason Monk361915c2017-03-21 20:33:59 -04001023
1024 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1025 TextView messageView = (TextView) v.findViewById(R.id.message);
1026
1027 TextView statusView = (TextView) v.findViewById(R.id.status);
1028 final String status = getStatus();
1029 if (!TextUtils.isEmpty(status)) {
1030 statusView.setText(status);
1031 } else {
1032 statusView.setVisibility(View.GONE);
1033 }
1034 if (mIcon != null) {
1035 icon.setImageDrawable(mIcon);
1036 icon.setScaleType(ScaleType.CENTER_CROP);
1037 } else if (mIconResId != 0) {
1038 icon.setImageDrawable(context.getDrawable(mIconResId));
1039 }
1040 if (mMessage != null) {
1041 messageView.setText(mMessage);
1042 } else {
1043 messageView.setText(mMessageResId);
1044 }
1045
1046 return v;
1047 }
1048 }
1049
1050 /**
1051 * A toggle action knows whether it is on or off, and displays an icon
1052 * and status message accordingly.
1053 */
1054 private static abstract class ToggleAction implements Action {
1055
1056 enum State {
1057 Off(false),
1058 TurningOn(true),
1059 TurningOff(true),
1060 On(false);
1061
1062 private final boolean inTransition;
1063
1064 State(boolean intermediate) {
1065 inTransition = intermediate;
1066 }
1067
1068 public boolean inTransition() {
1069 return inTransition;
1070 }
1071 }
1072
1073 protected State mState = State.Off;
1074
1075 // prefs
1076 protected int mEnabledIconResId;
1077 protected int mDisabledIconResid;
1078 protected int mMessageResId;
1079 protected int mEnabledStatusMessageResId;
1080 protected int mDisabledStatusMessageResId;
1081
1082 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -04001083 * @param enabledIconResId The icon for when this action is on.
1084 * @param disabledIconResid The icon for when this action is off.
1085 * @param message The general information message, e.g 'Silent Mode'
1086 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -04001087 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
1088 */
1089 public ToggleAction(int enabledIconResId,
1090 int disabledIconResid,
1091 int message,
1092 int enabledStatusMessageResId,
1093 int disabledStatusMessageResId) {
1094 mEnabledIconResId = enabledIconResId;
1095 mDisabledIconResid = disabledIconResid;
1096 mMessageResId = message;
1097 mEnabledStatusMessageResId = enabledStatusMessageResId;
1098 mDisabledStatusMessageResId = disabledStatusMessageResId;
1099 }
1100
1101 /**
1102 * Override to make changes to resource IDs just before creating the
1103 * View.
1104 */
1105 void willCreate() {
1106
1107 }
1108
1109 @Override
1110 public CharSequence getLabelForAccessibility(Context context) {
1111 return context.getString(mMessageResId);
1112 }
1113
1114 public View create(Context context, View convertView, ViewGroup parent,
1115 LayoutInflater inflater) {
1116 willCreate();
1117
1118 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -04001119 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -04001120
1121 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1122 TextView messageView = (TextView) v.findViewById(R.id.message);
1123 TextView statusView = (TextView) v.findViewById(R.id.status);
1124 final boolean enabled = isEnabled();
1125
1126 if (messageView != null) {
1127 messageView.setText(mMessageResId);
1128 messageView.setEnabled(enabled);
1129 }
1130
1131 boolean on = ((mState == State.On) || (mState == State.TurningOn));
1132 if (icon != null) {
1133 icon.setImageDrawable(context.getDrawable(
1134 (on ? mEnabledIconResId : mDisabledIconResid)));
1135 icon.setEnabled(enabled);
1136 }
1137
1138 if (statusView != null) {
1139 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
1140 statusView.setVisibility(View.VISIBLE);
1141 statusView.setEnabled(enabled);
1142 }
1143 v.setEnabled(enabled);
1144
1145 return v;
1146 }
1147
1148 public final void onPress() {
1149 if (mState.inTransition()) {
1150 Log.w(TAG, "shouldn't be able to toggle when in transition");
1151 return;
1152 }
1153
1154 final boolean nowOn = !(mState == State.On);
1155 onToggle(nowOn);
1156 changeStateFromPress(nowOn);
1157 }
1158
1159 public boolean isEnabled() {
1160 return !mState.inTransition();
1161 }
1162
1163 /**
1164 * Implementations may override this if their state can be in on of the intermediate
1165 * states until some notification is received (e.g airplane mode is 'turning off' until
1166 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001167 *
Jason Monk361915c2017-03-21 20:33:59 -04001168 * @param buttonOn Whether the button was turned on or off
1169 */
1170 protected void changeStateFromPress(boolean buttonOn) {
1171 mState = buttonOn ? State.On : State.Off;
1172 }
1173
1174 abstract void onToggle(boolean on);
1175
1176 public void updateState(State state) {
1177 mState = state;
1178 }
1179 }
1180
1181 private class SilentModeToggleAction extends ToggleAction {
1182 public SilentModeToggleAction() {
1183 super(R.drawable.ic_audio_vol_mute,
1184 R.drawable.ic_audio_vol,
1185 R.string.global_action_toggle_silent_mode,
1186 R.string.global_action_silent_mode_on_status,
1187 R.string.global_action_silent_mode_off_status);
1188 }
1189
1190 void onToggle(boolean on) {
1191 if (on) {
1192 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1193 } else {
1194 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1195 }
1196 }
1197
1198 public boolean showDuringKeyguard() {
1199 return true;
1200 }
1201
1202 public boolean showBeforeProvisioning() {
1203 return false;
1204 }
1205 }
1206
1207 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1208
Jason Monk16fbd9d2017-04-27 14:28:49 -04001209 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001210
1211 private final AudioManager mAudioManager;
1212 private final Handler mHandler;
1213 private final Context mContext;
1214
1215 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
1216 mAudioManager = audioManager;
1217 mHandler = handler;
1218 mContext = context;
1219 }
1220
1221 private int ringerModeToIndex(int ringerMode) {
1222 // They just happen to coincide
1223 return ringerMode;
1224 }
1225
1226 private int indexToRingerMode(int index) {
1227 // They just happen to coincide
1228 return index;
1229 }
1230
1231 @Override
1232 public CharSequence getLabelForAccessibility(Context context) {
1233 return null;
1234 }
1235
1236 public View create(Context context, View convertView, ViewGroup parent,
1237 LayoutInflater inflater) {
1238 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1239
1240 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1241 for (int i = 0; i < 3; i++) {
1242 View itemView = v.findViewById(ITEM_IDS[i]);
1243 itemView.setSelected(selectedIndex == i);
1244 // Set up click handler
1245 itemView.setTag(i);
1246 itemView.setOnClickListener(this);
1247 }
1248 return v;
1249 }
1250
1251 public void onPress() {
1252 }
1253
1254 public boolean showDuringKeyguard() {
1255 return true;
1256 }
1257
1258 public boolean showBeforeProvisioning() {
1259 return false;
1260 }
1261
1262 public boolean isEnabled() {
1263 return true;
1264 }
1265
1266 void willCreate() {
1267 }
1268
1269 public void onClick(View v) {
1270 if (!(v.getTag() instanceof Integer)) return;
1271
1272 int index = (Integer) v.getTag();
1273 mAudioManager.setRingerMode(indexToRingerMode(index));
1274 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1275 }
1276 }
1277
1278 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1279 public void onReceive(Context context, Intent intent) {
1280 String action = intent.getAction();
1281 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1282 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1283 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1284 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001285 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISMISS, reason));
Jason Monk361915c2017-03-21 20:33:59 -04001286 }
1287 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1288 // Airplane mode can be changed after ECM exits if airplane toggle button
1289 // is pressed during ECM mode
1290 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1291 mIsWaitingForEcmExit) {
1292 mIsWaitingForEcmExit = false;
1293 changeAirplaneModeSystemSetting(true);
1294 }
1295 }
1296 }
1297 };
1298
1299 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1300 @Override
1301 public void onServiceStateChanged(ServiceState serviceState) {
1302 if (!mHasTelephony) return;
1303 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1304 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1305 mAirplaneModeOn.updateState(mAirplaneState);
1306 mAdapter.notifyDataSetChanged();
1307 }
1308 };
1309
1310 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1311 @Override
1312 public void onReceive(Context context, Intent intent) {
1313 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1314 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1315 }
1316 }
1317 };
1318
1319 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1320 @Override
1321 public void onChange(boolean selfChange) {
1322 onAirplaneModeChanged();
1323 }
1324 };
1325
1326 private static final int MESSAGE_DISMISS = 0;
1327 private static final int MESSAGE_REFRESH = 1;
1328 private static final int MESSAGE_SHOW = 2;
1329 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1330
1331 private Handler mHandler = new Handler() {
1332 public void handleMessage(Message msg) {
1333 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001334 case MESSAGE_DISMISS:
1335 if (mDialog != null) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001336 if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
1337 mDialog.dismissImmediately();
1338 } else {
1339 mDialog.dismiss();
1340 }
Jason Monk16fbd9d2017-04-27 14:28:49 -04001341 mDialog = null;
1342 }
1343 break;
1344 case MESSAGE_REFRESH:
1345 refreshSilentMode();
1346 mAdapter.notifyDataSetChanged();
1347 break;
1348 case MESSAGE_SHOW:
1349 handleShow();
1350 break;
Jason Monk361915c2017-03-21 20:33:59 -04001351 }
1352 }
1353 };
1354
1355 private void onAirplaneModeChanged() {
1356 // Let the service state callbacks handle the state.
1357 if (mHasTelephony) return;
1358
1359 boolean airplaneModeOn = Settings.Global.getInt(
1360 mContext.getContentResolver(),
1361 Settings.Global.AIRPLANE_MODE_ON,
1362 0) == 1;
1363 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1364 mAirplaneModeOn.updateState(mAirplaneState);
1365 }
1366
1367 /**
1368 * Change the airplane mode system setting
1369 */
1370 private void changeAirplaneModeSystemSetting(boolean on) {
1371 Settings.Global.putInt(
1372 mContext.getContentResolver(),
1373 Settings.Global.AIRPLANE_MODE_ON,
1374 on ? 1 : 0);
1375 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1376 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1377 intent.putExtra("state", on);
1378 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1379 if (!mHasTelephony) {
1380 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1381 }
1382 }
1383
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001384 private static final class ActionsDialog extends Dialog implements DialogInterface,
1385 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001386
Jason Monk16fbd9d2017-04-27 14:28:49 -04001387 private final Context mContext;
1388 private final MyAdapter mAdapter;
1389 private final LinearLayout mListView;
1390 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;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001396
1397 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
1398 OnItemLongClickListener longClickListener) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001399 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Lucas Dupin448786c2017-07-24 17:44:25 -07001400 mContext = context;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001401 mAdapter = adapter;
1402 mClickListener = clickListener;
1403 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001404 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001405 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001406
1407 // Window initialization
1408 Window window = getWindow();
1409 window.requestFeature(Window.FEATURE_NO_TITLE);
Adrian Roosedfab3b2018-03-08 18:39:20 +01001410 // Inflate the decor view, so the attributes below are not overwritten by the theme.
1411 window.getDecorView();
1412 window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
1413 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
1414 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
1415 window.setLayout(MATCH_PARENT, MATCH_PARENT);
1416 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
Alison Cichowlas4f19f4a2017-07-25 10:56:16 -04001417 window.addFlags(
1418 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001419 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Adrian Roosedfab3b2018-03-08 18:39:20 +01001420 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001421 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1422 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1423 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1424 window.setBackgroundDrawable(mGradientDrawable);
1425 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1426
Jason Monk16fbd9d2017-04-27 14:28:49 -04001427 setContentView(com.android.systemui.R.layout.global_actions_wrapped);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001428 mListView = findViewById(android.R.id.list);
1429 mHardwareLayout = HardwareUiLayout.get(mListView);
1430 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
Phil Weaver8583ae82018-02-13 11:01:24 -08001431 setTitle(R.string.global_actions);
Phil Weaver9054e092018-04-27 16:28:50 -07001432 mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
1433 @Override
1434 public boolean dispatchPopulateAccessibilityEvent(
1435 View host, AccessibilityEvent event) {
1436 // Populate the title here, just as Activity does
1437 event.getText().add(mContext.getString(R.string.global_actions));
1438 return true;
1439 }
1440 });
Jason Monk361915c2017-03-21 20:33:59 -04001441 }
1442
Jason Monk16fbd9d2017-04-27 14:28:49 -04001443 private void updateList() {
1444 mListView.removeAllViews();
1445 for (int i = 0; i < mAdapter.getCount(); i++) {
1446 View v = mAdapter.getView(i, null, mListView);
1447 final int pos = i;
1448 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1449 v.setOnLongClickListener(view ->
1450 mLongClickListener.onItemLongClick(null, v, pos, 0));
1451 mListView.addView(v);
1452 }
Jason Monk361915c2017-03-21 20:33:59 -04001453 }
1454
1455 @Override
1456 protected void onStart() {
1457 super.setCanceledOnTouchOutside(true);
1458 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001459 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001460
1461 Point displaySize = new Point();
1462 mContext.getDisplay().getRealSize(displaySize);
1463 mColorExtractor.addOnColorsChangedListener(this);
1464 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1465 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1466 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001467 updateColors(colors, false /* animate */);
1468 }
1469
1470 /**
1471 * Updates background and system bars according to current GradientColors.
1472 * @param colors Colors and hints to use.
1473 * @param animate Interpolates gradient if true, just sets otherwise.
1474 */
1475 private void updateColors(GradientColors colors, boolean animate) {
1476 mGradientDrawable.setColors(colors, animate);
1477 View decorView = getWindow().getDecorView();
1478 if (colors.supportsDarkText()) {
1479 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
1480 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
1481 } else {
1482 decorView.setSystemUiVisibility(0);
1483 }
Jason Monk361915c2017-03-21 20:33:59 -04001484 }
1485
1486 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001487 protected void onStop() {
1488 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001489 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001490 }
1491
1492 @Override
1493 public void show() {
1494 super.show();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001495 mGradientDrawable.setAlpha(0);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001496 mHardwareLayout.setTranslationX(getAnimTranslation());
1497 mHardwareLayout.setAlpha(0);
1498 mHardwareLayout.animate()
1499 .alpha(1)
1500 .translationX(0)
1501 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001502 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001503 .setUpdateListener(animation -> {
1504 int alpha = (int) ((Float) animation.getAnimatedValue()
1505 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1506 mGradientDrawable.setAlpha(alpha);
1507 })
Jason Monka7af3b62017-07-07 11:35:13 -04001508 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
Jason Monk16fbd9d2017-04-27 14:28:49 -04001509 .start();
1510 }
1511
1512 @Override
1513 public void dismiss() {
1514 mHardwareLayout.setTranslationX(0);
1515 mHardwareLayout.setAlpha(1);
1516 mHardwareLayout.animate()
1517 .alpha(0)
1518 .translationX(getAnimTranslation())
1519 .setDuration(300)
1520 .withEndAction(() -> super.dismiss())
1521 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001522 .setUpdateListener(animation -> {
1523 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1524 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1525 mGradientDrawable.setAlpha(alpha);
1526 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001527 .start();
1528 }
1529
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001530 void dismissImmediately() {
1531 super.dismiss();
1532 }
1533
Jason Monk16fbd9d2017-04-27 14:28:49 -04001534 private float getAnimTranslation() {
1535 return getContext().getResources().getDimension(
1536 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001537 }
1538
1539 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001540 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001541 if (mKeyguardShowing) {
1542 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001543 updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
1544 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001545 }
1546 } else {
1547 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001548 updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
1549 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001550 }
1551 }
1552 }
1553
1554 public void setKeyguardShowing(boolean keyguardShowing) {
1555 mKeyguardShowing = keyguardShowing;
1556 }
Jason Monk361915c2017-03-21 20:33:59 -04001557 }
1558}