blob: 238ab299aa5e1a88b85e16e544912908ac374cb4 [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
Chad Brubakerf4075fe2018-01-03 13:23:22 -080017import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
18import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -070019import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
20
Jason Monk361915c2017-03-21 20:33:59 -040021import android.app.ActivityManager;
22import android.app.Dialog;
Chad Brubakerf4075fe2018-01-03 13:23:22 -080023import android.app.KeyguardManager;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070024import android.app.WallpaperManager;
Alex Chau04458852017-11-27 18:21:23 +000025import android.app.admin.DevicePolicyManager;
Jason Monk361915c2017-03-21 20:33:59 -040026import android.content.BroadcastReceiver;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050027import android.content.ComponentName;
Jason Monk361915c2017-03-21 20:33:59 -040028import android.content.Context;
29import android.content.DialogInterface;
30import android.content.Intent;
31import android.content.IntentFilter;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050032import android.content.ServiceConnection;
Jason Monk361915c2017-03-21 20:33:59 -040033import android.content.pm.UserInfo;
34import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070035import android.graphics.Point;
Jason Monk361915c2017-03-21 20:33:59 -040036import android.graphics.drawable.Drawable;
37import android.media.AudioManager;
38import android.net.ConnectivityManager;
39import android.os.Build;
Jason Monk361915c2017-03-21 20:33:59 -040040import android.os.Handler;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050041import android.os.IBinder;
Jason Monk361915c2017-03-21 20:33:59 -040042import android.os.Message;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050043import android.os.Messenger;
Jason Monk361915c2017-03-21 20:33:59 -040044import 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;
67import android.widget.AdapterView;
Jason Monk16fbd9d2017-04-27 14:28:49 -040068import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040069import android.widget.BaseAdapter;
70import 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";
107
108 private static final String TAG = "GlobalActionsDialog";
109
110 private static final boolean SHOW_SILENT_TOGGLE = true;
111
112 /* Valid settings for global actions keys.
113 * see config.xml config_globalActionList */
114 private static final String GLOBAL_ACTION_KEY_POWER = "power";
115 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
116 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
117 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
118 private static final String GLOBAL_ACTION_KEY_USERS = "users";
119 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
120 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
121 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
122 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
123 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
Alex Chau04458852017-11-27 18:21:23 +0000124 private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500125 private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
Jason Monk361915c2017-03-21 20:33:59 -0400126
127 private final Context mContext;
128 private final GlobalActionsManager mWindowManagerFuncs;
129 private final AudioManager mAudioManager;
130 private final IDreamManager mDreamManager;
Alex Chau04458852017-11-27 18:21:23 +0000131 private final DevicePolicyManager mDevicePolicyManager;
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800132 private final LockPatternUtils mLockPatternUtils;
133 private final KeyguardManager mKeyguardManager;
Jason Monk361915c2017-03-21 20:33:59 -0400134
135 private ArrayList<Action> mItems;
136 private ActionsDialog mDialog;
137
138 private Action mSilentModeAction;
139 private ToggleAction mAirplaneModeOn;
140
141 private MyAdapter mAdapter;
142
143 private boolean mKeyguardShowing = false;
144 private boolean mDeviceProvisioned = false;
145 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
146 private boolean mIsWaitingForEcmExit = false;
147 private boolean mHasTelephony;
148 private boolean mHasVibrator;
Alex Chau04458852017-11-27 18:21:23 +0000149 private boolean mHasLogoutButton;
Jason Monk361915c2017-03-21 20:33:59 -0400150 private final boolean mShowSilentToggle;
151 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500152 private final ScreenshotHelper mScreenshotHelper;
Jason Monk361915c2017-03-21 20:33:59 -0400153
154 /**
155 * @param context everything needs a context :(
156 */
157 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
Lucas Dupin448786c2017-07-24 17:44:25 -0700158 mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
Jason Monk361915c2017-03-21 20:33:59 -0400159 mWindowManagerFuncs = windowManagerFuncs;
160 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
161 mDreamManager = IDreamManager.Stub.asInterface(
162 ServiceManager.getService(DreamService.DREAM_SERVICE));
Alex Chau04458852017-11-27 18:21:23 +0000163 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
164 Context.DEVICE_POLICY_SERVICE);
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800165 mLockPatternUtils = new LockPatternUtils(mContext);
166 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Jason Monk361915c2017-03-21 20:33:59 -0400167
168 // receive broadcasts
169 IntentFilter filter = new IntentFilter();
170 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
171 filter.addAction(Intent.ACTION_SCREEN_OFF);
172 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
173 context.registerReceiver(mBroadcastReceiver, filter);
174
175 ConnectivityManager cm = (ConnectivityManager)
176 context.getSystemService(Context.CONNECTIVITY_SERVICE);
177 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
178
179 // get notified of phone state changes
180 TelephonyManager telephonyManager =
181 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
182 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
183 mContext.getContentResolver().registerContentObserver(
184 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
185 mAirplaneModeObserver);
186 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
187 mHasVibrator = vibrator != null && vibrator.hasVibrator();
188
189 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
190 R.bool.config_useFixedVolume);
191
192 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500193 mScreenshotHelper = new ScreenshotHelper(context);
Jason Monk361915c2017-03-21 20:33:59 -0400194 }
195
196 /**
197 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400198 *
Jason Monk361915c2017-03-21 20:33:59 -0400199 * @param keyguardShowing True if keyguard is showing
200 */
201 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
202 mKeyguardShowing = keyguardShowing;
203 mDeviceProvisioned = isDeviceProvisioned;
204 if (mDialog != null) {
205 mDialog.dismiss();
206 mDialog = null;
207 // Show delayed, so that the dismiss of the previous dialog completes
208 mHandler.sendEmptyMessage(MESSAGE_SHOW);
209 } else {
210 handleShow();
211 }
212 }
213
Charles He9851a8d2017-10-10 17:31:30 +0100214 /**
215 * Dismiss the global actions dialog, if it's currently shown
216 */
217 public void dismissDialog() {
218 mHandler.removeMessages(MESSAGE_DISMISS);
219 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
220 }
221
Jason Monk361915c2017-03-21 20:33:59 -0400222 private void awakenIfNecessary() {
223 if (mDreamManager != null) {
224 try {
225 if (mDreamManager.isDreaming()) {
226 mDreamManager.awaken();
227 }
228 } catch (RemoteException e) {
229 // we tried
230 }
231 }
232 }
233
234 private void handleShow() {
235 awakenIfNecessary();
236 mDialog = createDialog();
237 prepareDialog();
238
239 // If we only have 1 item and it's a simple press action, just do this action.
240 if (mAdapter.getCount() == 1
241 && mAdapter.getItem(0) instanceof SinglePressAction
242 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
243 ((SinglePressAction) mAdapter.getItem(0)).onPress();
244 } else {
245 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
246 attrs.setTitle("ActionsDialog");
247 mDialog.getWindow().setAttributes(attrs);
248 mDialog.show();
249 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400250 }
251 }
252
253 /**
254 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400255 *
Jason Monk361915c2017-03-21 20:33:59 -0400256 * @return A new dialog.
257 */
258 private ActionsDialog createDialog() {
259 // Simple toggle style if there's no vibrator, otherwise use a tri-state
260 if (!mHasVibrator) {
261 mSilentModeAction = new SilentModeToggleAction();
262 } else {
263 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
264 }
265 mAirplaneModeOn = new ToggleAction(
266 R.drawable.ic_lock_airplane_mode,
267 R.drawable.ic_lock_airplane_mode_off,
268 R.string.global_actions_toggle_airplane_mode,
269 R.string.global_actions_airplane_mode_on_status,
270 R.string.global_actions_airplane_mode_off_status) {
271
272 void onToggle(boolean on) {
273 if (mHasTelephony && Boolean.parseBoolean(
274 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
275 mIsWaitingForEcmExit = true;
276 // Launch ECM exit dialog
277 Intent ecmDialogIntent =
278 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
279 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
280 mContext.startActivity(ecmDialogIntent);
281 } else {
282 changeAirplaneModeSystemSetting(on);
283 }
284 }
285
286 @Override
287 protected void changeStateFromPress(boolean buttonOn) {
288 if (!mHasTelephony) return;
289
290 // In ECM mode airplane state cannot be changed
291 if (!(Boolean.parseBoolean(
292 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
293 mState = buttonOn ? State.TurningOn : State.TurningOff;
294 mAirplaneState = mState;
295 }
296 }
297
298 public boolean showDuringKeyguard() {
299 return true;
300 }
301
302 public boolean showBeforeProvisioning() {
303 return false;
304 }
305 };
306 onAirplaneModeChanged();
307
308 mItems = new ArrayList<Action>();
309 String[] defaultActions = mContext.getResources().getStringArray(
310 R.array.config_globalActionsList);
311
312 ArraySet<String> addedKeys = new ArraySet<String>();
Alex Chau04458852017-11-27 18:21:23 +0000313 mHasLogoutButton = false;
Jason Monk361915c2017-03-21 20:33:59 -0400314 for (int i = 0; i < defaultActions.length; i++) {
315 String actionKey = defaultActions[i];
316 if (addedKeys.contains(actionKey)) {
317 // If we already have added this, don't add it again.
318 continue;
319 }
320 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
321 mItems.add(new PowerAction());
322 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
323 mItems.add(mAirplaneModeOn);
324 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
325 if (Settings.Global.getInt(mContext.getContentResolver(),
326 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
327 mItems.add(new BugReportAction());
328 }
329 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
330 if (mShowSilentToggle) {
331 mItems.add(mSilentModeAction);
332 }
333 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
334 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
335 addUsersToMenu(mItems);
336 }
337 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
338 mItems.add(getSettingsAction());
339 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700340 if (Settings.Secure.getInt(mContext.getContentResolver(),
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800341 Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0
342 && shouldDisplayLockdown()) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700343 mItems.add(getLockdownAction());
344 }
Jason Monk361915c2017-03-21 20:33:59 -0400345 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
346 mItems.add(getVoiceAssistAction());
347 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
348 mItems.add(getAssistAction());
349 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
350 mItems.add(new RestartAction());
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500351 } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
352 mItems.add(new ScreenshotAction());
Alex Chau04458852017-11-27 18:21:23 +0000353 } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
Alex Chaud7958272017-12-08 11:30:52 +0000354 if (mDevicePolicyManager.isLogoutEnabled()
Alex Chau04458852017-11-27 18:21:23 +0000355 && getCurrentUser().id != UserHandle.USER_SYSTEM) {
356 mItems.add(new LogoutAction());
357 mHasLogoutButton = true;
358 }
Jason Monk361915c2017-03-21 20:33:59 -0400359 } else {
360 Log.e(TAG, "Invalid global action key " + actionKey);
361 }
362 // Add here so we don't add more than one.
363 addedKeys.add(actionKey);
364 }
365
366 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
367 mItems.add(getEmergencyAction());
368 }
369
370 mAdapter = new MyAdapter();
371
Jason Monk16fbd9d2017-04-27 14:28:49 -0400372 OnItemLongClickListener onItemLongClickListener = new OnItemLongClickListener() {
373 @Override
374 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
375 long id) {
376 final Action action = mAdapter.getItem(position);
377 if (action instanceof LongPressAction) {
Shunta Satobdb0e492017-04-14 16:01:54 +0900378 mDialog.dismiss();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400379 return ((LongPressAction) action).onLongPress();
380 }
381 return false;
382 }
383 };
384 ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener);
Jason Monk361915c2017-03-21 20:33:59 -0400385 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700386 dialog.setKeyguardShowing(mKeyguardShowing);
Jason Monk361915c2017-03-21 20:33:59 -0400387
388 dialog.setOnDismissListener(this);
389
390 return dialog;
391 }
392
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800393 private boolean shouldDisplayLockdown() {
394 int userId = getCurrentUser().id;
395 // Lockdown is meaningless without a place to go.
396 if (!mKeyguardManager.isDeviceSecure(userId)) {
397 return false;
398 }
399
400 // Only show the lockdown button if the device isn't locked down (for whatever reason).
401 int state = mLockPatternUtils.getStrongAuthForUser(userId);
402 return (state == STRONG_AUTH_NOT_REQUIRED
403 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
404 }
405
Jason Monk361915c2017-03-21 20:33:59 -0400406 private final class PowerAction extends SinglePressAction implements LongPressAction {
407 private PowerAction() {
408 super(R.drawable.ic_lock_power_off,
Jason Monk16fbd9d2017-04-27 14:28:49 -0400409 R.string.global_action_power_off);
Jason Monk361915c2017-03-21 20:33:59 -0400410 }
411
412 @Override
413 public boolean onLongPress() {
414 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
415 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
416 mWindowManagerFuncs.reboot(true);
417 return true;
418 }
419 return false;
420 }
421
422 @Override
423 public boolean showDuringKeyguard() {
424 return true;
425 }
426
427 @Override
428 public boolean showBeforeProvisioning() {
429 return true;
430 }
431
432 @Override
433 public void onPress() {
434 // shutdown by making sure radio and power are handled accordingly.
435 mWindowManagerFuncs.shutdown();
436 }
437 }
438
439 private final class RestartAction extends SinglePressAction implements LongPressAction {
440 private RestartAction() {
441 super(R.drawable.ic_restart, R.string.global_action_restart);
442 }
443
444 @Override
445 public boolean onLongPress() {
446 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
447 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
448 mWindowManagerFuncs.reboot(true);
449 return true;
450 }
451 return false;
452 }
453
454 @Override
455 public boolean showDuringKeyguard() {
456 return true;
457 }
458
459 @Override
460 public boolean showBeforeProvisioning() {
461 return true;
462 }
463
464 @Override
465 public void onPress() {
466 mWindowManagerFuncs.reboot(false);
467 }
468 }
469
470
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500471 private class ScreenshotAction extends SinglePressAction {
472 public ScreenshotAction() {
473 super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
474 }
475
476 @Override
477 public void onPress() {
478 // Add a little delay before executing, to give the
479 // dialog a chance to go away before it takes a
480 // screenshot.
481 // TODO: instead, omit global action dialog layer
482 mHandler.postDelayed(new Runnable() {
483 @Override
484 public void run() {
485 mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
486 MetricsLogger.action(mContext,
487 MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
488 }
489 }, 500);
490 }
491
492 @Override
493 public boolean showDuringKeyguard() {
494 return true;
495 }
496
497 @Override
498 public boolean showBeforeProvisioning() {
499 return false;
500 }
501 }
502
Jason Monk361915c2017-03-21 20:33:59 -0400503 private class BugReportAction extends SinglePressAction implements LongPressAction {
504
505 public BugReportAction() {
506 super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
507 }
508
509 @Override
510 public void onPress() {
511 // don't actually trigger the bugreport if we are running stability
512 // tests via monkey
513 if (ActivityManager.isUserAMonkey()) {
514 return;
515 }
516 // Add a little delay before executing, to give the
517 // dialog a chance to go away before it takes a
518 // screenshot.
519 mHandler.postDelayed(new Runnable() {
520 @Override
521 public void run() {
522 try {
523 // Take an "interactive" bugreport.
524 MetricsLogger.action(mContext,
525 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
526 ActivityManager.getService().requestBugReport(
527 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
528 } catch (RemoteException e) {
529 }
530 }
531 }, 500);
532 }
533
534 @Override
535 public boolean onLongPress() {
536 // don't actually trigger the bugreport if we are running stability
537 // tests via monkey
538 if (ActivityManager.isUserAMonkey()) {
539 return false;
540 }
541 try {
542 // Take a "full" bugreport.
543 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
544 ActivityManager.getService().requestBugReport(
545 ActivityManager.BUGREPORT_OPTION_FULL);
546 } catch (RemoteException e) {
547 }
548 return false;
549 }
550
551 public boolean showDuringKeyguard() {
552 return true;
553 }
554
555 @Override
556 public boolean showBeforeProvisioning() {
557 return false;
558 }
559
560 @Override
561 public String getStatus() {
562 return mContext.getString(
563 R.string.bugreport_status,
564 Build.VERSION.RELEASE,
565 Build.ID);
566 }
567 }
568
Alex Chau04458852017-11-27 18:21:23 +0000569 private final class LogoutAction extends SinglePressAction {
570 private LogoutAction() {
571 super(R.drawable.ic_logout, R.string.global_action_logout);
572 }
573
574 @Override
575 public boolean showDuringKeyguard() {
576 return true;
577 }
578
579 @Override
580 public boolean showBeforeProvisioning() {
581 return false;
582 }
583
584 @Override
585 public void onPress() {
586 // Add a little delay before executing, to give the dialog a chance to go away before
587 // switching user
588 mHandler.postDelayed(() -> {
589 try {
Alex Chauedb6a012018-01-26 12:52:43 +0000590 int currentUserId = getCurrentUser().id;
Alex Chau04458852017-11-27 18:21:23 +0000591 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
Alex Chauedb6a012018-01-26 12:52:43 +0000592 ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
Alex Chau04458852017-11-27 18:21:23 +0000593 } catch (RemoteException re) {
594 Log.e(TAG, "Couldn't logout user " + re);
595 }
596 }, 500);
597 }
598 }
599
Jason Monk361915c2017-03-21 20:33:59 -0400600 private Action getSettingsAction() {
601 return new SinglePressAction(R.drawable.ic_settings,
602 R.string.global_action_settings) {
603
604 @Override
605 public void onPress() {
606 Intent intent = new Intent(Settings.ACTION_SETTINGS);
607 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
608 mContext.startActivity(intent);
609 }
610
611 @Override
612 public boolean showDuringKeyguard() {
613 return true;
614 }
615
616 @Override
617 public boolean showBeforeProvisioning() {
618 return true;
619 }
620 };
621 }
622
623 private Action getEmergencyAction() {
624 return new SinglePressAction(R.drawable.emergency_icon,
625 R.string.global_action_emergency) {
626 @Override
627 public void onPress() {
628 mEmergencyAffordanceManager.performEmergencyCall();
629 }
630
631 @Override
632 public boolean showDuringKeyguard() {
633 return true;
634 }
635
636 @Override
637 public boolean showBeforeProvisioning() {
638 return true;
639 }
640 };
641 }
642
643 private Action getAssistAction() {
644 return new SinglePressAction(R.drawable.ic_action_assist_focused,
645 R.string.global_action_assist) {
646 @Override
647 public void onPress() {
648 Intent intent = new Intent(Intent.ACTION_ASSIST);
649 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
650 mContext.startActivity(intent);
651 }
652
653 @Override
654 public boolean showDuringKeyguard() {
655 return true;
656 }
657
658 @Override
659 public boolean showBeforeProvisioning() {
660 return true;
661 }
662 };
663 }
664
665 private Action getVoiceAssistAction() {
666 return new SinglePressAction(R.drawable.ic_voice_search,
667 R.string.global_action_voice_assist) {
668 @Override
669 public void onPress() {
670 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
671 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
672 mContext.startActivity(intent);
673 }
674
675 @Override
676 public boolean showDuringKeyguard() {
677 return true;
678 }
679
680 @Override
681 public boolean showBeforeProvisioning() {
682 return true;
683 }
684 };
685 }
686
687 private Action getLockdownAction() {
688 return new SinglePressAction(R.drawable.ic_lock_lock,
689 R.string.global_action_lockdown) {
690
691 @Override
692 public void onPress() {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700693 new LockPatternUtils(mContext)
694 .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
695 UserHandle.USER_ALL);
Jason Monk361915c2017-03-21 20:33:59 -0400696 try {
697 WindowManagerGlobal.getWindowManagerService().lockNow(null);
698 } catch (RemoteException e) {
699 Log.e(TAG, "Error while trying to lock device.", e);
700 }
701 }
702
703 @Override
704 public boolean showDuringKeyguard() {
705 return true;
706 }
707
708 @Override
709 public boolean showBeforeProvisioning() {
710 return false;
711 }
712 };
713 }
714
715 private UserInfo getCurrentUser() {
716 try {
717 return ActivityManager.getService().getCurrentUser();
718 } catch (RemoteException re) {
719 return null;
720 }
721 }
722
723 private boolean isCurrentUserOwner() {
724 UserInfo currentUser = getCurrentUser();
725 return currentUser == null || currentUser.isPrimary();
726 }
727
728 private void addUsersToMenu(ArrayList<Action> items) {
729 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
730 if (um.isUserSwitcherEnabled()) {
731 List<UserInfo> users = um.getUsers();
732 UserInfo currentUser = getCurrentUser();
733 for (final UserInfo user : users) {
734 if (user.supportsSwitchToByUser()) {
735 boolean isCurrentUser = currentUser == null
736 ? user.id == 0 : (currentUser.id == user.id);
737 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
738 : null;
739 SinglePressAction switchToUser = new SinglePressAction(
740 R.drawable.ic_menu_cc, icon,
741 (user.name != null ? user.name : "Primary")
Jason Monk16fbd9d2017-04-27 14:28:49 -0400742 + (isCurrentUser ? " \u2714" : "")) {
Jason Monk361915c2017-03-21 20:33:59 -0400743 public void onPress() {
744 try {
745 ActivityManager.getService().switchUser(user.id);
746 } catch (RemoteException re) {
747 Log.e(TAG, "Couldn't switch user " + re);
748 }
749 }
750
751 public boolean showDuringKeyguard() {
752 return true;
753 }
754
755 public boolean showBeforeProvisioning() {
756 return false;
757 }
758 };
759 items.add(switchToUser);
760 }
761 }
762 }
763 }
764
765 private void prepareDialog() {
766 refreshSilentMode();
767 mAirplaneModeOn.updateState(mAirplaneState);
768 mAdapter.notifyDataSetChanged();
Jason Monk361915c2017-03-21 20:33:59 -0400769 if (mShowSilentToggle) {
770 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
771 mContext.registerReceiver(mRingerModeReceiver, filter);
772 }
773 }
774
775 private void refreshSilentMode() {
776 if (!mHasVibrator) {
777 final boolean silentModeOn =
778 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400779 ((ToggleAction) mSilentModeAction).updateState(
Jason Monk361915c2017-03-21 20:33:59 -0400780 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
781 }
782 }
783
784 /** {@inheritDoc} */
785 public void onDismiss(DialogInterface dialog) {
786 mWindowManagerFuncs.onGlobalActionsHidden();
787 if (mShowSilentToggle) {
788 try {
789 mContext.unregisterReceiver(mRingerModeReceiver);
790 } catch (IllegalArgumentException ie) {
791 // ignore this
792 Log.w(TAG, ie);
793 }
794 }
795 }
796
797 /** {@inheritDoc} */
798 public void onClick(DialogInterface dialog, int which) {
Jason Monkfd279662017-06-29 19:37:48 -0400799 Action item = mAdapter.getItem(which);
Jason Monkb4302182017-08-04 13:39:17 -0400800 if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400801 dialog.dismiss();
802 }
Jason Monkfd279662017-06-29 19:37:48 -0400803 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400804 }
805
806 /**
807 * The adapter used for the list within the global actions dialog, taking
808 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400809 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
810 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400811 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
812 */
813 private class MyAdapter extends BaseAdapter {
814
815 public int getCount() {
816 int count = 0;
817
818 for (int i = 0; i < mItems.size(); i++) {
819 final Action action = mItems.get(i);
820
821 if (mKeyguardShowing && !action.showDuringKeyguard()) {
822 continue;
823 }
824 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
825 continue;
826 }
827 count++;
828 }
829 return count;
830 }
831
832 @Override
833 public boolean isEnabled(int position) {
834 return getItem(position).isEnabled();
835 }
836
837 @Override
838 public boolean areAllItemsEnabled() {
839 return false;
840 }
841
842 public Action getItem(int position) {
843
844 int filteredPos = 0;
845 for (int i = 0; i < mItems.size(); i++) {
846 final Action action = mItems.get(i);
847 if (mKeyguardShowing && !action.showDuringKeyguard()) {
848 continue;
849 }
850 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
851 continue;
852 }
853 if (filteredPos == position) {
854 return action;
855 }
856 filteredPos++;
857 }
858
859 throw new IllegalArgumentException("position " + position
860 + " out of range of showable actions"
861 + ", filtered count=" + getCount()
862 + ", keyguardshowing=" + mKeyguardShowing
863 + ", provisioned=" + mDeviceProvisioned);
864 }
865
866
867 public long getItemId(int position) {
868 return position;
869 }
870
871 public View getView(int position, View convertView, ViewGroup parent) {
872 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400873 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
Alex Chau04458852017-11-27 18:21:23 +0000874 // When there is no logout button, only power off and restart should be in white
875 // background, thus setting division view at third item; with logout button being the
876 // third item, set the division view at fourth item instead.
877 if (position == (mHasLogoutButton ? 3 : 2)) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400878 HardwareUiLayout.get(parent).setDivisionView(view);
879 }
880 return view;
Jason Monk361915c2017-03-21 20:33:59 -0400881 }
882 }
883
884 // note: the scheme below made more sense when we were planning on having
885 // 8 different things in the global actions dialog. seems overkill with
886 // only 3 items now, but may as well keep this flexible approach so it will
887 // be easy should someone decide at the last minute to include something
888 // else, such as 'enable wifi', or 'enable bluetooth'
889
890 /**
891 * What each item in the global actions dialog must be able to support.
892 */
893 private interface Action {
894 /**
895 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -0400896 * for none.
Jason Monk361915c2017-03-21 20:33:59 -0400897 */
898 CharSequence getLabelForAccessibility(Context context);
899
900 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
901
902 void onPress();
903
904 /**
905 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -0400906 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -0400907 */
908 boolean showDuringKeyguard();
909
910 /**
911 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -0400912 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -0400913 */
914 boolean showBeforeProvisioning();
915
916 boolean isEnabled();
917 }
918
919 /**
920 * An action that also supports long press.
921 */
922 private interface LongPressAction extends Action {
923 boolean onLongPress();
924 }
925
926 /**
927 * A single press action maintains no state, just responds to a press
928 * and takes an action.
929 */
930 private static abstract class SinglePressAction implements Action {
931 private final int mIconResId;
932 private final Drawable mIcon;
933 private final int mMessageResId;
934 private final CharSequence mMessage;
935
936 protected SinglePressAction(int iconResId, int messageResId) {
937 mIconResId = iconResId;
938 mMessageResId = messageResId;
939 mMessage = null;
940 mIcon = null;
941 }
942
943 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
944 mIconResId = iconResId;
945 mMessageResId = 0;
946 mMessage = message;
947 mIcon = icon;
948 }
949
950 public boolean isEnabled() {
951 return true;
952 }
953
954 public String getStatus() {
955 return null;
956 }
957
958 abstract public void onPress();
959
960 public CharSequence getLabelForAccessibility(Context context) {
961 if (mMessage != null) {
962 return mMessage;
963 } else {
964 return context.getString(mMessageResId);
965 }
966 }
967
968 public View create(
969 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400970 View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent,
971 false);
Jason Monk361915c2017-03-21 20:33:59 -0400972
973 ImageView icon = (ImageView) v.findViewById(R.id.icon);
974 TextView messageView = (TextView) v.findViewById(R.id.message);
975
976 TextView statusView = (TextView) v.findViewById(R.id.status);
977 final String status = getStatus();
978 if (!TextUtils.isEmpty(status)) {
979 statusView.setText(status);
980 } else {
981 statusView.setVisibility(View.GONE);
982 }
983 if (mIcon != null) {
984 icon.setImageDrawable(mIcon);
985 icon.setScaleType(ScaleType.CENTER_CROP);
986 } else if (mIconResId != 0) {
987 icon.setImageDrawable(context.getDrawable(mIconResId));
988 }
989 if (mMessage != null) {
990 messageView.setText(mMessage);
991 } else {
992 messageView.setText(mMessageResId);
993 }
994
995 return v;
996 }
997 }
998
999 /**
1000 * A toggle action knows whether it is on or off, and displays an icon
1001 * and status message accordingly.
1002 */
1003 private static abstract class ToggleAction implements Action {
1004
1005 enum State {
1006 Off(false),
1007 TurningOn(true),
1008 TurningOff(true),
1009 On(false);
1010
1011 private final boolean inTransition;
1012
1013 State(boolean intermediate) {
1014 inTransition = intermediate;
1015 }
1016
1017 public boolean inTransition() {
1018 return inTransition;
1019 }
1020 }
1021
1022 protected State mState = State.Off;
1023
1024 // prefs
1025 protected int mEnabledIconResId;
1026 protected int mDisabledIconResid;
1027 protected int mMessageResId;
1028 protected int mEnabledStatusMessageResId;
1029 protected int mDisabledStatusMessageResId;
1030
1031 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -04001032 * @param enabledIconResId The icon for when this action is on.
1033 * @param disabledIconResid The icon for when this action is off.
1034 * @param message The general information message, e.g 'Silent Mode'
1035 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -04001036 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
1037 */
1038 public ToggleAction(int enabledIconResId,
1039 int disabledIconResid,
1040 int message,
1041 int enabledStatusMessageResId,
1042 int disabledStatusMessageResId) {
1043 mEnabledIconResId = enabledIconResId;
1044 mDisabledIconResid = disabledIconResid;
1045 mMessageResId = message;
1046 mEnabledStatusMessageResId = enabledStatusMessageResId;
1047 mDisabledStatusMessageResId = disabledStatusMessageResId;
1048 }
1049
1050 /**
1051 * Override to make changes to resource IDs just before creating the
1052 * View.
1053 */
1054 void willCreate() {
1055
1056 }
1057
1058 @Override
1059 public CharSequence getLabelForAccessibility(Context context) {
1060 return context.getString(mMessageResId);
1061 }
1062
1063 public View create(Context context, View convertView, ViewGroup parent,
1064 LayoutInflater inflater) {
1065 willCreate();
1066
1067 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -04001068 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -04001069
1070 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1071 TextView messageView = (TextView) v.findViewById(R.id.message);
1072 TextView statusView = (TextView) v.findViewById(R.id.status);
1073 final boolean enabled = isEnabled();
1074
1075 if (messageView != null) {
1076 messageView.setText(mMessageResId);
1077 messageView.setEnabled(enabled);
1078 }
1079
1080 boolean on = ((mState == State.On) || (mState == State.TurningOn));
1081 if (icon != null) {
1082 icon.setImageDrawable(context.getDrawable(
1083 (on ? mEnabledIconResId : mDisabledIconResid)));
1084 icon.setEnabled(enabled);
1085 }
1086
1087 if (statusView != null) {
1088 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
1089 statusView.setVisibility(View.VISIBLE);
1090 statusView.setEnabled(enabled);
1091 }
1092 v.setEnabled(enabled);
1093
1094 return v;
1095 }
1096
1097 public final void onPress() {
1098 if (mState.inTransition()) {
1099 Log.w(TAG, "shouldn't be able to toggle when in transition");
1100 return;
1101 }
1102
1103 final boolean nowOn = !(mState == State.On);
1104 onToggle(nowOn);
1105 changeStateFromPress(nowOn);
1106 }
1107
1108 public boolean isEnabled() {
1109 return !mState.inTransition();
1110 }
1111
1112 /**
1113 * Implementations may override this if their state can be in on of the intermediate
1114 * states until some notification is received (e.g airplane mode is 'turning off' until
1115 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001116 *
Jason Monk361915c2017-03-21 20:33:59 -04001117 * @param buttonOn Whether the button was turned on or off
1118 */
1119 protected void changeStateFromPress(boolean buttonOn) {
1120 mState = buttonOn ? State.On : State.Off;
1121 }
1122
1123 abstract void onToggle(boolean on);
1124
1125 public void updateState(State state) {
1126 mState = state;
1127 }
1128 }
1129
1130 private class SilentModeToggleAction extends ToggleAction {
1131 public SilentModeToggleAction() {
1132 super(R.drawable.ic_audio_vol_mute,
1133 R.drawable.ic_audio_vol,
1134 R.string.global_action_toggle_silent_mode,
1135 R.string.global_action_silent_mode_on_status,
1136 R.string.global_action_silent_mode_off_status);
1137 }
1138
1139 void onToggle(boolean on) {
1140 if (on) {
1141 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1142 } else {
1143 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1144 }
1145 }
1146
1147 public boolean showDuringKeyguard() {
1148 return true;
1149 }
1150
1151 public boolean showBeforeProvisioning() {
1152 return false;
1153 }
1154 }
1155
1156 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1157
Jason Monk16fbd9d2017-04-27 14:28:49 -04001158 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001159
1160 private final AudioManager mAudioManager;
1161 private final Handler mHandler;
1162 private final Context mContext;
1163
1164 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
1165 mAudioManager = audioManager;
1166 mHandler = handler;
1167 mContext = context;
1168 }
1169
1170 private int ringerModeToIndex(int ringerMode) {
1171 // They just happen to coincide
1172 return ringerMode;
1173 }
1174
1175 private int indexToRingerMode(int index) {
1176 // They just happen to coincide
1177 return index;
1178 }
1179
1180 @Override
1181 public CharSequence getLabelForAccessibility(Context context) {
1182 return null;
1183 }
1184
1185 public View create(Context context, View convertView, ViewGroup parent,
1186 LayoutInflater inflater) {
1187 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1188
1189 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1190 for (int i = 0; i < 3; i++) {
1191 View itemView = v.findViewById(ITEM_IDS[i]);
1192 itemView.setSelected(selectedIndex == i);
1193 // Set up click handler
1194 itemView.setTag(i);
1195 itemView.setOnClickListener(this);
1196 }
1197 return v;
1198 }
1199
1200 public void onPress() {
1201 }
1202
1203 public boolean showDuringKeyguard() {
1204 return true;
1205 }
1206
1207 public boolean showBeforeProvisioning() {
1208 return false;
1209 }
1210
1211 public boolean isEnabled() {
1212 return true;
1213 }
1214
1215 void willCreate() {
1216 }
1217
1218 public void onClick(View v) {
1219 if (!(v.getTag() instanceof Integer)) return;
1220
1221 int index = (Integer) v.getTag();
1222 mAudioManager.setRingerMode(indexToRingerMode(index));
1223 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1224 }
1225 }
1226
1227 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1228 public void onReceive(Context context, Intent intent) {
1229 String action = intent.getAction();
1230 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1231 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1232 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1233 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
1234 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
1235 }
1236 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1237 // Airplane mode can be changed after ECM exits if airplane toggle button
1238 // is pressed during ECM mode
1239 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1240 mIsWaitingForEcmExit) {
1241 mIsWaitingForEcmExit = false;
1242 changeAirplaneModeSystemSetting(true);
1243 }
1244 }
1245 }
1246 };
1247
1248 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1249 @Override
1250 public void onServiceStateChanged(ServiceState serviceState) {
1251 if (!mHasTelephony) return;
1252 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1253 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1254 mAirplaneModeOn.updateState(mAirplaneState);
1255 mAdapter.notifyDataSetChanged();
1256 }
1257 };
1258
1259 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1260 @Override
1261 public void onReceive(Context context, Intent intent) {
1262 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1263 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1264 }
1265 }
1266 };
1267
1268 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1269 @Override
1270 public void onChange(boolean selfChange) {
1271 onAirplaneModeChanged();
1272 }
1273 };
1274
1275 private static final int MESSAGE_DISMISS = 0;
1276 private static final int MESSAGE_REFRESH = 1;
1277 private static final int MESSAGE_SHOW = 2;
1278 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1279
1280 private Handler mHandler = new Handler() {
1281 public void handleMessage(Message msg) {
1282 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001283 case MESSAGE_DISMISS:
1284 if (mDialog != null) {
1285 mDialog.dismiss();
1286 mDialog = null;
1287 }
1288 break;
1289 case MESSAGE_REFRESH:
1290 refreshSilentMode();
1291 mAdapter.notifyDataSetChanged();
1292 break;
1293 case MESSAGE_SHOW:
1294 handleShow();
1295 break;
Jason Monk361915c2017-03-21 20:33:59 -04001296 }
1297 }
1298 };
1299
1300 private void onAirplaneModeChanged() {
1301 // Let the service state callbacks handle the state.
1302 if (mHasTelephony) return;
1303
1304 boolean airplaneModeOn = Settings.Global.getInt(
1305 mContext.getContentResolver(),
1306 Settings.Global.AIRPLANE_MODE_ON,
1307 0) == 1;
1308 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1309 mAirplaneModeOn.updateState(mAirplaneState);
1310 }
1311
1312 /**
1313 * Change the airplane mode system setting
1314 */
1315 private void changeAirplaneModeSystemSetting(boolean on) {
1316 Settings.Global.putInt(
1317 mContext.getContentResolver(),
1318 Settings.Global.AIRPLANE_MODE_ON,
1319 on ? 1 : 0);
1320 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1321 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1322 intent.putExtra("state", on);
1323 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1324 if (!mHasTelephony) {
1325 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1326 }
1327 }
1328
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001329 private static final class ActionsDialog extends Dialog implements DialogInterface,
1330 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001331
Jason Monk16fbd9d2017-04-27 14:28:49 -04001332 private final Context mContext;
1333 private final MyAdapter mAdapter;
1334 private final LinearLayout mListView;
1335 private final HardwareUiLayout mHardwareLayout;
1336 private final OnClickListener mClickListener;
1337 private final OnItemLongClickListener mLongClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001338 private final GradientDrawable mGradientDrawable;
1339 private final ColorExtractor mColorExtractor;
1340 private boolean mKeyguardShowing;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001341
1342 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
1343 OnItemLongClickListener longClickListener) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001344 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Lucas Dupin448786c2017-07-24 17:44:25 -07001345 mContext = context;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001346 mAdapter = adapter;
1347 mClickListener = clickListener;
1348 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001349 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001350 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001351
1352 // Window initialization
1353 Window window = getWindow();
1354 window.requestFeature(Window.FEATURE_NO_TITLE);
1355 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
1356 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
Alison Cichowlas4f19f4a2017-07-25 10:56:16 -04001357 window.addFlags(
1358 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001359 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1360 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1361 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1362 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1363 window.setBackgroundDrawable(mGradientDrawable);
1364 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1365
Jason Monk16fbd9d2017-04-27 14:28:49 -04001366 setContentView(com.android.systemui.R.layout.global_actions_wrapped);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001367 mListView = findViewById(android.R.id.list);
1368 mHardwareLayout = HardwareUiLayout.get(mListView);
1369 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
Jason Monk361915c2017-03-21 20:33:59 -04001370 }
1371
Jason Monk16fbd9d2017-04-27 14:28:49 -04001372 private void updateList() {
1373 mListView.removeAllViews();
1374 for (int i = 0; i < mAdapter.getCount(); i++) {
1375 View v = mAdapter.getView(i, null, mListView);
1376 final int pos = i;
1377 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1378 v.setOnLongClickListener(view ->
1379 mLongClickListener.onItemLongClick(null, v, pos, 0));
1380 mListView.addView(v);
1381 }
Jason Monk361915c2017-03-21 20:33:59 -04001382 }
1383
1384 @Override
1385 protected void onStart() {
1386 super.setCanceledOnTouchOutside(true);
1387 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001388 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001389
1390 Point displaySize = new Point();
1391 mContext.getDisplay().getRealSize(displaySize);
1392 mColorExtractor.addOnColorsChangedListener(this);
1393 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1394 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1395 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001396 updateColors(colors, false /* animate */);
1397 }
1398
1399 /**
1400 * Updates background and system bars according to current GradientColors.
1401 * @param colors Colors and hints to use.
1402 * @param animate Interpolates gradient if true, just sets otherwise.
1403 */
1404 private void updateColors(GradientColors colors, boolean animate) {
1405 mGradientDrawable.setColors(colors, animate);
1406 View decorView = getWindow().getDecorView();
1407 if (colors.supportsDarkText()) {
1408 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
1409 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
1410 } else {
1411 decorView.setSystemUiVisibility(0);
1412 }
Jason Monk361915c2017-03-21 20:33:59 -04001413 }
1414
1415 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001416 protected void onStop() {
1417 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001418 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001419 }
1420
1421 @Override
1422 public void show() {
1423 super.show();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001424 mGradientDrawable.setAlpha(0);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001425 mHardwareLayout.setTranslationX(getAnimTranslation());
1426 mHardwareLayout.setAlpha(0);
1427 mHardwareLayout.animate()
1428 .alpha(1)
1429 .translationX(0)
1430 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001431 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001432 .setUpdateListener(animation -> {
1433 int alpha = (int) ((Float) animation.getAnimatedValue()
1434 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1435 mGradientDrawable.setAlpha(alpha);
1436 })
Jason Monka7af3b62017-07-07 11:35:13 -04001437 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
Jason Monk16fbd9d2017-04-27 14:28:49 -04001438 .start();
1439 }
1440
1441 @Override
1442 public void dismiss() {
1443 mHardwareLayout.setTranslationX(0);
1444 mHardwareLayout.setAlpha(1);
1445 mHardwareLayout.animate()
1446 .alpha(0)
1447 .translationX(getAnimTranslation())
1448 .setDuration(300)
1449 .withEndAction(() -> super.dismiss())
1450 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001451 .setUpdateListener(animation -> {
1452 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1453 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1454 mGradientDrawable.setAlpha(alpha);
1455 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001456 .start();
1457 }
1458
1459 private float getAnimTranslation() {
1460 return getContext().getResources().getDimension(
1461 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001462 }
1463
1464 @Override
1465 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1466 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1467 for (int i = 0; i < mAdapter.getCount(); ++i) {
1468 CharSequence label =
1469 mAdapter.getItem(i).getLabelForAccessibility(getContext());
1470 if (label != null) {
1471 event.getText().add(label);
1472 }
1473 }
1474 }
1475 return super.dispatchPopulateAccessibilityEvent(event);
1476 }
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001477
1478 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001479 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001480 if (mKeyguardShowing) {
1481 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001482 updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
1483 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001484 }
1485 } else {
1486 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001487 updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
1488 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001489 }
1490 }
1491 }
1492
1493 public void setKeyguardShowing(boolean keyguardShowing) {
1494 mKeyguardShowing = keyguardShowing;
1495 }
Jason Monk361915c2017-03-21 20:33:59 -04001496 }
1497}