blob: 3fa6035387c718934417d9186fddaec0b081a765 [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
Adrian Roosedfab3b2018-03-08 18:39:20 +010017import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Adrian Roos2f05bb32018-02-19 16:42:27 +010018import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
19
Chad Brubakerf4075fe2018-01-03 13:23:22 -080020import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
21import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -070022import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
23
Jason Monk361915c2017-03-21 20:33:59 -040024import android.app.ActivityManager;
25import android.app.Dialog;
Chad Brubakerf4075fe2018-01-03 13:23:22 -080026import android.app.KeyguardManager;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070027import android.app.WallpaperManager;
Alex Chau04458852017-11-27 18:21:23 +000028import android.app.admin.DevicePolicyManager;
Pavel Grafov059021b2018-05-02 13:44:46 +010029import android.app.trust.TrustManager;
Jason Monk361915c2017-03-21 20:33:59 -040030import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.pm.UserInfo;
36import android.database.ContentObserver;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070037import android.graphics.Point;
Jason Monk361915c2017-03-21 20:33:59 -040038import android.graphics.drawable.Drawable;
39import android.media.AudioManager;
40import android.net.ConnectivityManager;
41import android.os.Build;
Jason Monk361915c2017-03-21 20:33:59 -040042import android.os.Handler;
43import android.os.Message;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.SystemProperties;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.os.Vibrator;
50import android.provider.Settings;
51import android.service.dreams.DreamService;
52import android.service.dreams.IDreamManager;
53import android.telephony.PhoneStateListener;
54import android.telephony.ServiceState;
55import android.telephony.TelephonyManager;
56import android.text.TextUtils;
57import android.util.ArraySet;
yuanjiahsu88363e92018-09-06 19:23:52 +080058import android.util.FeatureFlagUtils;
Jason Monk361915c2017-03-21 20:33:59 -040059import android.util.Log;
Lucas Dupin448786c2017-07-24 17:44:25 -070060import android.view.ContextThemeWrapper;
Jason Monk361915c2017-03-21 20:33:59 -040061import android.view.LayoutInflater;
62import android.view.View;
63import android.view.ViewGroup;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070064import android.view.Window;
Jason Monk361915c2017-03-21 20:33:59 -040065import android.view.WindowManager;
66import android.view.WindowManagerGlobal;
67import android.view.accessibility.AccessibilityEvent;
Jason Monk16fbd9d2017-04-27 14:28:49 -040068import android.widget.AdapterView.OnItemLongClickListener;
Jason Monk361915c2017-03-21 20:33:59 -040069import android.widget.BaseAdapter;
70import android.widget.ImageView;
71import android.widget.ImageView.ScaleType;
Jason Monk361915c2017-03-21 20:33:59 -040072import android.widget.TextView;
73
Charles He9851a8d2017-10-10 17:31:30 +010074import com.android.internal.R;
75import com.android.internal.colorextraction.ColorExtractor;
76import com.android.internal.colorextraction.ColorExtractor.GradientColors;
Lucas Dupine2292a92017-07-06 14:35:30 -070077import com.android.internal.colorextraction.drawable.GradientDrawable;
Charles He9851a8d2017-10-10 17:31:30 +010078import com.android.internal.logging.MetricsLogger;
79import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
80import com.android.internal.telephony.TelephonyIntents;
81import com.android.internal.telephony.TelephonyProperties;
82import com.android.internal.util.EmergencyAffordanceManager;
Beth Thibodeau5898ac42018-10-26 13:00:09 -040083import com.android.internal.util.ScreenRecordHelper;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050084import com.android.internal.util.ScreenshotHelper;
Charles He9851a8d2017-10-10 17:31:30 +010085import com.android.internal.widget.LockPatternUtils;
86import com.android.systemui.Dependency;
Charles He9851a8d2017-10-10 17:31:30 +010087import com.android.systemui.Interpolators;
Aaron Heuckrothf708d472019-01-10 16:54:51 -050088import com.android.systemui.MultiListLayout;
Charles He9851a8d2017-10-10 17:31:30 +010089import com.android.systemui.colorextraction.SysuiColorExtractor;
90import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
91import com.android.systemui.statusbar.phone.ScrimController;
Lucas Dupine5b7dc72018-10-02 15:18:05 -070092import com.android.systemui.statusbar.policy.ConfigurationController;
Shaotang Li5c422632018-07-04 14:18:40 +080093import com.android.systemui.util.EmergencyDialerConstants;
Aaron Heuckroth75e249f2019-02-01 15:59:57 -050094import com.android.systemui.util.leak.RotationUtils;
Julia Reynolds42411922017-11-08 11:19:09 -050095import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070096
Jason Monk361915c2017-03-21 20:33:59 -040097import java.util.ArrayList;
98import java.util.List;
99
100/**
101 * Helper to show the global actions dialog. Each item is an {@link Action} that
102 * may show depending on whether the keyguard is showing, and whether the device
103 * is provisioned.
104 */
Charles He9851a8d2017-10-10 17:31:30 +0100105class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Lucas Dupine5b7dc72018-10-02 15:18:05 -0700106 DialogInterface.OnClickListener, DialogInterface.OnShowListener,
107 ConfigurationController.ConfigurationListener {
Jason Monk361915c2017-03-21 20:33:59 -0400108
109 static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
110 static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700111 static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
Jason Monk361915c2017-03-21 20:33:59 -0400112
113 private static final String TAG = "GlobalActionsDialog";
114
115 private static final boolean SHOW_SILENT_TOGGLE = true;
116
117 /* Valid settings for global actions keys.
118 * see config.xml config_globalActionList */
119 private static final String GLOBAL_ACTION_KEY_POWER = "power";
120 private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
121 private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
122 private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
123 private static final String GLOBAL_ACTION_KEY_USERS = "users";
124 private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
125 private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
126 private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
127 private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
128 private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
Alex Chau04458852017-11-27 18:21:23 +0000129 private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800130 private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500131 private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
Jason Monk361915c2017-03-21 20:33:59 -0400132
133 private final Context mContext;
134 private final GlobalActionsManager mWindowManagerFuncs;
135 private final AudioManager mAudioManager;
136 private final IDreamManager mDreamManager;
Alex Chau04458852017-11-27 18:21:23 +0000137 private final DevicePolicyManager mDevicePolicyManager;
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800138 private final LockPatternUtils mLockPatternUtils;
139 private final KeyguardManager mKeyguardManager;
Jason Monk361915c2017-03-21 20:33:59 -0400140
141 private ArrayList<Action> mItems;
142 private ActionsDialog mDialog;
143
144 private Action mSilentModeAction;
145 private ToggleAction mAirplaneModeOn;
146
147 private MyAdapter mAdapter;
148
149 private boolean mKeyguardShowing = false;
150 private boolean mDeviceProvisioned = false;
151 private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
152 private boolean mIsWaitingForEcmExit = false;
153 private boolean mHasTelephony;
154 private boolean mHasVibrator;
Alex Chau04458852017-11-27 18:21:23 +0000155 private boolean mHasLogoutButton;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800156 private boolean mHasLockdownButton;
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800157 private boolean mSeparatedEmergencyButtonEnabled;
Jason Monk361915c2017-03-21 20:33:59 -0400158 private final boolean mShowSilentToggle;
159 private final EmergencyAffordanceManager mEmergencyAffordanceManager;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500160 private final ScreenshotHelper mScreenshotHelper;
Beth Thibodeau5898ac42018-10-26 13:00:09 -0400161 private final ScreenRecordHelper mScreenRecordHelper;
Jason Monk361915c2017-03-21 20:33:59 -0400162
163 /**
164 * @param context everything needs a context :(
165 */
166 public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
Lucas Dupin448786c2017-07-24 17:44:25 -0700167 mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
Jason Monk361915c2017-03-21 20:33:59 -0400168 mWindowManagerFuncs = windowManagerFuncs;
169 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
170 mDreamManager = IDreamManager.Stub.asInterface(
171 ServiceManager.getService(DreamService.DREAM_SERVICE));
Alex Chau04458852017-11-27 18:21:23 +0000172 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
173 Context.DEVICE_POLICY_SERVICE);
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800174 mLockPatternUtils = new LockPatternUtils(mContext);
175 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Jason Monk361915c2017-03-21 20:33:59 -0400176
177 // receive broadcasts
178 IntentFilter filter = new IntentFilter();
179 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
180 filter.addAction(Intent.ACTION_SCREEN_OFF);
181 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
182 context.registerReceiver(mBroadcastReceiver, filter);
183
184 ConnectivityManager cm = (ConnectivityManager)
185 context.getSystemService(Context.CONNECTIVITY_SERVICE);
186 mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
187
188 // get notified of phone state changes
189 TelephonyManager telephonyManager =
190 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
191 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
192 mContext.getContentResolver().registerContentObserver(
193 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
194 mAirplaneModeObserver);
195 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
196 mHasVibrator = vibrator != null && vibrator.hasVibrator();
197
198 mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
199 R.bool.config_useFixedVolume);
200
201 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500202 mScreenshotHelper = new ScreenshotHelper(context);
Beth Thibodeau5898ac42018-10-26 13:00:09 -0400203 mScreenRecordHelper = new ScreenRecordHelper(context);
Lucas Dupine5b7dc72018-10-02 15:18:05 -0700204
205 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk361915c2017-03-21 20:33:59 -0400206 }
207
208 /**
209 * Show the global actions dialog (creating if necessary)
Jason Monk16fbd9d2017-04-27 14:28:49 -0400210 *
Jason Monk361915c2017-03-21 20:33:59 -0400211 * @param keyguardShowing True if keyguard is showing
212 */
213 public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
214 mKeyguardShowing = keyguardShowing;
215 mDeviceProvisioned = isDeviceProvisioned;
216 if (mDialog != null) {
217 mDialog.dismiss();
218 mDialog = null;
219 // Show delayed, so that the dismiss of the previous dialog completes
220 mHandler.sendEmptyMessage(MESSAGE_SHOW);
221 } else {
222 handleShow();
223 }
224 }
225
Charles He9851a8d2017-10-10 17:31:30 +0100226 /**
227 * Dismiss the global actions dialog, if it's currently shown
228 */
229 public void dismissDialog() {
230 mHandler.removeMessages(MESSAGE_DISMISS);
231 mHandler.sendEmptyMessage(MESSAGE_DISMISS);
232 }
233
Jason Monk361915c2017-03-21 20:33:59 -0400234 private void awakenIfNecessary() {
235 if (mDreamManager != null) {
236 try {
237 if (mDreamManager.isDreaming()) {
238 mDreamManager.awaken();
239 }
240 } catch (RemoteException e) {
241 // we tried
242 }
243 }
244 }
245
246 private void handleShow() {
247 awakenIfNecessary();
248 mDialog = createDialog();
249 prepareDialog();
250
251 // If we only have 1 item and it's a simple press action, just do this action.
252 if (mAdapter.getCount() == 1
253 && mAdapter.getItem(0) instanceof SinglePressAction
254 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
255 ((SinglePressAction) mAdapter.getItem(0)).onPress();
256 } else {
257 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
258 attrs.setTitle("ActionsDialog");
Adrian Roos2f05bb32018-02-19 16:42:27 +0100259 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Jason Monk361915c2017-03-21 20:33:59 -0400260 mDialog.getWindow().setAttributes(attrs);
261 mDialog.show();
262 mWindowManagerFuncs.onGlobalActionsShown();
Jason Monk361915c2017-03-21 20:33:59 -0400263 }
264 }
265
266 /**
267 * Create the global actions dialog.
Jason Monk16fbd9d2017-04-27 14:28:49 -0400268 *
Jason Monk361915c2017-03-21 20:33:59 -0400269 * @return A new dialog.
270 */
271 private ActionsDialog createDialog() {
272 // Simple toggle style if there's no vibrator, otherwise use a tri-state
273 if (!mHasVibrator) {
274 mSilentModeAction = new SilentModeToggleAction();
275 } else {
Lucas Dupin40ec6b782018-06-05 19:07:16 -0700276 mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
Jason Monk361915c2017-03-21 20:33:59 -0400277 }
278 mAirplaneModeOn = new ToggleAction(
279 R.drawable.ic_lock_airplane_mode,
280 R.drawable.ic_lock_airplane_mode_off,
281 R.string.global_actions_toggle_airplane_mode,
282 R.string.global_actions_airplane_mode_on_status,
283 R.string.global_actions_airplane_mode_off_status) {
284
285 void onToggle(boolean on) {
286 if (mHasTelephony && Boolean.parseBoolean(
287 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
288 mIsWaitingForEcmExit = true;
289 // Launch ECM exit dialog
290 Intent ecmDialogIntent =
291 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
292 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
293 mContext.startActivity(ecmDialogIntent);
294 } else {
295 changeAirplaneModeSystemSetting(on);
296 }
297 }
298
299 @Override
300 protected void changeStateFromPress(boolean buttonOn) {
301 if (!mHasTelephony) return;
302
303 // In ECM mode airplane state cannot be changed
304 if (!(Boolean.parseBoolean(
305 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
306 mState = buttonOn ? State.TurningOn : State.TurningOff;
307 mAirplaneState = mState;
308 }
309 }
310
311 public boolean showDuringKeyguard() {
312 return true;
313 }
314
315 public boolean showBeforeProvisioning() {
316 return false;
317 }
318 };
319 onAirplaneModeChanged();
320
321 mItems = new ArrayList<Action>();
322 String[] defaultActions = mContext.getResources().getStringArray(
323 R.array.config_globalActionsList);
324
325 ArraySet<String> addedKeys = new ArraySet<String>();
Alex Chau04458852017-11-27 18:21:23 +0000326 mHasLogoutButton = false;
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800327 mHasLockdownButton = false;
yuanjiahsu2c69b0b2018-11-16 21:37:33 +0800328 mSeparatedEmergencyButtonEnabled = true;
Jason Monk361915c2017-03-21 20:33:59 -0400329 for (int i = 0; i < defaultActions.length; i++) {
330 String actionKey = defaultActions[i];
331 if (addedKeys.contains(actionKey)) {
332 // If we already have added this, don't add it again.
333 continue;
334 }
335 if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
336 mItems.add(new PowerAction());
337 } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
338 mItems.add(mAirplaneModeOn);
339 } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
340 if (Settings.Global.getInt(mContext.getContentResolver(),
341 Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
342 mItems.add(new BugReportAction());
343 }
344 } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
345 if (mShowSilentToggle) {
346 mItems.add(mSilentModeAction);
347 }
348 } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
349 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
350 addUsersToMenu(mItems);
351 }
352 } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
353 mItems.add(getSettingsAction());
354 } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
Chad Brubaker02cd6cf2018-05-01 14:59:33 -0700355 if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
356 Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800357 && shouldDisplayLockdown()) {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700358 mItems.add(getLockdownAction());
Chad Brubaker72a73ea2018-01-26 15:56:55 -0800359 mHasLockdownButton = true;
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700360 }
Jason Monk361915c2017-03-21 20:33:59 -0400361 } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
362 mItems.add(getVoiceAssistAction());
363 } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
364 mItems.add(getAssistAction());
365 } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
366 mItems.add(new RestartAction());
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500367 } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
368 mItems.add(new ScreenshotAction());
Alex Chau04458852017-11-27 18:21:23 +0000369 } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
Alex Chaud7958272017-12-08 11:30:52 +0000370 if (mDevicePolicyManager.isLogoutEnabled()
Alex Chau04458852017-11-27 18:21:23 +0000371 && getCurrentUser().id != UserHandle.USER_SYSTEM) {
372 mItems.add(new LogoutAction());
373 mHasLogoutButton = true;
374 }
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800375 } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
376 if (mSeparatedEmergencyButtonEnabled
377 && !mEmergencyAffordanceManager.needsEmergencyAffordance()) {
378 mItems.add(new EmergencyDialerAction());
379 }
Jason Monk361915c2017-03-21 20:33:59 -0400380 } else {
381 Log.e(TAG, "Invalid global action key " + actionKey);
382 }
383 // Add here so we don't add more than one.
384 addedKeys.add(actionKey);
385 }
386
387 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
388 mItems.add(getEmergencyAction());
389 }
390
391 mAdapter = new MyAdapter();
392
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700393 OnItemLongClickListener onItemLongClickListener = (parent, view, position, id) -> {
394 final Action action = mAdapter.getItem(position);
395 if (action instanceof LongPressAction) {
396 mDialog.dismiss();
397 return ((LongPressAction) action).onLongPress();
Jason Monk16fbd9d2017-04-27 14:28:49 -0400398 }
Lucas Dupin1d4a5792018-04-02 15:14:59 -0700399 return false;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400400 };
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800401 ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener,
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800402 mSeparatedEmergencyButtonEnabled);
Jason Monk361915c2017-03-21 20:33:59 -0400403 dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700404 dialog.setKeyguardShowing(mKeyguardShowing);
Jason Monk361915c2017-03-21 20:33:59 -0400405
406 dialog.setOnDismissListener(this);
Shaotang Li786da902018-08-02 11:18:00 +0800407 dialog.setOnShowListener(this);
Jason Monk361915c2017-03-21 20:33:59 -0400408
409 return dialog;
410 }
411
Chad Brubakerf4075fe2018-01-03 13:23:22 -0800412 private boolean shouldDisplayLockdown() {
413 int userId = getCurrentUser().id;
414 // Lockdown is meaningless without a place to go.
415 if (!mKeyguardManager.isDeviceSecure(userId)) {
416 return false;
417 }
418
419 // Only show the lockdown button if the device isn't locked down (for whatever reason).
420 int state = mLockPatternUtils.getStrongAuthForUser(userId);
421 return (state == STRONG_AUTH_NOT_REQUIRED
422 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
423 }
424
Lucas Dupine5b7dc72018-10-02 15:18:05 -0700425 @Override
426 public void onUiModeChanged() {
427 mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
428 }
429
430 public void destroy() {
431 Dependency.get(ConfigurationController.class).removeCallback(this);
432 }
433
Jason Monk361915c2017-03-21 20:33:59 -0400434 private final class PowerAction extends SinglePressAction implements LongPressAction {
435 private PowerAction() {
436 super(R.drawable.ic_lock_power_off,
Jason Monk16fbd9d2017-04-27 14:28:49 -0400437 R.string.global_action_power_off);
Jason Monk361915c2017-03-21 20:33:59 -0400438 }
439
440 @Override
441 public boolean onLongPress() {
442 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
443 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
444 mWindowManagerFuncs.reboot(true);
445 return true;
446 }
447 return false;
448 }
449
450 @Override
451 public boolean showDuringKeyguard() {
452 return true;
453 }
454
455 @Override
456 public boolean showBeforeProvisioning() {
457 return true;
458 }
459
460 @Override
461 public void onPress() {
462 // shutdown by making sure radio and power are handled accordingly.
463 mWindowManagerFuncs.shutdown();
464 }
465 }
466
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800467 private class EmergencyDialerAction extends SinglePressAction {
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800468 private EmergencyDialerAction() {
469 super(R.drawable.ic_faster_emergency,
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +0800470 R.string.global_action_emergency);
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800471 }
472
473 @Override
474 public void onPress() {
Shaotang Li786da902018-08-02 11:18:00 +0800475 MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
Shaotang Li5c422632018-07-04 14:18:40 +0800476 Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800477 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Shaotang Li5c422632018-07-04 14:18:40 +0800478 intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
479 EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800480 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
481 }
482
483 @Override
484 public boolean showDuringKeyguard() {
485 return true;
486 }
487
488 @Override
489 public boolean showBeforeProvisioning() {
490 return true;
491 }
Aaron Heuckrothf708d472019-01-10 16:54:51 -0500492
493 @Override
494 public boolean shouldBeSeparated() {
495 return true;
496 }
Wesley.CW Wang8d072762018-05-28 16:39:27 +0800497 }
498
Jason Monk361915c2017-03-21 20:33:59 -0400499 private final class RestartAction extends SinglePressAction implements LongPressAction {
500 private RestartAction() {
501 super(R.drawable.ic_restart, R.string.global_action_restart);
502 }
503
504 @Override
505 public boolean onLongPress() {
506 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
507 if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
508 mWindowManagerFuncs.reboot(true);
509 return true;
510 }
511 return false;
512 }
513
514 @Override
515 public boolean showDuringKeyguard() {
516 return true;
517 }
518
519 @Override
520 public boolean showBeforeProvisioning() {
521 return true;
522 }
523
524 @Override
525 public void onPress() {
526 mWindowManagerFuncs.reboot(false);
527 }
528 }
529
530
Beth Thibodeau5898ac42018-10-26 13:00:09 -0400531 private class ScreenshotAction extends SinglePressAction implements LongPressAction {
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500532 public ScreenshotAction() {
533 super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
534 }
535
536 @Override
537 public void onPress() {
538 // Add a little delay before executing, to give the
539 // dialog a chance to go away before it takes a
540 // screenshot.
541 // TODO: instead, omit global action dialog layer
542 mHandler.postDelayed(new Runnable() {
543 @Override
544 public void run() {
545 mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
546 MetricsLogger.action(mContext,
547 MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
548 }
549 }, 500);
550 }
551
552 @Override
553 public boolean showDuringKeyguard() {
554 return true;
555 }
556
557 @Override
558 public boolean showBeforeProvisioning() {
559 return false;
560 }
Beth Thibodeau5898ac42018-10-26 13:00:09 -0400561
562 @Override
563 public boolean onLongPress() {
564 if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
565 mScreenRecordHelper.launchRecordPrompt();
566 } else {
567 onPress();
568 }
569 return true;
570 }
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -0500571 }
572
Jason Monk361915c2017-03-21 20:33:59 -0400573 private class BugReportAction extends SinglePressAction implements LongPressAction {
574
575 public BugReportAction() {
576 super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
577 }
578
579 @Override
580 public void onPress() {
581 // don't actually trigger the bugreport if we are running stability
582 // tests via monkey
583 if (ActivityManager.isUserAMonkey()) {
584 return;
585 }
586 // Add a little delay before executing, to give the
587 // dialog a chance to go away before it takes a
588 // screenshot.
589 mHandler.postDelayed(new Runnable() {
590 @Override
591 public void run() {
592 try {
593 // Take an "interactive" bugreport.
594 MetricsLogger.action(mContext,
595 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
596 ActivityManager.getService().requestBugReport(
597 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
598 } catch (RemoteException e) {
599 }
600 }
601 }, 500);
602 }
603
604 @Override
605 public boolean onLongPress() {
606 // don't actually trigger the bugreport if we are running stability
607 // tests via monkey
608 if (ActivityManager.isUserAMonkey()) {
609 return false;
610 }
611 try {
612 // Take a "full" bugreport.
613 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
614 ActivityManager.getService().requestBugReport(
615 ActivityManager.BUGREPORT_OPTION_FULL);
616 } catch (RemoteException e) {
617 }
618 return false;
619 }
620
621 public boolean showDuringKeyguard() {
622 return true;
623 }
624
625 @Override
626 public boolean showBeforeProvisioning() {
627 return false;
628 }
629
630 @Override
631 public String getStatus() {
632 return mContext.getString(
633 R.string.bugreport_status,
634 Build.VERSION.RELEASE,
635 Build.ID);
636 }
637 }
638
Alex Chau04458852017-11-27 18:21:23 +0000639 private final class LogoutAction extends SinglePressAction {
640 private LogoutAction() {
641 super(R.drawable.ic_logout, R.string.global_action_logout);
642 }
643
644 @Override
645 public boolean showDuringKeyguard() {
646 return true;
647 }
648
649 @Override
650 public boolean showBeforeProvisioning() {
651 return false;
652 }
653
654 @Override
655 public void onPress() {
656 // Add a little delay before executing, to give the dialog a chance to go away before
657 // switching user
658 mHandler.postDelayed(() -> {
659 try {
Alex Chauedb6a012018-01-26 12:52:43 +0000660 int currentUserId = getCurrentUser().id;
Alex Chau04458852017-11-27 18:21:23 +0000661 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
Alex Chauedb6a012018-01-26 12:52:43 +0000662 ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
Alex Chau04458852017-11-27 18:21:23 +0000663 } catch (RemoteException re) {
664 Log.e(TAG, "Couldn't logout user " + re);
665 }
666 }, 500);
667 }
668 }
669
Jason Monk361915c2017-03-21 20:33:59 -0400670 private Action getSettingsAction() {
671 return new SinglePressAction(R.drawable.ic_settings,
672 R.string.global_action_settings) {
673
674 @Override
675 public void onPress() {
676 Intent intent = new Intent(Settings.ACTION_SETTINGS);
677 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
678 mContext.startActivity(intent);
679 }
680
681 @Override
682 public boolean showDuringKeyguard() {
683 return true;
684 }
685
686 @Override
687 public boolean showBeforeProvisioning() {
688 return true;
689 }
690 };
691 }
692
693 private Action getEmergencyAction() {
Wesley.CW Wanga9de7922018-07-06 15:26:21 +0800694 Drawable emergencyIcon = mContext.getDrawable(R.drawable.emergency_icon);
695 if(!mSeparatedEmergencyButtonEnabled) {
696 // use un-colored legacy treatment
697 emergencyIcon.setTintList(null);
698 }
699
Jason Monk361915c2017-03-21 20:33:59 -0400700 return new SinglePressAction(R.drawable.emergency_icon,
701 R.string.global_action_emergency) {
702 @Override
703 public void onPress() {
704 mEmergencyAffordanceManager.performEmergencyCall();
705 }
706
707 @Override
708 public boolean showDuringKeyguard() {
709 return true;
710 }
711
712 @Override
713 public boolean showBeforeProvisioning() {
714 return true;
715 }
716 };
717 }
718
719 private Action getAssistAction() {
720 return new SinglePressAction(R.drawable.ic_action_assist_focused,
721 R.string.global_action_assist) {
722 @Override
723 public void onPress() {
724 Intent intent = new Intent(Intent.ACTION_ASSIST);
725 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
726 mContext.startActivity(intent);
727 }
728
729 @Override
730 public boolean showDuringKeyguard() {
731 return true;
732 }
733
734 @Override
735 public boolean showBeforeProvisioning() {
736 return true;
737 }
738 };
739 }
740
741 private Action getVoiceAssistAction() {
742 return new SinglePressAction(R.drawable.ic_voice_search,
743 R.string.global_action_voice_assist) {
744 @Override
745 public void onPress() {
746 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
747 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
748 mContext.startActivity(intent);
749 }
750
751 @Override
752 public boolean showDuringKeyguard() {
753 return true;
754 }
755
756 @Override
757 public boolean showBeforeProvisioning() {
758 return true;
759 }
760 };
761 }
762
763 private Action getLockdownAction() {
Alison Cichowlas21125432018-05-16 15:40:45 -0400764 return new SinglePressAction(R.drawable.ic_lock_lockdown,
Jason Monk361915c2017-03-21 20:33:59 -0400765 R.string.global_action_lockdown) {
766
767 @Override
768 public void onPress() {
Chad Brubaker4f28f0d2017-09-07 14:28:13 -0700769 new LockPatternUtils(mContext)
770 .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
771 UserHandle.USER_ALL);
Jason Monk361915c2017-03-21 20:33:59 -0400772 try {
773 WindowManagerGlobal.getWindowManagerService().lockNow(null);
Pavel Grafov059021b2018-05-02 13:44:46 +0100774 // Lock profiles (if any) on the background thread.
775 final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
776 bgHandler.post(() -> lockProfiles());
Jason Monk361915c2017-03-21 20:33:59 -0400777 } catch (RemoteException e) {
778 Log.e(TAG, "Error while trying to lock device.", e);
779 }
780 }
781
782 @Override
783 public boolean showDuringKeyguard() {
784 return true;
785 }
786
787 @Override
788 public boolean showBeforeProvisioning() {
789 return false;
790 }
791 };
792 }
793
Pavel Grafov059021b2018-05-02 13:44:46 +0100794 private void lockProfiles() {
795 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
796 final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
797 final int currentUserId = getCurrentUser().id;
798 final int[] profileIds = um.getEnabledProfileIds(currentUserId);
799 for (final int id : profileIds) {
800 if (id != currentUserId) {
801 tm.setDeviceLockedForUser(id, true);
802 }
803 }
804 }
805
Jason Monk361915c2017-03-21 20:33:59 -0400806 private UserInfo getCurrentUser() {
807 try {
808 return ActivityManager.getService().getCurrentUser();
809 } catch (RemoteException re) {
810 return null;
811 }
812 }
813
814 private boolean isCurrentUserOwner() {
815 UserInfo currentUser = getCurrentUser();
816 return currentUser == null || currentUser.isPrimary();
817 }
818
819 private void addUsersToMenu(ArrayList<Action> items) {
820 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
821 if (um.isUserSwitcherEnabled()) {
822 List<UserInfo> users = um.getUsers();
823 UserInfo currentUser = getCurrentUser();
824 for (final UserInfo user : users) {
825 if (user.supportsSwitchToByUser()) {
826 boolean isCurrentUser = currentUser == null
827 ? user.id == 0 : (currentUser.id == user.id);
828 Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
829 : null;
830 SinglePressAction switchToUser = new SinglePressAction(
831 R.drawable.ic_menu_cc, icon,
832 (user.name != null ? user.name : "Primary")
Jason Monk16fbd9d2017-04-27 14:28:49 -0400833 + (isCurrentUser ? " \u2714" : "")) {
Jason Monk361915c2017-03-21 20:33:59 -0400834 public void onPress() {
835 try {
836 ActivityManager.getService().switchUser(user.id);
837 } catch (RemoteException re) {
838 Log.e(TAG, "Couldn't switch user " + re);
839 }
840 }
841
842 public boolean showDuringKeyguard() {
843 return true;
844 }
845
846 public boolean showBeforeProvisioning() {
847 return false;
848 }
849 };
850 items.add(switchToUser);
851 }
852 }
853 }
854 }
855
856 private void prepareDialog() {
857 refreshSilentMode();
858 mAirplaneModeOn.updateState(mAirplaneState);
859 mAdapter.notifyDataSetChanged();
Jason Monk361915c2017-03-21 20:33:59 -0400860 if (mShowSilentToggle) {
861 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
862 mContext.registerReceiver(mRingerModeReceiver, filter);
863 }
864 }
865
866 private void refreshSilentMode() {
867 if (!mHasVibrator) {
868 final boolean silentModeOn =
869 mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
Jason Monk16fbd9d2017-04-27 14:28:49 -0400870 ((ToggleAction) mSilentModeAction).updateState(
Jason Monk361915c2017-03-21 20:33:59 -0400871 silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
872 }
873 }
874
875 /** {@inheritDoc} */
876 public void onDismiss(DialogInterface dialog) {
877 mWindowManagerFuncs.onGlobalActionsHidden();
878 if (mShowSilentToggle) {
879 try {
880 mContext.unregisterReceiver(mRingerModeReceiver);
881 } catch (IllegalArgumentException ie) {
882 // ignore this
883 Log.w(TAG, ie);
884 }
885 }
886 }
887
888 /** {@inheritDoc} */
889 public void onClick(DialogInterface dialog, int which) {
Jason Monkfd279662017-06-29 19:37:48 -0400890 Action item = mAdapter.getItem(which);
Jason Monkb4302182017-08-04 13:39:17 -0400891 if (!(item instanceof SilentModeTriStateAction)) {
Jason Monk361915c2017-03-21 20:33:59 -0400892 dialog.dismiss();
893 }
Jason Monkfd279662017-06-29 19:37:48 -0400894 item.onPress();
Jason Monk361915c2017-03-21 20:33:59 -0400895 }
896
Shaotang Li786da902018-08-02 11:18:00 +0800897 /** {@inheritDoc} */
898 public void onShow(DialogInterface dialog) {
899 MetricsLogger.visible(mContext, MetricsEvent.POWER_MENU);
900 }
901
Jason Monk361915c2017-03-21 20:33:59 -0400902 /**
903 * The adapter used for the list within the global actions dialog, taking
904 * into account whether the keyguard is showing via
Jason Monk16fbd9d2017-04-27 14:28:49 -0400905 * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
906 * the device is provisioned
Jason Monk361915c2017-03-21 20:33:59 -0400907 * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
908 */
909 private class MyAdapter extends BaseAdapter {
910
911 public int getCount() {
912 int count = 0;
913
914 for (int i = 0; i < mItems.size(); i++) {
915 final Action action = mItems.get(i);
916
917 if (mKeyguardShowing && !action.showDuringKeyguard()) {
918 continue;
919 }
920 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
921 continue;
922 }
923 count++;
924 }
925 return count;
926 }
927
928 @Override
929 public boolean isEnabled(int position) {
930 return getItem(position).isEnabled();
931 }
932
Aaron Heuckrothf708d472019-01-10 16:54:51 -0500933 public ArrayList<Action> getSeparatedActions(boolean shouldUseSeparatedView) {
934 ArrayList<Action> separatedActions = new ArrayList<Action>();
935 if (!shouldUseSeparatedView) {
936 return separatedActions;
937 }
938 for (int i = 0; i < mItems.size(); i++) {
939 final Action action = mItems.get(i);
940 if (action.shouldBeSeparated()) {
941 separatedActions.add(action);
942 }
943 }
944 return separatedActions;
945 }
946
947 public ArrayList<Action> getListActions(boolean shouldUseSeparatedView) {
948 if (!shouldUseSeparatedView) {
949 return new ArrayList<Action>(mItems);
950 }
951 ArrayList<Action> listActions = new ArrayList<Action>();
952 for (int i = 0; i < mItems.size(); i++) {
953 final Action action = mItems.get(i);
954 if (!action.shouldBeSeparated()) {
955 listActions.add(action);
956 }
957 }
958 return listActions;
959 }
960
Jason Monk361915c2017-03-21 20:33:59 -0400961 @Override
962 public boolean areAllItemsEnabled() {
963 return false;
964 }
965
966 public Action getItem(int position) {
967
968 int filteredPos = 0;
969 for (int i = 0; i < mItems.size(); i++) {
970 final Action action = mItems.get(i);
971 if (mKeyguardShowing && !action.showDuringKeyguard()) {
972 continue;
973 }
974 if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
975 continue;
976 }
977 if (filteredPos == position) {
978 return action;
979 }
980 filteredPos++;
981 }
982
983 throw new IllegalArgumentException("position " + position
984 + " out of range of showable actions"
985 + ", filtered count=" + getCount()
986 + ", keyguardshowing=" + mKeyguardShowing
987 + ", provisioned=" + mDeviceProvisioned);
988 }
989
990
991 public long getItemId(int position) {
992 return position;
993 }
994
995 public View getView(int position, View convertView, ViewGroup parent) {
996 Action action = getItem(position);
Jason Monk16fbd9d2017-04-27 14:28:49 -0400997 View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
Alison Cichowlas3be52db2018-03-06 19:48:06 -0500998 // Everything but screenshot, the last item, gets white background.
999 if (position == getCount() - 1) {
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001000 MultiListLayout.get(parent).setDivisionView(view);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001001 }
1002 return view;
Jason Monk361915c2017-03-21 20:33:59 -04001003 }
1004 }
1005
1006 // note: the scheme below made more sense when we were planning on having
1007 // 8 different things in the global actions dialog. seems overkill with
1008 // only 3 items now, but may as well keep this flexible approach so it will
1009 // be easy should someone decide at the last minute to include something
1010 // else, such as 'enable wifi', or 'enable bluetooth'
1011
1012 /**
1013 * What each item in the global actions dialog must be able to support.
1014 */
1015 private interface Action {
1016 /**
1017 * @return Text that will be announced when dialog is created. null
Jason Monk16fbd9d2017-04-27 14:28:49 -04001018 * for none.
Jason Monk361915c2017-03-21 20:33:59 -04001019 */
1020 CharSequence getLabelForAccessibility(Context context);
1021
1022 View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
1023
1024 void onPress();
1025
1026 /**
1027 * @return whether this action should appear in the dialog when the keygaurd
Jason Monk16fbd9d2017-04-27 14:28:49 -04001028 * is showing.
Jason Monk361915c2017-03-21 20:33:59 -04001029 */
1030 boolean showDuringKeyguard();
1031
1032 /**
1033 * @return whether this action should appear in the dialog before the
Jason Monk16fbd9d2017-04-27 14:28:49 -04001034 * device is provisioned.
Jason Monk361915c2017-03-21 20:33:59 -04001035 */
1036 boolean showBeforeProvisioning();
1037
1038 boolean isEnabled();
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001039
1040 default boolean shouldBeSeparated() {
1041 return false;
1042 }
Jason Monk361915c2017-03-21 20:33:59 -04001043 }
1044
1045 /**
1046 * An action that also supports long press.
1047 */
1048 private interface LongPressAction extends Action {
1049 boolean onLongPress();
1050 }
1051
1052 /**
1053 * A single press action maintains no state, just responds to a press
1054 * and takes an action.
1055 */
1056 private static abstract class SinglePressAction implements Action {
1057 private final int mIconResId;
1058 private final Drawable mIcon;
1059 private final int mMessageResId;
1060 private final CharSequence mMessage;
1061
1062 protected SinglePressAction(int iconResId, int messageResId) {
1063 mIconResId = iconResId;
1064 mMessageResId = messageResId;
1065 mMessage = null;
1066 mIcon = null;
1067 }
1068
1069 protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
1070 mIconResId = iconResId;
1071 mMessageResId = 0;
1072 mMessage = message;
1073 mIcon = icon;
1074 }
1075
1076 public boolean isEnabled() {
1077 return true;
1078 }
1079
1080 public String getStatus() {
1081 return null;
1082 }
1083
1084 abstract public void onPress();
1085
1086 public CharSequence getLabelForAccessibility(Context context) {
1087 if (mMessage != null) {
1088 return mMessage;
1089 } else {
1090 return context.getString(mMessageResId);
1091 }
1092 }
1093
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001094 protected int getActionLayoutId(Context context) {
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001095 if (isGridEnabled(context)) {
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001096 return com.android.systemui.R.layout.global_actions_grid_item;
1097 }
1098 return com.android.systemui.R.layout.global_actions_item;
1099 }
1100
Jason Monk361915c2017-03-21 20:33:59 -04001101 public View create(
1102 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001103 View v = inflater.inflate(getActionLayoutId(context), parent,
Jason Monk16fbd9d2017-04-27 14:28:49 -04001104 false);
Jason Monk361915c2017-03-21 20:33:59 -04001105
1106 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1107 TextView messageView = (TextView) v.findViewById(R.id.message);
1108
1109 TextView statusView = (TextView) v.findViewById(R.id.status);
1110 final String status = getStatus();
1111 if (!TextUtils.isEmpty(status)) {
1112 statusView.setText(status);
1113 } else {
1114 statusView.setVisibility(View.GONE);
1115 }
1116 if (mIcon != null) {
1117 icon.setImageDrawable(mIcon);
1118 icon.setScaleType(ScaleType.CENTER_CROP);
1119 } else if (mIconResId != 0) {
1120 icon.setImageDrawable(context.getDrawable(mIconResId));
1121 }
1122 if (mMessage != null) {
1123 messageView.setText(mMessage);
1124 } else {
1125 messageView.setText(mMessageResId);
1126 }
1127
1128 return v;
1129 }
1130 }
1131
1132 /**
1133 * A toggle action knows whether it is on or off, and displays an icon
1134 * and status message accordingly.
1135 */
1136 private static abstract class ToggleAction implements Action {
1137
1138 enum State {
1139 Off(false),
1140 TurningOn(true),
1141 TurningOff(true),
1142 On(false);
1143
1144 private final boolean inTransition;
1145
1146 State(boolean intermediate) {
1147 inTransition = intermediate;
1148 }
1149
1150 public boolean inTransition() {
1151 return inTransition;
1152 }
1153 }
1154
1155 protected State mState = State.Off;
1156
1157 // prefs
1158 protected int mEnabledIconResId;
1159 protected int mDisabledIconResid;
1160 protected int mMessageResId;
1161 protected int mEnabledStatusMessageResId;
1162 protected int mDisabledStatusMessageResId;
1163
1164 /**
Jason Monk16fbd9d2017-04-27 14:28:49 -04001165 * @param enabledIconResId The icon for when this action is on.
1166 * @param disabledIconResid The icon for when this action is off.
1167 * @param message The general information message, e.g 'Silent Mode'
1168 * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
Jason Monk361915c2017-03-21 20:33:59 -04001169 * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
1170 */
1171 public ToggleAction(int enabledIconResId,
1172 int disabledIconResid,
1173 int message,
1174 int enabledStatusMessageResId,
1175 int disabledStatusMessageResId) {
1176 mEnabledIconResId = enabledIconResId;
1177 mDisabledIconResid = disabledIconResid;
1178 mMessageResId = message;
1179 mEnabledStatusMessageResId = enabledStatusMessageResId;
1180 mDisabledStatusMessageResId = disabledStatusMessageResId;
1181 }
1182
1183 /**
1184 * Override to make changes to resource IDs just before creating the
1185 * View.
1186 */
1187 void willCreate() {
1188
1189 }
1190
1191 @Override
1192 public CharSequence getLabelForAccessibility(Context context) {
1193 return context.getString(mMessageResId);
1194 }
1195
1196 public View create(Context context, View convertView, ViewGroup parent,
1197 LayoutInflater inflater) {
1198 willCreate();
1199
1200 View v = inflater.inflate(R
Jason Monk16fbd9d2017-04-27 14:28:49 -04001201 .layout.global_actions_item, parent, false);
Jason Monk361915c2017-03-21 20:33:59 -04001202
1203 ImageView icon = (ImageView) v.findViewById(R.id.icon);
1204 TextView messageView = (TextView) v.findViewById(R.id.message);
1205 TextView statusView = (TextView) v.findViewById(R.id.status);
1206 final boolean enabled = isEnabled();
1207
1208 if (messageView != null) {
1209 messageView.setText(mMessageResId);
1210 messageView.setEnabled(enabled);
1211 }
1212
1213 boolean on = ((mState == State.On) || (mState == State.TurningOn));
1214 if (icon != null) {
1215 icon.setImageDrawable(context.getDrawable(
1216 (on ? mEnabledIconResId : mDisabledIconResid)));
1217 icon.setEnabled(enabled);
1218 }
1219
1220 if (statusView != null) {
1221 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
1222 statusView.setVisibility(View.VISIBLE);
1223 statusView.setEnabled(enabled);
1224 }
1225 v.setEnabled(enabled);
1226
1227 return v;
1228 }
1229
1230 public final void onPress() {
1231 if (mState.inTransition()) {
1232 Log.w(TAG, "shouldn't be able to toggle when in transition");
1233 return;
1234 }
1235
1236 final boolean nowOn = !(mState == State.On);
1237 onToggle(nowOn);
1238 changeStateFromPress(nowOn);
1239 }
1240
1241 public boolean isEnabled() {
1242 return !mState.inTransition();
1243 }
1244
1245 /**
1246 * Implementations may override this if their state can be in on of the intermediate
1247 * states until some notification is received (e.g airplane mode is 'turning off' until
1248 * we know the wireless connections are back online
Jason Monk16fbd9d2017-04-27 14:28:49 -04001249 *
Jason Monk361915c2017-03-21 20:33:59 -04001250 * @param buttonOn Whether the button was turned on or off
1251 */
1252 protected void changeStateFromPress(boolean buttonOn) {
1253 mState = buttonOn ? State.On : State.Off;
1254 }
1255
1256 abstract void onToggle(boolean on);
1257
1258 public void updateState(State state) {
1259 mState = state;
1260 }
1261 }
1262
1263 private class SilentModeToggleAction extends ToggleAction {
1264 public SilentModeToggleAction() {
1265 super(R.drawable.ic_audio_vol_mute,
1266 R.drawable.ic_audio_vol,
1267 R.string.global_action_toggle_silent_mode,
1268 R.string.global_action_silent_mode_on_status,
1269 R.string.global_action_silent_mode_off_status);
1270 }
1271
1272 void onToggle(boolean on) {
1273 if (on) {
1274 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1275 } else {
1276 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1277 }
1278 }
1279
1280 public boolean showDuringKeyguard() {
1281 return true;
1282 }
1283
1284 public boolean showBeforeProvisioning() {
1285 return false;
1286 }
1287 }
1288
1289 private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1290
Jason Monk16fbd9d2017-04-27 14:28:49 -04001291 private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
Jason Monk361915c2017-03-21 20:33:59 -04001292
1293 private final AudioManager mAudioManager;
1294 private final Handler mHandler;
Jason Monk361915c2017-03-21 20:33:59 -04001295
Lucas Dupin40ec6b782018-06-05 19:07:16 -07001296 SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
Jason Monk361915c2017-03-21 20:33:59 -04001297 mAudioManager = audioManager;
1298 mHandler = handler;
Jason Monk361915c2017-03-21 20:33:59 -04001299 }
1300
1301 private int ringerModeToIndex(int ringerMode) {
1302 // They just happen to coincide
1303 return ringerMode;
1304 }
1305
1306 private int indexToRingerMode(int index) {
1307 // They just happen to coincide
1308 return index;
1309 }
1310
1311 @Override
1312 public CharSequence getLabelForAccessibility(Context context) {
1313 return null;
1314 }
1315
1316 public View create(Context context, View convertView, ViewGroup parent,
1317 LayoutInflater inflater) {
1318 View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
1319
1320 int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
1321 for (int i = 0; i < 3; i++) {
1322 View itemView = v.findViewById(ITEM_IDS[i]);
1323 itemView.setSelected(selectedIndex == i);
1324 // Set up click handler
1325 itemView.setTag(i);
1326 itemView.setOnClickListener(this);
1327 }
1328 return v;
1329 }
1330
1331 public void onPress() {
1332 }
1333
1334 public boolean showDuringKeyguard() {
1335 return true;
1336 }
1337
1338 public boolean showBeforeProvisioning() {
1339 return false;
1340 }
1341
1342 public boolean isEnabled() {
1343 return true;
1344 }
1345
1346 void willCreate() {
1347 }
1348
1349 public void onClick(View v) {
1350 if (!(v.getTag() instanceof Integer)) return;
1351
1352 int index = (Integer) v.getTag();
1353 mAudioManager.setRingerMode(indexToRingerMode(index));
1354 mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
1355 }
1356 }
1357
1358 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1359 public void onReceive(Context context, Intent intent) {
1360 String action = intent.getAction();
1361 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1362 || Intent.ACTION_SCREEN_OFF.equals(action)) {
1363 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
1364 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001365 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISMISS, reason));
Jason Monk361915c2017-03-21 20:33:59 -04001366 }
1367 } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
1368 // Airplane mode can be changed after ECM exits if airplane toggle button
1369 // is pressed during ECM mode
1370 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
1371 mIsWaitingForEcmExit) {
1372 mIsWaitingForEcmExit = false;
1373 changeAirplaneModeSystemSetting(true);
1374 }
1375 }
1376 }
1377 };
1378
1379 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1380 @Override
1381 public void onServiceStateChanged(ServiceState serviceState) {
1382 if (!mHasTelephony) return;
1383 final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
1384 mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
1385 mAirplaneModeOn.updateState(mAirplaneState);
1386 mAdapter.notifyDataSetChanged();
1387 }
1388 };
1389
1390 private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
1391 @Override
1392 public void onReceive(Context context, Intent intent) {
1393 if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1394 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
1395 }
1396 }
1397 };
1398
1399 private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
1400 @Override
1401 public void onChange(boolean selfChange) {
1402 onAirplaneModeChanged();
1403 }
1404 };
1405
1406 private static final int MESSAGE_DISMISS = 0;
1407 private static final int MESSAGE_REFRESH = 1;
1408 private static final int MESSAGE_SHOW = 2;
1409 private static final int DIALOG_DISMISS_DELAY = 300; // ms
1410
1411 private Handler mHandler = new Handler() {
1412 public void handleMessage(Message msg) {
1413 switch (msg.what) {
Jason Monk16fbd9d2017-04-27 14:28:49 -04001414 case MESSAGE_DISMISS:
1415 if (mDialog != null) {
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001416 if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
1417 mDialog.dismissImmediately();
1418 } else {
1419 mDialog.dismiss();
1420 }
Jason Monk16fbd9d2017-04-27 14:28:49 -04001421 mDialog = null;
1422 }
1423 break;
1424 case MESSAGE_REFRESH:
1425 refreshSilentMode();
1426 mAdapter.notifyDataSetChanged();
1427 break;
1428 case MESSAGE_SHOW:
1429 handleShow();
1430 break;
Jason Monk361915c2017-03-21 20:33:59 -04001431 }
1432 }
1433 };
1434
1435 private void onAirplaneModeChanged() {
1436 // Let the service state callbacks handle the state.
1437 if (mHasTelephony) return;
1438
1439 boolean airplaneModeOn = Settings.Global.getInt(
1440 mContext.getContentResolver(),
1441 Settings.Global.AIRPLANE_MODE_ON,
1442 0) == 1;
1443 mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
1444 mAirplaneModeOn.updateState(mAirplaneState);
1445 }
1446
1447 /**
1448 * Change the airplane mode system setting
1449 */
1450 private void changeAirplaneModeSystemSetting(boolean on) {
1451 Settings.Global.putInt(
1452 mContext.getContentResolver(),
1453 Settings.Global.AIRPLANE_MODE_ON,
1454 on ? 1 : 0);
1455 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1456 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1457 intent.putExtra("state", on);
1458 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1459 if (!mHasTelephony) {
1460 mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
1461 }
1462 }
1463
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001464 private static final class ActionsDialog extends Dialog implements DialogInterface,
1465 ColorExtractor.OnColorsChangedListener {
Jason Monk361915c2017-03-21 20:33:59 -04001466
Jason Monk16fbd9d2017-04-27 14:28:49 -04001467 private final Context mContext;
1468 private final MyAdapter mAdapter;
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001469 private MultiListLayout mGlobalActionsLayout;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001470 private final OnClickListener mClickListener;
1471 private final OnItemLongClickListener mLongClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001472 private final GradientDrawable mGradientDrawable;
1473 private final ColorExtractor mColorExtractor;
1474 private boolean mKeyguardShowing;
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001475 private boolean mShouldDisplaySeparatedButton;
Beverly526d2d62018-08-15 12:55:33 -04001476 private boolean mShowing;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001477
1478 public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001479 OnItemLongClickListener longClickListener, boolean shouldDisplaySeparatedButton) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001480 super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
Lucas Dupin448786c2017-07-24 17:44:25 -07001481 mContext = context;
Jason Monk16fbd9d2017-04-27 14:28:49 -04001482 mAdapter = adapter;
1483 mClickListener = clickListener;
1484 mLongClickListener = longClickListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001485 mGradientDrawable = new GradientDrawable(mContext);
Lucas Dupin1ead7fc2017-05-24 14:14:44 -07001486 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
Wesley.CW Wang3004fcb2018-06-15 16:24:57 +08001487 mShouldDisplaySeparatedButton = shouldDisplaySeparatedButton;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001488
1489 // Window initialization
1490 Window window = getWindow();
1491 window.requestFeature(Window.FEATURE_NO_TITLE);
Adrian Roosedfab3b2018-03-08 18:39:20 +01001492 // Inflate the decor view, so the attributes below are not overwritten by the theme.
1493 window.getDecorView();
1494 window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
1495 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
1496 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
1497 window.setLayout(MATCH_PARENT, MATCH_PARENT);
1498 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
Alison Cichowlas4f19f4a2017-07-25 10:56:16 -04001499 window.addFlags(
1500 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001501 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Adrian Roosedfab3b2018-03-08 18:39:20 +01001502 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001503 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
1504 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1505 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
1506 window.setBackgroundDrawable(mGradientDrawable);
1507 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
1508
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001509 initializeLayout();
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001510
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001511 setTitle(R.string.global_actions);
1512 }
1513
1514 private void initializeLayout() {
1515 setContentView(getGlobalActionsLayoutId(mContext));
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001516 mGlobalActionsLayout = (MultiListLayout)
1517 findViewById(com.android.systemui.R.id.global_actions_view);
1518 mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
1519 mGlobalActionsLayout.setHasSeparatedView(mShouldDisplaySeparatedButton);
1520 mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
Phil Weaver9054e092018-04-27 16:28:50 -07001521 @Override
1522 public boolean dispatchPopulateAccessibilityEvent(
1523 View host, AccessibilityEvent event) {
1524 // Populate the title here, just as Activity does
1525 event.getText().add(mContext.getString(R.string.global_actions));
1526 return true;
1527 }
1528 });
Aaron Heuckroth4ea2fdb2019-02-14 16:28:35 -05001529 mGlobalActionsLayout.setRotationListener(this::onRotate);
Jason Monk361915c2017-03-21 20:33:59 -04001530 }
1531
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001532 private int getGlobalActionsLayoutId(Context context) {
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001533 if (isGridEnabled(context)) {
1534 if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
1535 return com.android.systemui.R.layout.global_actions_grid_seascape;
1536 }
Aaron Heuckroth166392f2019-01-17 16:50:59 -05001537 return com.android.systemui.R.layout.global_actions_grid;
1538 }
1539 return com.android.systemui.R.layout.global_actions_wrapped;
1540 }
1541
Jason Monk16fbd9d2017-04-27 14:28:49 -04001542 private void updateList() {
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001543 mGlobalActionsLayout.removeAllItems();
1544 ArrayList<Action> separatedActions =
1545 mAdapter.getSeparatedActions(mShouldDisplaySeparatedButton);
1546 ArrayList<Action> listActions = mAdapter.getListActions(mShouldDisplaySeparatedButton);
1547 mGlobalActionsLayout.setExpectedListItemCount(listActions.size());
1548 mGlobalActionsLayout.setExpectedSeparatedItemCount(separatedActions.size());
1549
Jason Monk16fbd9d2017-04-27 14:28:49 -04001550 for (int i = 0; i < mAdapter.getCount(); i++) {
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001551 Action action = mAdapter.getItem(i);
1552 int separatedIndex = separatedActions.indexOf(action);
1553 ViewGroup parent;
1554 if (separatedIndex != -1) {
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001555 parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false);
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001556 } else {
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001557 boolean reverse = false;
1558
1559 // If we're using the grid layout and we're in seascape, reverse the order
1560 // of sublists to make sure they render in the correct positions,
1561 // since we can't reverse vertical LinearLayouts through the layout xml.
1562
1563 if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext)
1564 == RotationUtils.ROTATION_SEASCAPE) {
1565 reverse = true;
1566 }
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001567 int listIndex = listActions.indexOf(action);
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001568 parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse);
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001569 }
1570 View v = mAdapter.getView(i, null, parent);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001571 final int pos = i;
1572 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
1573 v.setOnLongClickListener(view ->
1574 mLongClickListener.onItemLongClick(null, v, pos, 0));
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001575 parent.addView(v);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001576 }
Jason Monk361915c2017-03-21 20:33:59 -04001577 }
1578
1579 @Override
1580 protected void onStart() {
1581 super.setCanceledOnTouchOutside(true);
1582 super.onStart();
Jason Monk16fbd9d2017-04-27 14:28:49 -04001583 updateList();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001584
1585 Point displaySize = new Point();
1586 mContext.getDisplay().getRealSize(displaySize);
1587 mColorExtractor.addOnColorsChangedListener(this);
1588 mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
1589 GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
1590 WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001591 updateColors(colors, false /* animate */);
1592 }
1593
1594 /**
1595 * Updates background and system bars according to current GradientColors.
1596 * @param colors Colors and hints to use.
1597 * @param animate Interpolates gradient if true, just sets otherwise.
1598 */
1599 private void updateColors(GradientColors colors, boolean animate) {
1600 mGradientDrawable.setColors(colors, animate);
1601 View decorView = getWindow().getDecorView();
1602 if (colors.supportsDarkText()) {
1603 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
1604 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
1605 } else {
1606 decorView.setSystemUiVisibility(0);
1607 }
Jason Monk361915c2017-03-21 20:33:59 -04001608 }
1609
1610 @Override
Jason Monk16fbd9d2017-04-27 14:28:49 -04001611 protected void onStop() {
1612 super.onStop();
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001613 mColorExtractor.removeOnColorsChangedListener(this);
Jason Monk16fbd9d2017-04-27 14:28:49 -04001614 }
1615
1616 @Override
1617 public void show() {
1618 super.show();
Beverly526d2d62018-08-15 12:55:33 -04001619 mShowing = true;
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001620 mGradientDrawable.setAlpha(0);
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001621 mGlobalActionsLayout.setTranslationX(getAnimTranslation());
1622 mGlobalActionsLayout.setAlpha(0);
1623 mGlobalActionsLayout.animate()
Jason Monk16fbd9d2017-04-27 14:28:49 -04001624 .alpha(1)
1625 .translationX(0)
1626 .setDuration(300)
Lucas Dupinde9db422017-07-19 17:15:41 -07001627 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001628 .setUpdateListener(animation -> {
1629 int alpha = (int) ((Float) animation.getAnimatedValue()
1630 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1631 mGradientDrawable.setAlpha(alpha);
1632 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001633 .start();
1634 }
1635
1636 @Override
1637 public void dismiss() {
Beverly526d2d62018-08-15 12:55:33 -04001638 if (!mShowing) {
1639 return;
1640 }
1641 mShowing = false;
Aaron Heuckrothf708d472019-01-10 16:54:51 -05001642 mGlobalActionsLayout.setTranslationX(0);
1643 mGlobalActionsLayout.setAlpha(1);
1644 mGlobalActionsLayout.animate()
Jason Monk16fbd9d2017-04-27 14:28:49 -04001645 .alpha(0)
1646 .translationX(getAnimTranslation())
1647 .setDuration(300)
1648 .withEndAction(() -> super.dismiss())
1649 .setInterpolator(new LogAccelerateInterpolator())
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001650 .setUpdateListener(animation -> {
1651 int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
1652 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
1653 mGradientDrawable.setAlpha(alpha);
1654 })
Jason Monk16fbd9d2017-04-27 14:28:49 -04001655 .start();
1656 }
1657
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001658 void dismissImmediately() {
1659 super.dismiss();
Beverly526d2d62018-08-15 12:55:33 -04001660 mShowing = false;
Lucas Dupin1d4a5792018-04-02 15:14:59 -07001661 }
1662
Jason Monk16fbd9d2017-04-27 14:28:49 -04001663 private float getAnimTranslation() {
1664 return getContext().getResources().getDimension(
1665 com.android.systemui.R.dimen.global_actions_panel_width) / 2;
Jason Monk361915c2017-03-21 20:33:59 -04001666 }
1667
1668 @Override
Lucas Dupin7aaa3532017-05-28 08:51:07 -07001669 public void onColorsChanged(ColorExtractor extractor, int which) {
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001670 if (mKeyguardShowing) {
1671 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001672 updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
1673 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001674 }
1675 } else {
1676 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
Lucas Dupinb5f59fe2017-09-14 17:09:39 -07001677 updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
1678 true /* animate */);
Lucas Dupinc1cc7592017-05-22 15:56:16 -07001679 }
1680 }
1681 }
1682
1683 public void setKeyguardShowing(boolean keyguardShowing) {
1684 mKeyguardShowing = keyguardShowing;
1685 }
Aaron Heuckroth4ea2fdb2019-02-14 16:28:35 -05001686
1687 public void onRotate(int from, int to) {
1688 if (mShowing && isGridEnabled(mContext)) {
1689 initializeLayout();
1690 updateList();
1691 }
1692 }
Jason Monk361915c2017-03-21 20:33:59 -04001693 }
Aaron Heuckroth75e249f2019-02-01 15:59:57 -05001694
1695 /**
1696 * Determines whether or not the Global Actions Dialog should use the newer grid-style layout.
1697 */
1698 public static boolean isGridEnabled(Context context) {
1699 return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED);
1700 }
Jason Monk361915c2017-03-21 20:33:59 -04001701}