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