1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.accessibility; 18 19 import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; 20 import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled; 21 import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.app.Activity; 25 import android.app.Dialog; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.DialogInterface; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.ApplicationInfo; 36 import android.content.pm.ResolveInfo; 37 import android.content.pm.ServiceInfo; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.UserHandle; 42 import android.os.storage.StorageManager; 43 import android.provider.Settings; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.view.Menu; 47 import android.view.MenuInflater; 48 import android.view.View; 49 import android.view.accessibility.AccessibilityManager; 50 import android.widget.Switch; 51 52 import androidx.annotation.Nullable; 53 54 import com.android.internal.widget.LockPatternUtils; 55 import com.android.settings.R; 56 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; 57 import com.android.settings.password.ConfirmDeviceCredentialActivity; 58 import com.android.settings.widget.SettingsMainSwitchPreference; 59 import com.android.settingslib.accessibility.AccessibilityUtils; 60 61 import java.util.List; 62 import java.util.concurrent.atomic.AtomicBoolean; 63 64 /** Fragment for providing toggle bar and basic accessibility service setup. */ 65 public class ToggleAccessibilityServicePreferenceFragment extends 66 ToggleFeaturePreferenceFragment { 67 68 private static final String TAG = "ToggleAccessibilityServicePreferenceFragment"; 69 private static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1; 70 private LockPatternUtils mLockPatternUtils; 71 private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false); 72 73 private static final String EMPTY_STRING = ""; 74 75 private final SettingsContentObserver mSettingsContentObserver = 76 new SettingsContentObserver(new Handler()) { 77 @Override 78 public void onChange(boolean selfChange, Uri uri) { 79 updateSwitchBarToggleSwitch(); 80 } 81 }; 82 83 private Dialog mDialog; 84 private BroadcastReceiver mPackageRemovedReceiver; 85 86 @Override getMetricsCategory()87 public int getMetricsCategory() { 88 return SettingsEnums.ACCESSIBILITY_SERVICE; 89 } 90 91 @Override onCreateOptionsMenu(Menu menu, MenuInflater infalter)92 public void onCreateOptionsMenu(Menu menu, MenuInflater infalter) { 93 // Do not call super. We don't want to see the "Help & feedback" option on this page so as 94 // not to confuse users who think they might be able to send feedback about a specific 95 // accessibility service from this page. 96 } 97 98 @Override onCreate(Bundle savedInstanceState)99 public void onCreate(Bundle savedInstanceState) { 100 super.onCreate(savedInstanceState); 101 mLockPatternUtils = new LockPatternUtils(getPrefContext()); 102 } 103 104 @Override onStart()105 public void onStart() { 106 super.onStart(); 107 final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo(); 108 if (serviceInfo == null) { 109 getActivity().finishAndRemoveTask(); 110 } else if (!AccessibilityUtil.isSystemApp(serviceInfo)) { 111 registerPackageRemoveReceiver(); 112 } 113 } 114 115 @Override onResume()116 public void onResume() { 117 super.onResume(); 118 updateSwitchBarToggleSwitch(); 119 mSettingsContentObserver.register(getContentResolver()); 120 } 121 122 @Override onPreferenceToggled(String preferenceKey, boolean enabled)123 public void onPreferenceToggled(String preferenceKey, boolean enabled) { 124 ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey); 125 logAccessibilityServiceEnabled(toggledService, enabled); 126 AccessibilityUtils.setAccessibilityServiceState(getPrefContext(), toggledService, enabled); 127 } 128 129 // IMPORTANT: Refresh the info since there are dynamically changing 130 // capabilities. For 131 // example, before JellyBean MR2 the user was granting the explore by touch 132 // one. 133 @Nullable getAccessibilityServiceInfo()134 AccessibilityServiceInfo getAccessibilityServiceInfo() { 135 final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance( 136 getPrefContext()).getInstalledAccessibilityServiceList(); 137 138 for (int i = 0, count = infos.size(); i < count; i++) { 139 AccessibilityServiceInfo serviceInfo = infos.get(i); 140 ResolveInfo resolveInfo = serviceInfo.getResolveInfo(); 141 if (mComponentName.getPackageName().equals(resolveInfo.serviceInfo.packageName) 142 && mComponentName.getClassName().equals(resolveInfo.serviceInfo.name)) { 143 return serviceInfo; 144 } 145 } 146 return null; 147 } 148 149 @Override onCreateDialog(int dialogId)150 public Dialog onCreateDialog(int dialogId) { 151 switch (dialogId) { 152 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: { 153 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 154 if (info == null) { 155 return null; 156 } 157 mDialog = AccessibilityServiceWarning 158 .createCapabilitiesDialog(getPrefContext(), info, 159 this::onDialogButtonFromEnableToggleClicked, 160 this::onDialogButtonFromUninstallClicked); 161 break; 162 } 163 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: { 164 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 165 if (info == null) { 166 return null; 167 } 168 mDialog = AccessibilityServiceWarning 169 .createCapabilitiesDialog(getPrefContext(), info, 170 this::onDialogButtonFromShortcutToggleClicked, 171 this::onDialogButtonFromUninstallClicked); 172 break; 173 } 174 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: { 175 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 176 if (info == null) { 177 return null; 178 } 179 mDialog = AccessibilityServiceWarning 180 .createCapabilitiesDialog(getPrefContext(), info, 181 this::onDialogButtonFromShortcutClicked, 182 this::onDialogButtonFromUninstallClicked); 183 break; 184 } 185 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: { 186 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 187 if (info == null) { 188 return null; 189 } 190 mDialog = AccessibilityServiceWarning 191 .createDisableDialog(getPrefContext(), info, 192 this::onDialogButtonFromDisableToggleClicked); 193 break; 194 } 195 default: { 196 mDialog = super.onCreateDialog(dialogId); 197 } 198 } 199 return mDialog; 200 } 201 202 @Override getDialogMetricsCategory(int dialogId)203 public int getDialogMetricsCategory(int dialogId) { 204 switch (dialogId) { 205 case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: 206 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: 207 case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: 208 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_ENABLE; 209 case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: 210 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_DISABLE; 211 case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: 212 return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL; 213 default: 214 return super.getDialogMetricsCategory(dialogId); 215 } 216 } 217 218 @Override getUserShortcutTypes()219 int getUserShortcutTypes() { 220 return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), 221 mComponentName); 222 } 223 224 @Override updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference)225 protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) { 226 final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 227 final String switchBarText = (info == null) ? "" : 228 getString(R.string.accessibility_service_primary_switch_title, 229 info.getResolveInfo().loadLabel(getPackageManager())); 230 switchPreference.setTitle(switchBarText); 231 } 232 233 @Override updateSwitchBarToggleSwitch()234 protected void updateSwitchBarToggleSwitch() { 235 final boolean checked = isAccessibilityServiceEnabled(); 236 if (mToggleServiceSwitchPreference.isChecked() == checked) { 237 return; 238 } 239 mToggleServiceSwitchPreference.setChecked(checked); 240 } 241 isAccessibilityServiceEnabled()242 private boolean isAccessibilityServiceEnabled() { 243 return AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext()) 244 .contains(mComponentName); 245 } 246 247 /** 248 * Return whether the device is encrypted with legacy full disk encryption. Newer devices 249 * should be using File Based Encryption. 250 * 251 * @return true if device is encrypted 252 */ isFullDiskEncrypted()253 private boolean isFullDiskEncrypted() { 254 return StorageManager.isNonDefaultBlockEncrypted(); 255 } 256 257 @Override onActivityResult(int requestCode, int resultCode, Intent data)258 public void onActivityResult(int requestCode, int resultCode, Intent data) { 259 if (requestCode == ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION) { 260 if (resultCode == Activity.RESULT_OK) { 261 handleConfirmServiceEnabled(/* confirmed= */ true); 262 // The user confirmed that they accept weaker encryption when 263 // enabling the accessibility service, so change encryption. 264 // Since we came here asynchronously, check encryption again. 265 if (isFullDiskEncrypted()) { 266 mLockPatternUtils.clearEncryptionPassword(); 267 Settings.Global.putInt(getContentResolver(), 268 Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, 0); 269 } 270 } else { 271 handleConfirmServiceEnabled(/* confirmed= */ false); 272 } 273 } 274 } 275 registerPackageRemoveReceiver()276 private void registerPackageRemoveReceiver() { 277 if (mPackageRemovedReceiver != null || getContext() == null) { 278 return; 279 } 280 mPackageRemovedReceiver = new BroadcastReceiver() { 281 @Override 282 public void onReceive(Context context, Intent intent) { 283 final String packageName = intent.getData().getSchemeSpecificPart(); 284 if (TextUtils.equals(mComponentName.getPackageName(), packageName)) { 285 getActivity().finishAndRemoveTask(); 286 } 287 } 288 }; 289 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 290 filter.addDataScheme("package"); 291 getContext().registerReceiver(mPackageRemovedReceiver, filter); 292 } 293 unregisterPackageRemoveReceiver()294 private void unregisterPackageRemoveReceiver() { 295 if (mPackageRemovedReceiver == null || getContext() == null) { 296 return; 297 } 298 getContext().unregisterReceiver(mPackageRemovedReceiver); 299 mPackageRemovedReceiver = null; 300 } 301 isServiceSupportAccessibilityButton()302 private boolean isServiceSupportAccessibilityButton() { 303 final AccessibilityManager ams = getPrefContext().getSystemService( 304 AccessibilityManager.class); 305 final List<AccessibilityServiceInfo> services = ams.getInstalledAccessibilityServiceList(); 306 307 for (AccessibilityServiceInfo info : services) { 308 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 309 ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; 310 if (serviceInfo != null && TextUtils.equals(serviceInfo.name, 311 getAccessibilityServiceInfo().getResolveInfo().serviceInfo.name)) { 312 return true; 313 } 314 } 315 } 316 317 return false; 318 } 319 handleConfirmServiceEnabled(boolean confirmed)320 private void handleConfirmServiceEnabled(boolean confirmed) { 321 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed); 322 onPreferenceToggled(mPreferenceKey, confirmed); 323 } 324 createConfirmCredentialReasonMessage()325 private String createConfirmCredentialReasonMessage() { 326 int resId = R.string.enable_service_password_reason; 327 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) { 328 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: { 329 resId = R.string.enable_service_pattern_reason; 330 } 331 break; 332 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 333 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: { 334 resId = R.string.enable_service_pin_reason; 335 } 336 break; 337 } 338 return getString(resId, getAccessibilityServiceInfo().getResolveInfo() 339 .loadLabel(getPackageManager())); 340 } 341 342 @Override onSwitchChanged(Switch switchView, boolean isChecked)343 public void onSwitchChanged(Switch switchView, boolean isChecked) { 344 if (isChecked != isAccessibilityServiceEnabled()) { 345 onPreferenceClick(isChecked); 346 } 347 } 348 349 @Override onToggleClicked(ShortcutPreference preference)350 public void onToggleClicked(ShortcutPreference preference) { 351 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 352 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 353 if (preference.isChecked()) { 354 if (!mToggleServiceSwitchPreference.isChecked()) { 355 preference.setChecked(false); 356 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE); 357 } else { 358 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, 359 mComponentName); 360 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 361 } 362 } else { 363 AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, 364 mComponentName); 365 } 366 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 367 } 368 369 @Override onSettingsClicked(ShortcutPreference preference)370 public void onSettingsClicked(ShortcutPreference preference) { 371 final boolean isServiceOnOrShortcutAdded = mShortcutPreference.isChecked() 372 || mToggleServiceSwitchPreference.isChecked(); 373 showPopupDialog(isServiceOnOrShortcutAdded ? DialogEnums.EDIT_SHORTCUT 374 : DialogEnums.ENABLE_WARNING_FROM_SHORTCUT); 375 } 376 377 @Override onProcessArguments(Bundle arguments)378 protected void onProcessArguments(Bundle arguments) { 379 super.onProcessArguments(arguments); 380 // Settings title and intent. 381 String settingsTitle = arguments.getString(AccessibilitySettings.EXTRA_SETTINGS_TITLE); 382 String settingsComponentName = arguments.getString( 383 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME); 384 if (!TextUtils.isEmpty(settingsTitle) && !TextUtils.isEmpty(settingsComponentName)) { 385 Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent( 386 ComponentName.unflattenFromString(settingsComponentName.toString())); 387 if (!getPackageManager().queryIntentActivities(settingsIntent, 0).isEmpty()) { 388 mSettingsTitle = settingsTitle; 389 mSettingsIntent = settingsIntent; 390 setHasOptionsMenu(true); 391 } 392 } 393 394 mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME); 395 396 // Settings animated image. 397 final int animatedImageRes = arguments.getInt( 398 AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES); 399 if (animatedImageRes > 0) { 400 mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 401 .authority(mComponentName.getPackageName()) 402 .appendPath(String.valueOf(animatedImageRes)) 403 .build(); 404 } 405 406 // Get Accessibility service name. 407 mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel( 408 getPackageManager()); 409 } 410 onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which)411 private void onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which) { 412 switch (which) { 413 case DialogInterface.BUTTON_POSITIVE: 414 handleConfirmServiceEnabled(/* confirmed= */ false); 415 break; 416 case DialogInterface.BUTTON_NEGATIVE: 417 handleConfirmServiceEnabled(/* confirmed= */ true); 418 break; 419 default: 420 throw new IllegalArgumentException("Unexpected button identifier"); 421 } 422 } 423 onDialogButtonFromEnableToggleClicked(View view)424 private void onDialogButtonFromEnableToggleClicked(View view) { 425 final int viewId = view.getId(); 426 if (viewId == R.id.permission_enable_allow_button) { 427 onAllowButtonFromEnableToggleClicked(); 428 } else if (viewId == R.id.permission_enable_deny_button) { 429 onDenyButtonFromEnableToggleClicked(); 430 } else { 431 throw new IllegalArgumentException("Unexpected view id"); 432 } 433 } 434 onDialogButtonFromUninstallClicked()435 private void onDialogButtonFromUninstallClicked() { 436 mDialog.dismiss(); 437 final Intent uninstallIntent = createUninstallPackageActivityIntent(); 438 if (uninstallIntent == null) { 439 return; 440 } 441 startActivity(uninstallIntent); 442 } 443 444 @Nullable createUninstallPackageActivityIntent()445 private Intent createUninstallPackageActivityIntent() { 446 final AccessibilityServiceInfo a11yServiceInfo = getAccessibilityServiceInfo(); 447 if (a11yServiceInfo == null) { 448 Log.w(TAG, "createUnInstallIntent -- invalid a11yServiceInfo"); 449 return null; 450 } 451 final ApplicationInfo appInfo = 452 a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo; 453 final Uri packageUri = Uri.parse("package:" + appInfo.packageName); 454 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 455 return uninstallIntent; 456 } 457 458 @Override onStop()459 public void onStop() { 460 super.onStop(); 461 unregisterPackageRemoveReceiver(); 462 } 463 onAllowButtonFromEnableToggleClicked()464 private void onAllowButtonFromEnableToggleClicked() { 465 if (isFullDiskEncrypted()) { 466 final String title = createConfirmCredentialReasonMessage(); 467 final Intent intent = ConfirmDeviceCredentialActivity.createIntent(title, /* details= */ 468 null); 469 startActivityForResult(intent, 470 ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION); 471 } else { 472 handleConfirmServiceEnabled(/* confirmed= */ true); 473 if (isServiceSupportAccessibilityButton()) { 474 mIsDialogShown.set(false); 475 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 476 } 477 } 478 479 mDialog.dismiss(); 480 } 481 onDenyButtonFromEnableToggleClicked()482 private void onDenyButtonFromEnableToggleClicked() { 483 handleConfirmServiceEnabled(/* confirmed= */ false); 484 mDialog.dismiss(); 485 } 486 onDialogButtonFromShortcutToggleClicked(View view)487 void onDialogButtonFromShortcutToggleClicked(View view) { 488 final int viewId = view.getId(); 489 if (viewId == R.id.permission_enable_allow_button) { 490 onAllowButtonFromShortcutToggleClicked(); 491 } else if (viewId == R.id.permission_enable_deny_button) { 492 onDenyButtonFromShortcutToggleClicked(); 493 } else { 494 throw new IllegalArgumentException("Unexpected view id"); 495 } 496 } 497 onAllowButtonFromShortcutToggleClicked()498 private void onAllowButtonFromShortcutToggleClicked() { 499 mShortcutPreference.setChecked(true); 500 501 final int shortcutTypes = retrieveUserShortcutType(getPrefContext(), 502 mComponentName.flattenToString(), UserShortcutType.SOFTWARE); 503 AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); 504 505 mIsDialogShown.set(false); 506 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 507 508 mDialog.dismiss(); 509 510 mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); 511 } 512 onDenyButtonFromShortcutToggleClicked()513 private void onDenyButtonFromShortcutToggleClicked() { 514 mShortcutPreference.setChecked(false); 515 516 mDialog.dismiss(); 517 } 518 onDialogButtonFromShortcutClicked(View view)519 void onDialogButtonFromShortcutClicked(View view) { 520 final int viewId = view.getId(); 521 if (viewId == R.id.permission_enable_allow_button) { 522 onAllowButtonFromShortcutClicked(); 523 } else if (viewId == R.id.permission_enable_deny_button) { 524 onDenyButtonFromShortcutClicked(); 525 } else { 526 throw new IllegalArgumentException("Unexpected view id"); 527 } 528 } 529 onAllowButtonFromShortcutClicked()530 private void onAllowButtonFromShortcutClicked() { 531 mIsDialogShown.set(false); 532 showPopupDialog(DialogEnums.EDIT_SHORTCUT); 533 534 mDialog.dismiss(); 535 } 536 onDenyButtonFromShortcutClicked()537 private void onDenyButtonFromShortcutClicked() { 538 mDialog.dismiss(); 539 } 540 onPreferenceClick(boolean isChecked)541 private boolean onPreferenceClick(boolean isChecked) { 542 if (isChecked) { 543 mToggleServiceSwitchPreference.setChecked(false); 544 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 545 /* disableService */ false); 546 if (!mShortcutPreference.isChecked()) { 547 showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_TOGGLE); 548 } else { 549 handleConfirmServiceEnabled(/* confirmed= */ true); 550 if (isServiceSupportAccessibilityButton()) { 551 showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 552 } 553 } 554 } else { 555 mToggleServiceSwitchPreference.setChecked(true); 556 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, 557 /* enableService */ true); 558 showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE); 559 } 560 return true; 561 } 562 showPopupDialog(int dialogId)563 private void showPopupDialog(int dialogId) { 564 if (mIsDialogShown.compareAndSet(/* expect= */ false, /* update= */ true)) { 565 showDialog(dialogId); 566 setOnDismissListener( 567 dialog -> mIsDialogShown.compareAndSet(/* expect= */ true, /* update= */ 568 false)); 569 } 570 } 571 } 572