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