blob: ad177a7e849f70892916935e674f5a4430655119 [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
17import com.android.internal.R;
Lucas Dupine2292a92017-07-06 14:35:30 -070018import com.android.internal.colorextraction.ColorExtractor;
19import com.android.internal.colorextraction.ColorExtractor.GradientColors;
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;
Lucas Dupinde9db422017-07-19 17:15:41 -070028import com.android.systemui.Interpolators;
Lucas Dupin1ead7fc2017-05-24 14:14:44 -070029import com.android.systemui.colorextraction.SysuiColorExtractor;
Jason Monk361915c2017-03-21 20:33:59 -040030import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
Lucas Dupinde9db422017-07-19 17:15:41 -070031import com.android.systemui.statusbar.notification.NotificationUtils;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070032import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk16fbd9d2017-04-27 14:28:49 -040033import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
34import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator;
Jason Monk361915c2017-03-21 20:33:59 -040035
Jason Monk4e04e492017-07-11 11:07:08 -040036import android.animation.ValueAnimator;
37import android.animation.ValueAnimator.AnimatorUpdateListener;
Jason Monk361915c2017-03-21 20:33:59 -040038import android.app.ActivityManager;
39import android.app.Dialog;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070040import android.app.WallpaperManager;
Jason Monk361915c2017-03-21 20:33:59 -040041import android.content.BroadcastReceiver;
42import android.content.Context;
43import android.content.DialogInterface;
44import android.content.Intent;
45import android.content.IntentFilter;
46import android.content.pm.UserInfo;
47import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070048import android.graphics.Point;
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 Dupinde9db422017-07-19 17:15:41 -070070import android.util.MathUtils;
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.Window;
Jason Monk361915c2017-03-21 20:33:59 -040075import android.view.WindowManager;
76import android.view.WindowManagerGlobal;
77import android.view.accessibility.AccessibilityEvent;
78import android.widget.AdapterView;
Jason Monk16fbd9d2017-04-27 14:28:49 -040079import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040080import android.widget.BaseAdapter;
81import android.widget.ImageView;
82import android.widget.ImageView.ScaleType;
Jason Monk16fbd9d2017-04-27 14:28:49 -040083import android.widget.LinearLayout;
Jason Monk361915c2017-03-21 20:33:59 -040084import android.widget.TextView;
85
Lucas Dupine2292a92017-07-06 14:35:30 -070086import com.android.internal.colorextraction.drawable.GradientDrawable;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070087
Jason Monk361915c2017-03-21 20:33:59 -040088import java.util.ArrayList;
89import java.util.List;
90
91/**
92 * Helper to show the global actions dialog. Each item is an {@link Action} that
93 * may show depending on whether the keyguard is showing, and whether the device
94 * is provisioned.
95 */
Jason Monk16fbd9d2017-04-27 14:28:49 -040096class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Jason Monk361915c2017-03-21 20:33:59 -040097
98 static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
99 static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
100
101 private static final String TAG = "GlobalActionsDialog";
102
103 private static final boolean SHOW_SILENT_TOGGLE = true;
104
105 /* Valid settings for global actions keys.
106 * see config.xml config_globalActionList */
107 private static final String GLOBAL_ACTION_KEY_POWER = "power";
108 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
109 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
110 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
111 private static final String GLOBAL_ACTION_KEY_USERS = "users";
112 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
113 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
114 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
115 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
116 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
117
Lucas Dupinde9db422017-07-19 17:15:41 -0700118 private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
119
Jason Monk361915c2017-03-21 20:33:59 -0400120 private final Context mContext;
121 private final GlobalActionsManager mWindowManagerFuncs;
122 private final AudioManager mAudioManager;
123 private final IDreamManager mDreamManager;
124
125 private ArrayList<Action> mItems;
126 private ActionsDialog mDialog;
127
128 private Action mSilentModeAction;
129 private ToggleAction mAirplaneModeOn;
130
131 private MyAdapter mAdapter;
132
133 private boolean mKeyguardShowing = false;
134 private boolean mDeviceProvisioned = false;
135 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
136 private boolean mIsWaitingForEcmExit = false;
137 private boolean mHasTelephony;
138 private boolean mHasVibrator;
139 private final boolean mShowSilentToggle;
140 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
141
142 /**
143 * @param context everything needs a context :(
144 */
145 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
146 mContext = context;
147 mWindowManagerFuncs = windowManagerFuncs;
148 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
149 mDreamManager = IDreamManager.Stub.asInterface(
150 ServiceManager.getService(DreamService.DREAM_SERVICE));
151
152 // receive broadcasts
153 IntentFilter filter = new IntentFilter();
154 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
155 filter.addAction(Intent.ACTION_SCREEN_OFF);
156 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
157 context.registerReceiver(mBroadcastReceiver, filter);
158
159 ConnectivityManager cm = (ConnectivityManager)
160 context.getSystemService(Context.CONNECTIVITY_SERVICE);
161 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
162
163 // get notified of phone state changes
164 TelephonyManager telephonyManager =
165 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
166 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
167 mContext.getContentResolver().registerContentObserver(
168 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
169 mAirplaneModeObserver);
170 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
171 mHasVibrator = vibrator != null && vibrator.hasVibrator();
172
173 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
174 R.bool.config_useFixedVolume);
175
176 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
177 }
178
179 /**
180 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400181 *
Jason Monk361915c2017-03-21 20:33:59 -0400182 * @param keyguardShowing True if keyguard is showing
183 */
184 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
185 mKeyguardShowing = keyguardShowing;
186 mDeviceProvisioned = isDeviceProvisioned;
187 if (mDialog != null) {
188 mDialog.dismiss();
189 mDialog = null;
190 // Show delayed, so that the dismiss of the previous dialog completes
191 mHandler.sendEmptyMessage(MESSAGE_SHOW);
192 } else {
193 handleShow();
194 }
195 }
196
197 private void awakenIfNecessary() {
198 if (mDreamManager != null) {
199 try {
200 if (mDreamManager.isDreaming()) {
201 mDreamManager.awaken();
202 }
203 } catch (RemoteException e) {
204 // we tried
205 }
206 }
207 }
208
209 private void handleShow() {
210 awakenIfNecessary();
211 mDialog = createDialog();
212 prepareDialog();
213
214 // If we only have 1 item and it's a simple press action, just do this action.
215 if (mAdapter.getCount() == 1
216 && mAdapter.getItem(0) instanceof SinglePressAction
217 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
218 ((SinglePressAction) mAdapter.getItem(0)).onPress();
219 } else {
220 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
221 attrs.setTitle("ActionsDialog");
222 mDialog.getWindow().setAttributes(attrs);
223 mDialog.show();
224 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400225 }
226 }
227
228 /**
229 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400230 *
Jason Monk361915c2017-03-21 20:33:59 -0400231 * @return A new dialog.
232 */
233 private ActionsDialog createDialog() {
234 // Simple toggle style if there's no vibrator, otherwise use a tri-state
235 if (!mHasVibrator) {
236 mSilentModeAction = new SilentModeToggleAction();
237 } else {
238 mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
239 }
240 mAirplaneModeOn = new ToggleAction(
241 R.drawable.ic_lock_airplane_mode,
242 R.drawable.ic_lock_airplane_mode_off,
243 R.string.global_actions_toggle_airplane_mode,
244 R.string.global_actions_airplane_mode_on_status,
245 R.string.global_actions_airplane_mode_off_status) {
246
247 void onToggle(boolean on) {
248 if (mHasTelephony && Boolean.parseBoolean(
249 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
250 mIsWaitingForEcmExit = true;
251 // Launch ECM exit dialog
252 Intent ecmDialogIntent =
253 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
254 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
255 mContext.startActivity(ecmDialogIntent);
256 } else {
257 changeAirplaneModeSystemSetting(on);
258 }
259 }
260
261 @Override
262 protected void changeStateFromPress(boolean buttonOn) {
263 if (!mHasTelephony) return;
264
265 // In ECM mode airplane state cannot be changed
266 if (!(Boolean.parseBoolean(
267 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
268 mState = buttonOn ? State.TurningOn : State.TurningOff;
269 mAirplaneState = mState;
270 }
271 }
272
273 public boolean showDuringKeyguard() {
274 return true;
275 }
276
277 public boolean showBeforeProvisioning() {
278 return false;
279 }
280 };
281 onAirplaneModeChanged();
282
283 mItems = new ArrayList<Action>();
284 String[] defaultActions = mContext.getResources().getStringArray(
285 R.array.config_globalActionsList);
286
287 ArraySet<String> addedKeys = new ArraySet<String>();
288 for (int i = 0; i < defaultActions.length; i++) {
289 String actionKey = defaultActions[i];
290 if (addedKeys.contains(actionKey)) {
291 // If we already have added this, don't add it again.
292 continue;
293 }
294 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
295 mItems.add(new PowerAction());
296 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
297 mItems.add(mAirplaneModeOn);
298 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
299 if (Settings.Global.getInt(mContext.getContentResolver(),
300 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
301 mItems.add(new BugReportAction());
302 }
303 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
304 if (mShowSilentToggle) {
305 mItems.add(mSilentModeAction);
306 }
307 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
308 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
309 addUsersToMenu(mItems);
310 }
311 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
312 mItems.add(getSettingsAction());
313 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
314 mItems.add(getLockdownAction());
315 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
316 mItems.add(getVoiceAssistAction());
317 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
318 mItems.add(getAssistAction());
319 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
320 mItems.add(new RestartAction());
321 } else {
322 Log.e(TAG, "Invalid global action key " + actionKey);
323 }
324 // Add here so we don't add more than one.
325 addedKeys.add(actionKey);
326 }
327
328 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
329 mItems.add(getEmergencyAction());
330 }
331
332 mAdapter = new MyAdapter();
333
Jason Monk16fbd9d2017-04-27 14:28:49 -0400334 OnItemLongClickListener onItemLongClickListener = new OnItemLongClickListener() {
335 @Override
336 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
337 long id) {
338 final Action action = mAdapter.getItem(position);
339 if (action instanceof LongPressAction) {
Shunta Satobdb0e492017-04-14 16:01:54 +0900340 mDialog.dismiss();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400341 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) {
Jason Monkfd279662017-06-29 19:37:48 -0400683 Action item = mAdapter.getItem(which);
684 if ((item instanceof PowerAction)
685 || (item instanceof RestartAction)) {
686 if (mDialog != null) mDialog.fadeOut();
687 } else if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400688 dialog.dismiss();
689 }
Jason Monkfd279662017-06-29 19:37:48 -0400690 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400691 }
692
693 /**
694 * The adapter used for the list within the global actions dialog, taking
695 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400696 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
697 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400698 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
699 */
700 private class MyAdapter extends BaseAdapter {
701
702 public int getCount() {
703 int count = 0;
704
705 for (int i = 0; i < mItems.size(); i++) {
706 final Action action = mItems.get(i);
707
708 if (mKeyguardShowing && !action.showDuringKeyguard()) {
709 continue;
710 }
711 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
712 continue;
713 }
714 count++;
715 }
716 return count;
717 }
718
719 @Override
720 public boolean isEnabled(int position) {
721 return getItem(position).isEnabled();
722 }
723
724 @Override
725 public boolean areAllItemsEnabled() {
726 return false;
727 }
728
729 public Action getItem(int position) {
730
731 int filteredPos = 0;
732 for (int i = 0; i < mItems.size(); i++) {
733 final Action action = mItems.get(i);
734 if (mKeyguardShowing && !action.showDuringKeyguard()) {
735 continue;
736 }
737 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
738 continue;
739 }
740 if (filteredPos == position) {
741 return action;
742 }
743 filteredPos++;
744 }
745
746 throw new IllegalArgumentException("position " + position
747 + " out of range of showable actions"
748 + ", filtered count=" + getCount()
749 + ", keyguardshowing=" + mKeyguardShowing
750 + ", provisioned=" + mDeviceProvisioned);
751 }
752
753
754 public long getItemId(int position) {
755 return position;
756 }
757
758 public View getView(int position, View convertView, ViewGroup parent) {
759 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400760 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
761 if (position == 2) {
762 HardwareUiLayout.get(parent).setDivisionView(view);
763 }
764 return view;
Jason Monk361915c2017-03-21 20:33:59 -0400765 }
766 }
767
768 // note: the scheme below made more sense when we were planning on having
769 // 8 different things in the global actions dialog. seems overkill with
770 // only 3 items now, but may as well keep this flexible approach so it will
771 // be easy should someone decide at the last minute to include something
772 // else, such as 'enable wifi', or 'enable bluetooth'
773
774 /**
775 * What each item in the global actions dialog must be able to support.
776 */
777 private interface Action {
778 /**
779 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -0400780 * for none.
Jason Monk361915c2017-03-21 20:33:59 -0400781 */
782 CharSequence getLabelForAccessibility(Context context);
783
784 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
785
786 void onPress();
787
788 /**
789 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -0400790 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -0400791 */
792 boolean showDuringKeyguard();
793
794 /**
795 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -0400796 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -0400797 */
798 boolean showBeforeProvisioning();
799
800 boolean isEnabled();
801 }
802
803 /**
804 * An action that also supports long press.
805 */
806 private interface LongPressAction extends Action {
807 boolean onLongPress();
808 }
809
810 /**
811 * A single press action maintains no state, just responds to a press
812 * and takes an action.
813 */
814 private static abstract class SinglePressAction implements Action {
815 private final int mIconResId;
816 private final Drawable mIcon;
817 private final int mMessageResId;
818 private final CharSequence mMessage;
819
820 protected SinglePressAction(int iconResId, int messageResId) {
821 mIconResId = iconResId;
822 mMessageResId = messageResId;
823 mMessage = null;
824 mIcon = null;
825 }
826
827 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
828 mIconResId = iconResId;
829 mMessageResId = 0;
830 mMessage = message;
831 mIcon = icon;
832 }
833
834 public boolean isEnabled() {
835 return true;
836 }
837
838 public String getStatus() {
839 return null;
840 }
841
842 abstract public void onPress();
843
844 public CharSequence getLabelForAccessibility(Context context) {
845 if (mMessage != null) {
846 return mMessage;
847 } else {
848 return context.getString(mMessageResId);
849 }
850 }
851
852 public View create(
853 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Jason Monk16fbd9d2017-04-27 14:28:49 -0400854 View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent,
855 false);
Jason Monk361915c2017-03-21 20:33:59 -0400856
857 ImageView icon = (ImageView) v.findViewById(R.id.icon);
858 TextView messageView = (TextView) v.findViewById(R.id.message);
859
860 TextView statusView = (TextView) v.findViewById(R.id.status);
861 final String status = getStatus();
862 if (!TextUtils.isEmpty(status)) {
863 statusView.setText(status);
864 } else {
865 statusView.setVisibility(View.GONE);
866 }
867 if (mIcon != null) {
868 icon.setImageDrawable(mIcon);
869 icon.setScaleType(ScaleType.CENTER_CROP);
870 } else if (mIconResId != 0) {
871 icon.setImageDrawable(context.getDrawable(mIconResId));
872 }
873 if (mMessage != null) {
874 messageView.setText(mMessage);
875 } else {
876 messageView.setText(mMessageResId);
877 }
878
879 return v;
880 }
881 }
882
883 /**
884 * A toggle action knows whether it is on or off, and displays an icon
885 * and status message accordingly.
886 */
887 private static abstract class ToggleAction implements Action {
888
889 enum State {
890 Off(false),
891 TurningOn(true),
892 TurningOff(true),
893 On(false);
894
895 private final boolean inTransition;
896
897 State(boolean intermediate) {
898 inTransition = intermediate;
899 }
900
901 public boolean inTransition() {
902 return inTransition;
903 }
904 }
905
906 protected State mState = State.Off;
907
908 // prefs
909 protected int mEnabledIconResId;
910 protected int mDisabledIconResid;
911 protected int mMessageResId;
912 protected int mEnabledStatusMessageResId;
913 protected int mDisabledStatusMessageResId;
914
915 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -0400916 * @param enabledIconResId The icon for when this action is on.
917 * @param disabledIconResid The icon for when this action is off.
918 * @param message The general information message, e.g 'Silent Mode'
919 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -0400920 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
921 */
922 public ToggleAction(int enabledIconResId,
923 int disabledIconResid,
924 int message,
925 int enabledStatusMessageResId,
926 int disabledStatusMessageResId) {
927 mEnabledIconResId = enabledIconResId;
928 mDisabledIconResid = disabledIconResid;
929 mMessageResId = message;
930 mEnabledStatusMessageResId = enabledStatusMessageResId;
931 mDisabledStatusMessageResId = disabledStatusMessageResId;
932 }
933
934 /**
935 * Override to make changes to resource IDs just before creating the
936 * View.
937 */
938 void willCreate() {
939
940 }
941
942 @Override
943 public CharSequence getLabelForAccessibility(Context context) {
944 return context.getString(mMessageResId);
945 }
946
947 public View create(Context context, View convertView, ViewGroup parent,
948 LayoutInflater inflater) {
949 willCreate();
950
951 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -0400952 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -0400953
954 ImageView icon = (ImageView) v.findViewById(R.id.icon);
955 TextView messageView = (TextView) v.findViewById(R.id.message);
956 TextView statusView = (TextView) v.findViewById(R.id.status);
957 final boolean enabled = isEnabled();
958
959 if (messageView != null) {
960 messageView.setText(mMessageResId);
961 messageView.setEnabled(enabled);
962 }
963
964 boolean on = ((mState == State.On) || (mState == State.TurningOn));
965 if (icon != null) {
966 icon.setImageDrawable(context.getDrawable(
967 (on ? mEnabledIconResId : mDisabledIconResid)));
968 icon.setEnabled(enabled);
969 }
970
971 if (statusView != null) {
972 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
973 statusView.setVisibility(View.VISIBLE);
974 statusView.setEnabled(enabled);
975 }
976 v.setEnabled(enabled);
977
978 return v;
979 }
980
981 public final void onPress() {
982 if (mState.inTransition()) {
983 Log.w(TAG, "shouldn't be able to toggle when in transition");
984 return;
985 }
986
987 final boolean nowOn = !(mState == State.On);
988 onToggle(nowOn);
989 changeStateFromPress(nowOn);
990 }
991
992 public boolean isEnabled() {
993 return !mState.inTransition();
994 }
995
996 /**
997 * Implementations may override this if their state can be in on of the intermediate
998 * states until some notification is received (e.g airplane mode is 'turning off' until
999 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001000 *
Jason Monk361915c2017-03-21 20:33:59 -04001001 * @param buttonOn Whether the button was turned on or off
1002 */
1003 protected void changeStateFromPress(boolean buttonOn) {
1004 mState = buttonOn ? State.On : State.Off;
1005 }
1006
1007 abstract void onToggle(boolean on);
1008
1009 public void updateState(State state) {
1010 mState = state;
1011 }
1012 }
1013
1014 private class SilentModeToggleAction extends ToggleAction {
1015 public SilentModeToggleAction() {
1016 super(R.drawable.ic_audio_vol_mute,
1017 R.drawable.ic_audio_vol,
1018 R.string.global_action_toggle_silent_mode,
1019 R.string.global_action_silent_mode_on_status,
1020 R.string.global_action_silent_mode_off_status);
1021 }
1022
1023 void onToggle(boolean on) {
1024 if (on) {
1025 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1026 } else {
1027 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1028 }
1029 }
1030
1031 public boolean showDuringKeyguard() {
1032 return true;
1033 }
1034
1035 public boolean showBeforeProvisioning() {
1036 return false;
1037 }
1038 }
1039
1040 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1041
Jason Monk16fbd9d2017-04-27 14:28:49 -04001042 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001043
1044 private final AudioManager mAudioManager;
1045 private final Handler mHandler;
1046 private final Context mContext;
1047
1048 SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
1049 mAudioManager = audioManager;
1050 mHandler = handler;
1051 mContext = context;
1052 }
1053
1054 private int ringerModeToIndex(int ringerMode) {
1055 // They just happen to coincide
1056 return ringerMode;
1057 }
1058
1059 private int indexToRingerMode(int index) {
1060 // They just happen to coincide
1061 return index;
1062 }
1063
1064 @Override
1065 public CharSequence getLabelForAccessibility(Context context) {
1066 return null;
1067 }
1068
1069 public View create(Context context, View convertView, ViewGroup parent,
1070 LayoutInflater inflater) {
1071 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1072
1073 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1074 for (int i = 0; i < 3; i++) {
1075 View itemView = v.findViewById(ITEM_IDS[i]);
1076 itemView.setSelected(selectedIndex == i);
1077 // Set up click handler
1078 itemView.setTag(i);
1079 itemView.setOnClickListener(this);
1080 }
1081 return v;
1082 }
1083
1084 public void onPress() {
1085 }
1086
1087 public boolean showDuringKeyguard() {
1088 return true;
1089 }
1090
1091 public boolean showBeforeProvisioning() {
1092 return false;
1093 }
1094
1095 public boolean isEnabled() {
1096 return true;
1097 }
1098
1099 void willCreate() {
1100 }
1101
1102 public void onClick(View v) {
1103 if (!(v.getTag() instanceof Integer)) return;
1104
1105 int index = (Integer) v.getTag();
1106 mAudioManager.setRingerMode(indexToRingerMode(index));
1107 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1108 }
1109 }
1110
1111 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1112 public void onReceive(Context context, Intent intent) {
1113 String action = intent.getAction();
1114 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1115 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1116 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1117 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
1118 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
1119 }
1120 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1121 // Airplane mode can be changed after ECM exits if airplane toggle button
1122 // is pressed during ECM mode
1123 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1124 mIsWaitingForEcmExit) {
1125 mIsWaitingForEcmExit = false;
1126 changeAirplaneModeSystemSetting(true);
1127 }
1128 }
1129 }
1130 };
1131
1132 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1133 @Override
1134 public void onServiceStateChanged(ServiceState serviceState) {
1135 if (!mHasTelephony) return;
1136 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1137 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1138 mAirplaneModeOn.updateState(mAirplaneState);
1139 mAdapter.notifyDataSetChanged();
1140 }
1141 };
1142
1143 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1144 @Override
1145 public void onReceive(Context context, Intent intent) {
1146 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1147 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1148 }
1149 }
1150 };
1151
1152 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1153 @Override
1154 public void onChange(boolean selfChange) {
1155 onAirplaneModeChanged();
1156 }
1157 };
1158
1159 private static final int MESSAGE_DISMISS = 0;
1160 private static final int MESSAGE_REFRESH = 1;
1161 private static final int MESSAGE_SHOW = 2;
1162 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1163
1164 private Handler mHandler = new Handler() {
1165 public void handleMessage(Message msg) {
1166 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001167 case MESSAGE_DISMISS:
1168 if (mDialog != null) {
1169 mDialog.dismiss();
1170 mDialog = null;
1171 }
1172 break;
1173 case MESSAGE_REFRESH:
1174 refreshSilentMode();
1175 mAdapter.notifyDataSetChanged();
1176 break;
1177 case MESSAGE_SHOW:
1178 handleShow();
1179 break;
Jason Monk361915c2017-03-21 20:33:59 -04001180 }
1181 }
1182 };
1183
1184 private void onAirplaneModeChanged() {
1185 // Let the service state callbacks handle the state.
1186 if (mHasTelephony) return;
1187
1188 boolean airplaneModeOn = Settings.Global.getInt(
1189 mContext.getContentResolver(),
1190 Settings.Global.AIRPLANE_MODE_ON,
1191 0) == 1;
1192 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1193 mAirplaneModeOn.updateState(mAirplaneState);
1194 }
1195
1196 /**
1197 * Change the airplane mode system setting
1198 */
1199 private void changeAirplaneModeSystemSetting(boolean on) {
1200 Settings.Global.putInt(
1201 mContext.getContentResolver(),
1202 Settings.Global.AIRPLANE_MODE_ON,
1203 on ? 1 : 0);
1204 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1205 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1206 intent.putExtra("state", on);
1207 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1208 if (!mHasTelephony) {
1209 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1210 }
1211 }
1212
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001213 private static final class ActionsDialog extends Dialog implements DialogInterface,
1214 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001215
Jason Monk16fbd9d2017-04-27 14:28:49 -04001216 private final Context mContext;
1217 private final MyAdapter mAdapter;
1218 private final LinearLayout mListView;
1219 private final HardwareUiLayout mHardwareLayout;
1220 private final OnClickListener mClickListener;
1221 private final OnItemLongClickListener mLongClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001222 private final GradientDrawable mGradientDrawable;
1223 private final ColorExtractor mColorExtractor;
1224 private boolean mKeyguardShowing;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001225
1226 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
1227 OnItemLongClickListener longClickListener) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001228 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Jason Monk361915c2017-03-21 20:33:59 -04001229 mContext = getContext();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001230 mAdapter = adapter;
1231 mClickListener = clickListener;
1232 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001233 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001234 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001235
1236 // Window initialization
1237 Window window = getWindow();
1238 window.requestFeature(Window.FEATURE_NO_TITLE);
1239 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
1240 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1241 window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1242 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1243 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1244 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1245 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1246 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1247 window.setBackgroundDrawable(mGradientDrawable);
1248 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1249
Jason Monk16fbd9d2017-04-27 14:28:49 -04001250 setContentView(com.android.systemui.R.layout.global_actions_wrapped);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001251 mListView = findViewById(android.R.id.list);
1252 mHardwareLayout = HardwareUiLayout.get(mListView);
1253 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
Jason Monk361915c2017-03-21 20:33:59 -04001254 }
1255
Jason Monk16fbd9d2017-04-27 14:28:49 -04001256 private void updateList() {
1257 mListView.removeAllViews();
1258 for (int i = 0; i < mAdapter.getCount(); i++) {
1259 View v = mAdapter.getView(i, null, mListView);
1260 final int pos = i;
1261 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1262 v.setOnLongClickListener(view ->
1263 mLongClickListener.onItemLongClick(null, v, pos, 0));
1264 mListView.addView(v);
1265 }
Jason Monk361915c2017-03-21 20:33:59 -04001266 }
1267
1268 @Override
1269 protected void onStart() {
1270 super.setCanceledOnTouchOutside(true);
1271 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001272 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001273
1274 Point displaySize = new Point();
1275 mContext.getDisplay().getRealSize(displaySize);
1276 mColorExtractor.addOnColorsChangedListener(this);
1277 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1278 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1279 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
1280 mGradientDrawable.setColors(colors, false);
Jason Monk361915c2017-03-21 20:33:59 -04001281 }
1282
1283 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001284 protected void onStop() {
1285 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001286 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001287 }
1288
1289 @Override
1290 public void show() {
1291 super.show();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001292 mGradientDrawable.setAlpha(0);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001293 mHardwareLayout.setTranslationX(getAnimTranslation());
1294 mHardwareLayout.setAlpha(0);
1295 mHardwareLayout.animate()
1296 .alpha(1)
1297 .translationX(0)
1298 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001299 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001300 .setUpdateListener(animation -> {
1301 int alpha = (int) ((Float) animation.getAnimatedValue()
1302 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1303 mGradientDrawable.setAlpha(alpha);
1304 })
Jason Monka7af3b62017-07-07 11:35:13 -04001305 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
Jason Monk16fbd9d2017-04-27 14:28:49 -04001306 .start();
1307 }
1308
1309 @Override
1310 public void dismiss() {
1311 mHardwareLayout.setTranslationX(0);
1312 mHardwareLayout.setAlpha(1);
1313 mHardwareLayout.animate()
1314 .alpha(0)
1315 .translationX(getAnimTranslation())
1316 .setDuration(300)
1317 .withEndAction(() -> super.dismiss())
1318 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001319 .setUpdateListener(animation -> {
1320 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1321 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1322 mGradientDrawable.setAlpha(alpha);
1323 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001324 .start();
1325 }
1326
Jason Monkfd279662017-06-29 19:37:48 -04001327 public void fadeOut() {
1328 mHardwareLayout.setTranslationX(0);
1329 mHardwareLayout.setAlpha(1);
1330 mListView.animate()
1331 .alpha(0)
1332 .translationX(getAnimTranslation())
1333 .setDuration(300)
1334 .setInterpolator(new LogAccelerateInterpolator())
Jason Monk4e04e492017-07-11 11:07:08 -04001335 .setUpdateListener(animation -> {
1336 float frac = animation.getAnimatedFraction();
Lucas Dupinde9db422017-07-19 17:15:41 -07001337 float alpha = NotificationUtils.interpolate(
1338 ScrimController.GRADIENT_SCRIM_ALPHA, SHUTDOWN_SCRIM_ALPHA, frac);
Jason Monk4e04e492017-07-11 11:07:08 -04001339 mGradientDrawable.setAlpha((int) (alpha * 255));
1340 })
Jason Monkfd279662017-06-29 19:37:48 -04001341 .start();
1342 }
1343
Jason Monk16fbd9d2017-04-27 14:28:49 -04001344 private float getAnimTranslation() {
1345 return getContext().getResources().getDimension(
1346 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001347 }
1348
1349 @Override
1350 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1351 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
1352 for (int i = 0; i < mAdapter.getCount(); ++i) {
1353 CharSequence label =
1354 mAdapter.getItem(i).getLabelForAccessibility(getContext());
1355 if (label != null) {
1356 event.getText().add(label);
1357 }
1358 }
1359 }
1360 return super.dispatchPopulateAccessibilityEvent(event);
1361 }
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001362
1363 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001364 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001365 if (mKeyguardShowing) {
1366 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001367 mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_LOCK));
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001368 }
1369 } else {
1370 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001371 mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM));
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001372 }
1373 }
1374 }
1375
1376 public void setKeyguardShowing(boolean keyguardShowing) {
1377 mKeyguardShowing = keyguardShowing;
1378 }
Jason Monk361915c2017-03-21 20:33:59 -04001379 }
1380}