blob: 3b54e11447b713a36af411d56c8cd706cbdbaf0b [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 Brubaker4f28f0d2017-09-07 14:28:13 -070017import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
18
Jason Monk361915c2017-03-21 20:33:59 -040019import android.app.ActivityManager;
20import android.app.Dialog;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070021import android.app.WallpaperManager;
Alex Chau04458852017-11-27 18:21:23 +000022import android.app.admin.DevicePolicyManager;
Jason Monk361915c2017-03-21 20:33:59 -040023import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.UserInfo;
29import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070030import android.graphics.Point;
Jason Monk361915c2017-03-21 20:33:59 -040031import android.graphics.drawable.Drawable;
32import android.media.AudioManager;
33import android.net.ConnectivityManager;
34import android.os.Build;
Jason Monk361915c2017-03-21 20:33:59 -040035import android.os.Handler;
36import android.os.Message;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemProperties;
40import android.os.UserHandle;
41import android.os.UserManager;
42import android.os.Vibrator;
43import android.provider.Settings;
44import android.service.dreams.DreamService;
45import android.service.dreams.IDreamManager;
46import android.telephony.PhoneStateListener;
47import android.telephony.ServiceState;
48import android.telephony.TelephonyManager;
49import android.text.TextUtils;
50import android.util.ArraySet;
51import android.util.Log;
Lucas Dupin448786c2017-07-24 17:44:25 -070052import android.view.ContextThemeWrapper;
Jason Monk361915c2017-03-21 20:33:59 -040053import android.view.LayoutInflater;
54import android.view.View;
55import android.view.ViewGroup;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070056import android.view.Window;
Jason Monk361915c2017-03-21 20:33:59 -040057import android.view.WindowManager;
58import android.view.WindowManagerGlobal;
59import android.view.accessibility.AccessibilityEvent;
60import android.widget.AdapterView;
Jason Monk16fbd9d2017-04-27 14:28:49 -040061import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040062import android.widget.BaseAdapter;
63import android.widget.ImageView;
64import android.widget.ImageView.ScaleType;
Jason Monk16fbd9d2017-04-27 14:28:49 -040065import android.widget.LinearLayout;
Jason Monk361915c2017-03-21 20:33:59 -040066import android.widget.TextView;
67
Charles He9851a8d2017-10-10 17:31:30 +010068import com.android.internal.R;
69import com.android.internal.colorextraction.ColorExtractor;
70import com.android.internal.colorextraction.ColorExtractor.GradientColors;
Lucas Dupine2292a92017-07-06 14:35:30 -070071import com.android.internal.colorextraction.drawable.GradientDrawable;
Charles He9851a8d2017-10-10 17:31:30 +010072import com.android.internal.logging.MetricsLogger;
73import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
74import com.android.internal.telephony.TelephonyIntents;
75import com.android.internal.telephony.TelephonyProperties;
76import com.android.internal.util.EmergencyAffordanceManager;
77import com.android.internal.widget.LockPatternUtils;
78import com.android.systemui.Dependency;
79import com.android.systemui.HardwareUiLayout;
80import com.android.systemui.Interpolators;
81import com.android.systemui.colorextraction.SysuiColorExtractor;
82import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
83import com.android.systemui.statusbar.phone.ScrimController;
Julia Reynolds42411922017-11-08 11:19:09 -050084import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070085
Jason Monk361915c2017-03-21 20:33:59 -040086import java.util.ArrayList;
87import java.util.List;
88
89/**
90 * Helper to show the global actions dialog. Each item is an {@link Action} that
91 * may show depending on whether the keyguard is showing, and whether the device
92 * is provisioned.
93 */
Charles He9851a8d2017-10-10 17:31:30 +010094class GlobalActionsDialog implements DialogInterface.OnDismissListener,
95 DialogInterface.OnClickListener {
Jason Monk361915c2017-03-21 20:33:59 -040096
97 static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
98 static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
99
100 private static final String TAG = "GlobalActionsDialog";
101
102 private static final boolean SHOW_SILENT_TOGGLE = true;
103
104 /* Valid settings for global actions keys.
105 * see config.xml config_globalActionList */
106 private static final String GLOBAL_ACTION_KEY_POWER = "power";
107 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
108 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
109 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
110 private static final String GLOBAL_ACTION_KEY_USERS = "users";
111 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
112 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
113 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
114 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
115 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
Alex Chau04458852017-11-27 18:21:23 +0000116 private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
Jason Monk361915c2017-03-21 20:33:59 -0400117
118 private final Context mContext;
119 private final GlobalActionsManager mWindowManagerFuncs;
120 private final AudioManager mAudioManager;
121 private final IDreamManager mDreamManager;
Alex Chau04458852017-11-27 18:21:23 +0000122 private final DevicePolicyManager mDevicePolicyManager;
Jason Monk361915c2017-03-21 20:33:59 -0400123
124 private ArrayList<Action> mItems;
125 private ActionsDialog mDialog;
126
127 private Action mSilentModeAction;
128 private ToggleAction mAirplaneModeOn;
129
130 private MyAdapter mAdapter;
131
132 private boolean mKeyguardShowing = false;
133 private boolean mDeviceProvisioned = false;
134 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
135 private boolean mIsWaitingForEcmExit = false;
136 private boolean mHasTelephony;
137 private boolean mHasVibrator;
Alex Chau04458852017-11-27 18:21:23 +0000138 private boolean mHasLogoutButton;
Jason Monk361915c2017-03-21 20:33:59 -0400139 private final boolean mShowSilentToggle;
140 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
141
142 /**
143 * @param context everything needs a context :(
144 */
145 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
Lucas Dupin448786c2017-07-24 17:44:25 -0700146 mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
Jason Monk361915c2017-03-21 20:33:59 -0400147 mWindowManagerFuncs = windowManagerFuncs;
148 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
149 mDreamManager = IDreamManager.Stub.asInterface(
150 ServiceManager.getService(DreamService.DREAM_SERVICE));
Alex Chau04458852017-11-27 18:21:23 +0000151 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
152 Context.DEVICE_POLICY_SERVICE);
Jason Monk361915c2017-03-21 20:33:59 -0400153
154 // receive broadcasts
155 IntentFilter filter = new IntentFilter();
156 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
157 filter.addAction(Intent.ACTION_SCREEN_OFF);
158 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
159 context.registerReceiver(mBroadcastReceiver, filter);
160
161 ConnectivityManager cm = (ConnectivityManager)
162 context.getSystemService(Context.CONNECTIVITY_SERVICE);
163 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
164
165 // get notified of phone state changes
166 TelephonyManager telephonyManager =
167 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
168 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
169 mContext.getContentResolver().registerContentObserver(
170 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
171 mAirplaneModeObserver);
172 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
173 mHasVibrator = vibrator != null && vibrator.hasVibrator();
174
175 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
176 R.bool.config_useFixedVolume);
177
178 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
179 }
180
181 /**
182 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400183 *
Jason Monk361915c2017-03-21 20:33:59 -0400184 * @param keyguardShowing True if keyguard is showing
185 */
186 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
187 mKeyguardShowing = keyguardShowing;
188 mDeviceProvisioned = isDeviceProvisioned;
189 if (mDialog != null) {
190 mDialog.dismiss();
191 mDialog = null;
192 // Show delayed, so that the dismiss of the previous dialog completes
193 mHandler.sendEmptyMessage(MESSAGE_SHOW);
194 } else {
195 handleShow();
196 }
197 }
198
Charles He9851a8d2017-10-10 17:31:30 +0100199 /**
200 * Dismiss the global actions dialog, if it's currently shown
201 */
202 public void dismissDialog() {
203 mHandler.removeMessages(MESSAGE_DISMISS);
204 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
205 }
206
Jason Monk361915c2017-03-21 20:33:59 -0400207 private void awakenIfNecessary() {
208 if (mDreamManager != null) {
209 try {
210 if (mDreamManager.isDreaming()) {
211 mDreamManager.awaken();
212 }
213 } catch (RemoteException e) {
214 // we tried
215 }
216 }
217 }
218
219 private void handleShow() {
220 awakenIfNecessary();
221 mDialog = createDialog();
222 prepareDialog();
223
224 // If we only have 1 item and it's a simple press action, just do this action.
225 if (mAdapter.getCount() == 1
226 && mAdapter.getItem(0) instanceof SinglePressAction
227 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
228 ((SinglePressAction) mAdapter.getItem(0)).onPress();
229 } else {
230 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
231 attrs.setTitle("ActionsDialog");
232 mDialog.getWindow().setAttributes(attrs);
233 mDialog.show();
234 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400235 }
236 }
237
238 /**
239 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400240 *
Jason Monk361915c2017-03-21 20:33:59 -0400241 * @return A new dialog.
242 */
243 private ActionsDialog createDialog() {
244 // Simple toggle style if there's no vibrator, otherwise use a tri-state
245 if (!mHasVibrator) {
246 mSilentModeAction = new SilentModeToggleAction();
247 } else {
248 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
249 }
250 mAirplaneModeOn = new ToggleAction(
251 R.drawable.ic_lock_airplane_mode,
252 R.drawable.ic_lock_airplane_mode_off,
253 R.string.global_actions_toggle_airplane_mode,
254 R.string.global_actions_airplane_mode_on_status,
255 R.string.global_actions_airplane_mode_off_status) {
256
257 void onToggle(boolean on) {
258 if (mHasTelephony && Boolean.parseBoolean(
259 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
260 mIsWaitingForEcmExit = true;
261 // Launch ECM exit dialog
262 Intent ecmDialogIntent =
263 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
264 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
265 mContext.startActivity(ecmDialogIntent);
266 } else {
267 changeAirplaneModeSystemSetting(on);
268 }
269 }
270
271 @Override
272 protected void changeStateFromPress(boolean buttonOn) {
273 if (!mHasTelephony) return;
274
275 // In ECM mode airplane state cannot be changed
276 if (!(Boolean.parseBoolean(
277 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
278 mState = buttonOn ? State.TurningOn : State.TurningOff;
279 mAirplaneState = mState;
280 }
281 }
282
283 public boolean showDuringKeyguard() {
284 return true;
285 }
286
287 public boolean showBeforeProvisioning() {
288 return false;
289 }
290 };
291 onAirplaneModeChanged();
292
293 mItems = new ArrayList<Action>();
294 String[] defaultActions = mContext.getResources().getStringArray(
295 R.array.config_globalActionsList);
296
297 ArraySet<String> addedKeys = new ArraySet<String>();
Alex Chau04458852017-11-27 18:21:23 +0000298 mHasLogoutButton = false;
Jason Monk361915c2017-03-21 20:33:59 -0400299 for (int i = 0; i < defaultActions.length; i++) {
300 String actionKey = defaultActions[i];
301 if (addedKeys.contains(actionKey)) {
302 // If we already have added this, don't add it again.
303 continue;
304 }
305 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
306 mItems.add(new PowerAction());
307 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
308 mItems.add(mAirplaneModeOn);
309 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
310 if (Settings.Global.getInt(mContext.getContentResolver(),
311 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
312 mItems.add(new BugReportAction());
313 }
314 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
315 if (mShowSilentToggle) {
316 mItems.add(mSilentModeAction);
317 }
318 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
319 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
320 addUsersToMenu(mItems);
321 }
322 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
323 mItems.add(getSettingsAction());
324 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700325 if (Settings.Secure.getInt(mContext.getContentResolver(),
326 Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0) {
327 mItems.add(getLockdownAction());
328 }
Jason Monk361915c2017-03-21 20:33:59 -0400329 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
330 mItems.add(getVoiceAssistAction());
331 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
332 mItems.add(getAssistAction());
333 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
334 mItems.add(new RestartAction());
Alex Chau04458852017-11-27 18:21:23 +0000335 } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
336 if (mDevicePolicyManager.isLogoutButtonEnabled()
337 && getCurrentUser().id != UserHandle.USER_SYSTEM) {
338 mItems.add(new LogoutAction());
339 mHasLogoutButton = true;
340 }
Jason Monk361915c2017-03-21 20:33:59 -0400341 } else {
342 Log.e(TAG, "Invalid global action key " + actionKey);
343 }
344 // Add here so we don't add more than one.
345 addedKeys.add(actionKey);
346 }
347
348 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
349 mItems.add(getEmergencyAction());
350 }
351
352 mAdapter = new MyAdapter();
353
Jason Monk16fbd9d2017-04-27 14:28:49 -0400354 OnItemLongClickListener onItemLongClickListener = new OnItemLongClickListener() {
355 @Override
356 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
357 long id) {
358 final Action action = mAdapter.getItem(position);
359 if (action instanceof LongPressAction) {
Shunta Satobdb0e492017-04-14 16:01:54 +0900360 mDialog.dismiss();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400361 return ((LongPressAction) action).onLongPress();
362 }
363 return false;
364 }
365 };
366 ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener);
Jason Monk361915c2017-03-21 20:33:59 -0400367 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700368 dialog.setKeyguardShowing(mKeyguardShowing);
Jason Monk361915c2017-03-21 20:33:59 -0400369
370 dialog.setOnDismissListener(this);
371
372 return dialog;
373 }
374
375 private final class PowerAction extends SinglePressAction implements LongPressAction {
376 private PowerAction() {
377 super(R.drawable.ic_lock_power_off,
Jason Monk16fbd9d2017-04-27 14:28:49 -0400378 R.string.global_action_power_off);
Jason Monk361915c2017-03-21 20:33:59 -0400379 }
380
381 @Override
382 public boolean onLongPress() {
383 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
384 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
385 mWindowManagerFuncs.reboot(true);
386 return true;
387 }
388 return false;
389 }
390
391 @Override
392 public boolean showDuringKeyguard() {
393 return true;
394 }
395
396 @Override
397 public boolean showBeforeProvisioning() {
398 return true;
399 }
400
401 @Override
402 public void onPress() {
403 // shutdown by making sure radio and power are handled accordingly.
404 mWindowManagerFuncs.shutdown();
405 }
406 }
407
408 private final class RestartAction extends SinglePressAction implements LongPressAction {
409 private RestartAction() {
410 super(R.drawable.ic_restart, R.string.global_action_restart);
411 }
412
413 @Override
414 public boolean onLongPress() {
415 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
416 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
417 mWindowManagerFuncs.reboot(true);
418 return true;
419 }
420 return false;
421 }
422
423 @Override
424 public boolean showDuringKeyguard() {
425 return true;
426 }
427
428 @Override
429 public boolean showBeforeProvisioning() {
430 return true;
431 }
432
433 @Override
434 public void onPress() {
435 mWindowManagerFuncs.reboot(false);
436 }
437 }
438
439
440 private class BugReportAction extends SinglePressAction implements LongPressAction {
441
442 public BugReportAction() {
443 super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
444 }
445
446 @Override
447 public void onPress() {
448 // don't actually trigger the bugreport if we are running stability
449 // tests via monkey
450 if (ActivityManager.isUserAMonkey()) {
451 return;
452 }
453 // Add a little delay before executing, to give the
454 // dialog a chance to go away before it takes a
455 // screenshot.
456 mHandler.postDelayed(new Runnable() {
457 @Override
458 public void run() {
459 try {
460 // Take an "interactive" bugreport.
461 MetricsLogger.action(mContext,
462 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
463 ActivityManager.getService().requestBugReport(
464 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
465 } catch (RemoteException e) {
466 }
467 }
468 }, 500);
469 }
470
471 @Override
472 public boolean onLongPress() {
473 // don't actually trigger the bugreport if we are running stability
474 // tests via monkey
475 if (ActivityManager.isUserAMonkey()) {
476 return false;
477 }
478 try {
479 // Take a "full" bugreport.
480 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
481 ActivityManager.getService().requestBugReport(
482 ActivityManager.BUGREPORT_OPTION_FULL);
483 } catch (RemoteException e) {
484 }
485 return false;
486 }
487
488 public boolean showDuringKeyguard() {
489 return true;
490 }
491
492 @Override
493 public boolean showBeforeProvisioning() {
494 return false;
495 }
496
497 @Override
498 public String getStatus() {
499 return mContext.getString(
500 R.string.bugreport_status,
501 Build.VERSION.RELEASE,
502 Build.ID);
503 }
504 }
505
Alex Chau04458852017-11-27 18:21:23 +0000506 private final class LogoutAction extends SinglePressAction {
507 private LogoutAction() {
508 super(R.drawable.ic_logout, R.string.global_action_logout);
509 }
510
511 @Override
512 public boolean showDuringKeyguard() {
513 return true;
514 }
515
516 @Override
517 public boolean showBeforeProvisioning() {
518 return false;
519 }
520
521 @Override
522 public void onPress() {
523 // Add a little delay before executing, to give the dialog a chance to go away before
524 // switching user
525 mHandler.postDelayed(() -> {
526 try {
527 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
528 ActivityManager.getService().stopUser(getCurrentUser().id, true /*force*/,
529 null);
530 } catch (RemoteException re) {
531 Log.e(TAG, "Couldn't logout user " + re);
532 }
533 }, 500);
534 }
535 }
536
Jason Monk361915c2017-03-21 20:33:59 -0400537 private Action getSettingsAction() {
538 return new SinglePressAction(R.drawable.ic_settings,
539 R.string.global_action_settings) {
540
541 @Override
542 public void onPress() {
543 Intent intent = new Intent(Settings.ACTION_SETTINGS);
544 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
545 mContext.startActivity(intent);
546 }
547
548 @Override
549 public boolean showDuringKeyguard() {
550 return true;
551 }
552
553 @Override
554 public boolean showBeforeProvisioning() {
555 return true;
556 }
557 };
558 }
559
560 private Action getEmergencyAction() {
561 return new SinglePressAction(R.drawable.emergency_icon,
562 R.string.global_action_emergency) {
563 @Override
564 public void onPress() {
565 mEmergencyAffordanceManager.performEmergencyCall();
566 }
567
568 @Override
569 public boolean showDuringKeyguard() {
570 return true;
571 }
572
573 @Override
574 public boolean showBeforeProvisioning() {
575 return true;
576 }
577 };
578 }
579
580 private Action getAssistAction() {
581 return new SinglePressAction(R.drawable.ic_action_assist_focused,
582 R.string.global_action_assist) {
583 @Override
584 public void onPress() {
585 Intent intent = new Intent(Intent.ACTION_ASSIST);
586 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
587 mContext.startActivity(intent);
588 }
589
590 @Override
591 public boolean showDuringKeyguard() {
592 return true;
593 }
594
595 @Override
596 public boolean showBeforeProvisioning() {
597 return true;
598 }
599 };
600 }
601
602 private Action getVoiceAssistAction() {
603 return new SinglePressAction(R.drawable.ic_voice_search,
604 R.string.global_action_voice_assist) {
605 @Override
606 public void onPress() {
607 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
608 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
609 mContext.startActivity(intent);
610 }
611
612 @Override
613 public boolean showDuringKeyguard() {
614 return true;
615 }
616
617 @Override
618 public boolean showBeforeProvisioning() {
619 return true;
620 }
621 };
622 }
623
624 private Action getLockdownAction() {
625 return new SinglePressAction(R.drawable.ic_lock_lock,
626 R.string.global_action_lockdown) {
627
628 @Override
629 public void onPress() {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700630 new LockPatternUtils(mContext)
631 .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
632 UserHandle.USER_ALL);
Jason Monk361915c2017-03-21 20:33:59 -0400633 try {
634 WindowManagerGlobal.getWindowManagerService().lockNow(null);
635 } catch (RemoteException e) {
636 Log.e(TAG, "Error while trying to lock device.", e);
637 }
638 }
639
640 @Override
641 public boolean showDuringKeyguard() {
642 return true;
643 }
644
645 @Override
646 public boolean showBeforeProvisioning() {
647 return false;
648 }
649 };
650 }
651
652 private UserInfo getCurrentUser() {
653 try {
654 return ActivityManager.getService().getCurrentUser();
655 } catch (RemoteException re) {
656 return null;
657 }
658 }
659
660 private boolean isCurrentUserOwner() {
661 UserInfo currentUser = getCurrentUser();
662 return currentUser == null || currentUser.isPrimary();
663 }
664
665 private void addUsersToMenu(ArrayList<Action> items) {
666 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
667 if (um.isUserSwitcherEnabled()) {
668 List<UserInfo> users = um.getUsers();
669 UserInfo currentUser = getCurrentUser();
670 for (final UserInfo user : users) {
671 if (user.supportsSwitchToByUser()) {
672 boolean isCurrentUser = currentUser == null
673 ? user.id == 0 : (currentUser.id == user.id);
674 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
675 : null;
676 SinglePressAction switchToUser = new SinglePressAction(
677 R.drawable.ic_menu_cc, icon,
678 (user.name != null ? user.name : "Primary")
Jason Monk16fbd9d2017-04-27 14:28:49 -0400679 + (isCurrentUser ? " \u2714" : "")) {
Jason Monk361915c2017-03-21 20:33:59 -0400680 public void onPress() {
681 try {
682 ActivityManager.getService().switchUser(user.id);
683 } catch (RemoteException re) {
684 Log.e(TAG, "Couldn't switch user " + re);
685 }
686 }
687
688 public boolean showDuringKeyguard() {
689 return true;
690 }
691
692 public boolean showBeforeProvisioning() {
693 return false;
694 }
695 };
696 items.add(switchToUser);
697 }
698 }
699 }
700 }
701
702 private void prepareDialog() {
703 refreshSilentMode();
704 mAirplaneModeOn.updateState(mAirplaneState);
705 mAdapter.notifyDataSetChanged();
Jason Monk361915c2017-03-21 20:33:59 -0400706 if (mShowSilentToggle) {
707 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
708 mContext.registerReceiver(mRingerModeReceiver, filter);
709 }
710 }
711
712 private void refreshSilentMode() {
713 if (!mHasVibrator) {
714 final boolean silentModeOn =
715 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400716 ((ToggleAction) mSilentModeAction).updateState(
Jason Monk361915c2017-03-21 20:33:59 -0400717 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
718 }
719 }
720
721 /** {@inheritDoc} */
722 public void onDismiss(DialogInterface dialog) {
723 mWindowManagerFuncs.onGlobalActionsHidden();
724 if (mShowSilentToggle) {
725 try {
726 mContext.unregisterReceiver(mRingerModeReceiver);
727 } catch (IllegalArgumentException ie) {
728 // ignore this
729 Log.w(TAG, ie);
730 }
731 }
732 }
733
734 /** {@inheritDoc} */
735 public void onClick(DialogInterface dialog, int which) {
Jason Monkfd279662017-06-29 19:37:48 -0400736 Action item = mAdapter.getItem(which);
Jason Monkb4302182017-08-04 13:39:17 -0400737 if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400738 dialog.dismiss();
739 }
Jason Monkfd279662017-06-29 19:37:48 -0400740 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400741 }
742
743 /**
744 * The adapter used for the list within the global actions dialog, taking
745 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400746 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
747 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400748 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
749 */
750 private class MyAdapter extends BaseAdapter {
751
752 public int getCount() {
753 int count = 0;
754
755 for (int i = 0; i < mItems.size(); i++) {
756 final Action action = mItems.get(i);
757
758 if (mKeyguardShowing && !action.showDuringKeyguard()) {
759 continue;
760 }
761 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
762 continue;
763 }
764 count++;
765 }
766 return count;
767 }
768
769 @Override
770 public boolean isEnabled(int position) {
771 return getItem(position).isEnabled();
772 }
773
774 @Override
775 public boolean areAllItemsEnabled() {
776 return false;
777 }
778
779 public Action getItem(int position) {
780
781 int filteredPos = 0;
782 for (int i = 0; i < mItems.size(); i++) {
783 final Action action = mItems.get(i);
784 if (mKeyguardShowing && !action.showDuringKeyguard()) {
785 continue;
786 }
787 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
788 continue;
789 }
790 if (filteredPos == position) {
791 return action;
792 }
793 filteredPos++;
794 }
795
796 throw new IllegalArgumentException("position " + position
797 + " out of range of showable actions"
798 + ", filtered count=" + getCount()
799 + ", keyguardshowing=" + mKeyguardShowing
800 + ", provisioned=" + mDeviceProvisioned);
801 }
802
803
804 public long getItemId(int position) {
805 return position;
806 }
807
808 public View getView(int position, View convertView, ViewGroup parent) {
809 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400810 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
Alex Chau04458852017-11-27 18:21:23 +0000811 // When there is no logout button, only power off and restart should be in white
812 // background, thus setting division view at third item; with logout button being the
813 // third item, set the division view at fourth item instead.
814 if (position == (mHasLogoutButton ? 3 : 2)) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400815 HardwareUiLayout.get(parent).setDivisionView(view);
816 }
817 return view;
Jason Monk361915c2017-03-21 20:33:59 -0400818 }
819 }
820
821 // note: the scheme below made more sense when we were planning on having
822 // 8 different things in the global actions dialog. seems overkill with
823 // only 3 items now, but may as well keep this flexible approach so it will
824 // be easy should someone decide at the last minute to include something
825 // else, such as 'enable wifi', or 'enable bluetooth'
826
827 /**
828 * What each item in the global actions dialog must be able to support.
829 */
830 private interface Action {
831 /**
832 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -0400833 * for none.
Jason Monk361915c2017-03-21 20:33:59 -0400834 */
835 CharSequence getLabelForAccessibility(Context context);
836
837 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
838
839 void onPress();
840
841 /**
842 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -0400843 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -0400844 */
845 boolean showDuringKeyguard();
846
847 /**
848 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -0400849 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -0400850 */
851 boolean showBeforeProvisioning();
852
853 boolean isEnabled();
854 }
855
856 /**
857 * An action that also supports long press.
858 */
859 private interface LongPressAction extends Action {
860 boolean onLongPress();
861 }
862
863 /**
864 * A single press action maintains no state, just responds to a press
865 * and takes an action.
866 */
867 private static abstract class SinglePressAction implements Action {
868 private final int mIconResId;
869 private final Drawable mIcon;
870 private final int mMessageResId;
871 private final CharSequence mMessage;
872
873 protected SinglePressAction(int iconResId, int messageResId) {
874 mIconResId = iconResId;
875 mMessageResId = messageResId;
876 mMessage = null;
877 mIcon = null;
878 }
879
880 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
881 mIconResId = iconResId;
882 mMessageResId = 0;
883 mMessage = message;
884 mIcon = icon;
885 }
886
887 public boolean isEnabled() {
888 return true;
889 }
890
891 public String getStatus() {
892 return null;
893 }
894
895 abstract public void onPress();
896
897 public CharSequence getLabelForAccessibility(Context context) {
898 if (mMessage != null) {
899 return mMessage;
900 } else {
901 return context.getString(mMessageResId);
902 }
903 }
904
905 public View create(
906 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400907 View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent,
908 false);
Jason Monk361915c2017-03-21 20:33:59 -0400909
910 ImageView icon = (ImageView) v.findViewById(R.id.icon);
911 TextView messageView = (TextView) v.findViewById(R.id.message);
912
913 TextView statusView = (TextView) v.findViewById(R.id.status);
914 final String status = getStatus();
915 if (!TextUtils.isEmpty(status)) {
916 statusView.setText(status);
917 } else {
918 statusView.setVisibility(View.GONE);
919 }
920 if (mIcon != null) {
921 icon.setImageDrawable(mIcon);
922 icon.setScaleType(ScaleType.CENTER_CROP);
923 } else if (mIconResId != 0) {
924 icon.setImageDrawable(context.getDrawable(mIconResId));
925 }
926 if (mMessage != null) {
927 messageView.setText(mMessage);
928 } else {
929 messageView.setText(mMessageResId);
930 }
931
932 return v;
933 }
934 }
935
936 /**
937 * A toggle action knows whether it is on or off, and displays an icon
938 * and status message accordingly.
939 */
940 private static abstract class ToggleAction implements Action {
941
942 enum State {
943 Off(false),
944 TurningOn(true),
945 TurningOff(true),
946 On(false);
947
948 private final boolean inTransition;
949
950 State(boolean intermediate) {
951 inTransition = intermediate;
952 }
953
954 public boolean inTransition() {
955 return inTransition;
956 }
957 }
958
959 protected State mState = State.Off;
960
961 // prefs
962 protected int mEnabledIconResId;
963 protected int mDisabledIconResid;
964 protected int mMessageResId;
965 protected int mEnabledStatusMessageResId;
966 protected int mDisabledStatusMessageResId;
967
968 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -0400969 * @param enabledIconResId The icon for when this action is on.
970 * @param disabledIconResid The icon for when this action is off.
971 * @param message The general information message, e.g 'Silent Mode'
972 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -0400973 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
974 */
975 public ToggleAction(int enabledIconResId,
976 int disabledIconResid,
977 int message,
978 int enabledStatusMessageResId,
979 int disabledStatusMessageResId) {
980 mEnabledIconResId = enabledIconResId;
981 mDisabledIconResid = disabledIconResid;
982 mMessageResId = message;
983 mEnabledStatusMessageResId = enabledStatusMessageResId;
984 mDisabledStatusMessageResId = disabledStatusMessageResId;
985 }
986
987 /**
988 * Override to make changes to resource IDs just before creating the
989 * View.
990 */
991 void willCreate() {
992
993 }
994
995 @Override
996 public CharSequence getLabelForAccessibility(Context context) {
997 return context.getString(mMessageResId);
998 }
999
1000 public View create(Context context, View convertView, ViewGroup parent,
1001 LayoutInflater inflater) {
1002 willCreate();
1003
1004 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -04001005 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -04001006
1007 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1008 TextView messageView = (TextView) v.findViewById(R.id.message);
1009 TextView statusView = (TextView) v.findViewById(R.id.status);
1010 final boolean enabled = isEnabled();
1011
1012 if (messageView != null) {
1013 messageView.setText(mMessageResId);
1014 messageView.setEnabled(enabled);
1015 }
1016
1017 boolean on = ((mState == State.On) || (mState == State.TurningOn));
1018 if (icon != null) {
1019 icon.setImageDrawable(context.getDrawable(
1020 (on ? mEnabledIconResId : mDisabledIconResid)));
1021 icon.setEnabled(enabled);
1022 }
1023
1024 if (statusView != null) {
1025 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
1026 statusView.setVisibility(View.VISIBLE);
1027 statusView.setEnabled(enabled);
1028 }
1029 v.setEnabled(enabled);
1030
1031 return v;
1032 }
1033
1034 public final void onPress() {
1035 if (mState.inTransition()) {
1036 Log.w(TAG, "shouldn't be able to toggle when in transition");
1037 return;
1038 }
1039
1040 final boolean nowOn = !(mState == State.On);
1041 onToggle(nowOn);
1042 changeStateFromPress(nowOn);
1043 }
1044
1045 public boolean isEnabled() {
1046 return !mState.inTransition();
1047 }
1048
1049 /**
1050 * Implementations may override this if their state can be in on of the intermediate
1051 * states until some notification is received (e.g airplane mode is 'turning off' until
1052 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001053 *
Jason Monk361915c2017-03-21 20:33:59 -04001054 * @param buttonOn Whether the button was turned on or off
1055 */
1056 protected void changeStateFromPress(boolean buttonOn) {
1057 mState = buttonOn ? State.On : State.Off;
1058 }
1059
1060 abstract void onToggle(boolean on);
1061
1062 public void updateState(State state) {
1063 mState = state;
1064 }
1065 }
1066
1067 private class SilentModeToggleAction extends ToggleAction {
1068 public SilentModeToggleAction() {
1069 super(R.drawable.ic_audio_vol_mute,
1070 R.drawable.ic_audio_vol,
1071 R.string.global_action_toggle_silent_mode,
1072 R.string.global_action_silent_mode_on_status,
1073 R.string.global_action_silent_mode_off_status);
1074 }
1075
1076 void onToggle(boolean on) {
1077 if (on) {
1078 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1079 } else {
1080 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1081 }
1082 }
1083
1084 public boolean showDuringKeyguard() {
1085 return true;
1086 }
1087
1088 public boolean showBeforeProvisioning() {
1089 return false;
1090 }
1091 }
1092
1093 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1094
Jason Monk16fbd9d2017-04-27 14:28:49 -04001095 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001096
1097 private final AudioManager mAudioManager;
1098 private final Handler mHandler;
1099 private final Context mContext;
1100
1101 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
1102 mAudioManager = audioManager;
1103 mHandler = handler;
1104 mContext = context;
1105 }
1106
1107 private int ringerModeToIndex(int ringerMode) {
1108 // They just happen to coincide
1109 return ringerMode;
1110 }
1111
1112 private int indexToRingerMode(int index) {
1113 // They just happen to coincide
1114 return index;
1115 }
1116
1117 @Override
1118 public CharSequence getLabelForAccessibility(Context context) {
1119 return null;
1120 }
1121
1122 public View create(Context context, View convertView, ViewGroup parent,
1123 LayoutInflater inflater) {
1124 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1125
1126 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1127 for (int i = 0; i < 3; i++) {
1128 View itemView = v.findViewById(ITEM_IDS[i]);
1129 itemView.setSelected(selectedIndex == i);
1130 // Set up click handler
1131 itemView.setTag(i);
1132 itemView.setOnClickListener(this);
1133 }
1134 return v;
1135 }
1136
1137 public void onPress() {
1138 }
1139
1140 public boolean showDuringKeyguard() {
1141 return true;
1142 }
1143
1144 public boolean showBeforeProvisioning() {
1145 return false;
1146 }
1147
1148 public boolean isEnabled() {
1149 return true;
1150 }
1151
1152 void willCreate() {
1153 }
1154
1155 public void onClick(View v) {
1156 if (!(v.getTag() instanceof Integer)) return;
1157
1158 int index = (Integer) v.getTag();
1159 mAudioManager.setRingerMode(indexToRingerMode(index));
1160 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1161 }
1162 }
1163
1164 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1165 public void onReceive(Context context, Intent intent) {
1166 String action = intent.getAction();
1167 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1168 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1169 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1170 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
1171 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
1172 }
1173 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1174 // Airplane mode can be changed after ECM exits if airplane toggle button
1175 // is pressed during ECM mode
1176 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1177 mIsWaitingForEcmExit) {
1178 mIsWaitingForEcmExit = false;
1179 changeAirplaneModeSystemSetting(true);
1180 }
1181 }
1182 }
1183 };
1184
1185 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1186 @Override
1187 public void onServiceStateChanged(ServiceState serviceState) {
1188 if (!mHasTelephony) return;
1189 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1190 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1191 mAirplaneModeOn.updateState(mAirplaneState);
1192 mAdapter.notifyDataSetChanged();
1193 }
1194 };
1195
1196 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1197 @Override
1198 public void onReceive(Context context, Intent intent) {
1199 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1200 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1201 }
1202 }
1203 };
1204
1205 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1206 @Override
1207 public void onChange(boolean selfChange) {
1208 onAirplaneModeChanged();
1209 }
1210 };
1211
1212 private static final int MESSAGE_DISMISS = 0;
1213 private static final int MESSAGE_REFRESH = 1;
1214 private static final int MESSAGE_SHOW = 2;
1215 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1216
1217 private Handler mHandler = new Handler() {
1218 public void handleMessage(Message msg) {
1219 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001220 case MESSAGE_DISMISS:
1221 if (mDialog != null) {
1222 mDialog.dismiss();
1223 mDialog = null;
1224 }
1225 break;
1226 case MESSAGE_REFRESH:
1227 refreshSilentMode();
1228 mAdapter.notifyDataSetChanged();
1229 break;
1230 case MESSAGE_SHOW:
1231 handleShow();
1232 break;
Jason Monk361915c2017-03-21 20:33:59 -04001233 }
1234 }
1235 };
1236
1237 private void onAirplaneModeChanged() {
1238 // Let the service state callbacks handle the state.
1239 if (mHasTelephony) return;
1240
1241 boolean airplaneModeOn = Settings.Global.getInt(
1242 mContext.getContentResolver(),
1243 Settings.Global.AIRPLANE_MODE_ON,
1244 0) == 1;
1245 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1246 mAirplaneModeOn.updateState(mAirplaneState);
1247 }
1248
1249 /**
1250 * Change the airplane mode system setting
1251 */
1252 private void changeAirplaneModeSystemSetting(boolean on) {
1253 Settings.Global.putInt(
1254 mContext.getContentResolver(),
1255 Settings.Global.AIRPLANE_MODE_ON,
1256 on ? 1 : 0);
1257 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1258 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1259 intent.putExtra("state", on);
1260 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1261 if (!mHasTelephony) {
1262 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1263 }
1264 }
1265
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001266 private static final class ActionsDialog extends Dialog implements DialogInterface,
1267 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001268
Jason Monk16fbd9d2017-04-27 14:28:49 -04001269 private final Context mContext;
1270 private final MyAdapter mAdapter;
1271 private final LinearLayout mListView;
1272 private final HardwareUiLayout mHardwareLayout;
1273 private final OnClickListener mClickListener;
1274 private final OnItemLongClickListener mLongClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001275 private final GradientDrawable mGradientDrawable;
1276 private final ColorExtractor mColorExtractor;
1277 private boolean mKeyguardShowing;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001278
1279 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
1280 OnItemLongClickListener longClickListener) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001281 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Lucas Dupin448786c2017-07-24 17:44:25 -07001282 mContext = context;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001283 mAdapter = adapter;
1284 mClickListener = clickListener;
1285 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001286 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001287 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001288
1289 // Window initialization
1290 Window window = getWindow();
1291 window.requestFeature(Window.FEATURE_NO_TITLE);
1292 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
1293 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
Alison Cichowlas4f19f4a2017-07-25 10:56:16 -04001294 window.addFlags(
1295 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001296 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1297 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1298 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1299 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1300 window.setBackgroundDrawable(mGradientDrawable);
1301 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1302
Jason Monk16fbd9d2017-04-27 14:28:49 -04001303 setContentView(com.android.systemui.R.layout.global_actions_wrapped);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001304 mListView = findViewById(android.R.id.list);
1305 mHardwareLayout = HardwareUiLayout.get(mListView);
1306 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
Jason Monk361915c2017-03-21 20:33:59 -04001307 }
1308
Jason Monk16fbd9d2017-04-27 14:28:49 -04001309 private void updateList() {
1310 mListView.removeAllViews();
1311 for (int i = 0; i < mAdapter.getCount(); i++) {
1312 View v = mAdapter.getView(i, null, mListView);
1313 final int pos = i;
1314 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1315 v.setOnLongClickListener(view ->
1316 mLongClickListener.onItemLongClick(null, v, pos, 0));
1317 mListView.addView(v);
1318 }
Jason Monk361915c2017-03-21 20:33:59 -04001319 }
1320
1321 @Override
1322 protected void onStart() {
1323 super.setCanceledOnTouchOutside(true);
1324 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001325 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001326
1327 Point displaySize = new Point();
1328 mContext.getDisplay().getRealSize(displaySize);
1329 mColorExtractor.addOnColorsChangedListener(this);
1330 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1331 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1332 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001333 updateColors(colors, false /* animate */);
1334 }
1335
1336 /**
1337 * Updates background and system bars according to current GradientColors.
1338 * @param colors Colors and hints to use.
1339 * @param animate Interpolates gradient if true, just sets otherwise.
1340 */
1341 private void updateColors(GradientColors colors, boolean animate) {
1342 mGradientDrawable.setColors(colors, animate);
1343 View decorView = getWindow().getDecorView();
1344 if (colors.supportsDarkText()) {
1345 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
1346 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
1347 } else {
1348 decorView.setSystemUiVisibility(0);
1349 }
Jason Monk361915c2017-03-21 20:33:59 -04001350 }
1351
1352 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001353 protected void onStop() {
1354 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001355 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001356 }
1357
1358 @Override
1359 public void show() {
1360 super.show();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001361 mGradientDrawable.setAlpha(0);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001362 mHardwareLayout.setTranslationX(getAnimTranslation());
1363 mHardwareLayout.setAlpha(0);
1364 mHardwareLayout.animate()
1365 .alpha(1)
1366 .translationX(0)
1367 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001368 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001369 .setUpdateListener(animation -> {
1370 int alpha = (int) ((Float) animation.getAnimatedValue()
1371 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1372 mGradientDrawable.setAlpha(alpha);
1373 })
Jason Monka7af3b62017-07-07 11:35:13 -04001374 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
Jason Monk16fbd9d2017-04-27 14:28:49 -04001375 .start();
1376 }
1377
1378 @Override
1379 public void dismiss() {
1380 mHardwareLayout.setTranslationX(0);
1381 mHardwareLayout.setAlpha(1);
1382 mHardwareLayout.animate()
1383 .alpha(0)
1384 .translationX(getAnimTranslation())
1385 .setDuration(300)
1386 .withEndAction(() -> super.dismiss())
1387 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001388 .setUpdateListener(animation -> {
1389 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1390 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1391 mGradientDrawable.setAlpha(alpha);
1392 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001393 .start();
1394 }
1395
1396 private float getAnimTranslation() {
1397 return getContext().getResources().getDimension(
1398 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001399 }
1400
1401 @Override
1402 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1403 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1404 for (int i = 0; i < mAdapter.getCount(); ++i) {
1405 CharSequence label =
1406 mAdapter.getItem(i).getLabelForAccessibility(getContext());
1407 if (label != null) {
1408 event.getText().add(label);
1409 }
1410 }
1411 }
1412 return super.dispatchPopulateAccessibilityEvent(event);
1413 }
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001414
1415 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001416 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001417 if (mKeyguardShowing) {
1418 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001419 updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
1420 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001421 }
1422 } else {
1423 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001424 updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
1425 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001426 }
1427 }
1428 }
1429
1430 public void setKeyguardShowing(boolean keyguardShowing) {
1431 mKeyguardShowing = keyguardShowing;
1432 }
Jason Monk361915c2017-03-21 20:33:59 -04001433 }
1434}