1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.cellbroadcastreceiver; 18 19 import android.annotation.NonNull; 20 import android.app.ActionBar; 21 import android.app.ActivityManager; 22 import android.app.Fragment; 23 import android.app.backup.BackupManager; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.SharedPreferences; 29 import android.content.pm.PackageManager; 30 import android.content.res.Configuration; 31 import android.content.res.Resources; 32 import android.os.Bundle; 33 import android.os.UserManager; 34 import android.os.Vibrator; 35 import android.telephony.SubscriptionManager; 36 import android.util.Log; 37 import android.view.MenuItem; 38 import android.widget.Switch; 39 40 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 41 import androidx.preference.ListPreference; 42 import androidx.preference.Preference; 43 import androidx.preference.PreferenceCategory; 44 import androidx.preference.PreferenceFragment; 45 import androidx.preference.PreferenceManager; 46 import androidx.preference.TwoStatePreference; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.modules.utils.build.SdkLevel; 50 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; 51 import com.android.settingslib.widget.MainSwitchPreference; 52 import com.android.settingslib.widget.OnMainSwitchChangeListener; 53 54 import java.util.HashMap; 55 import java.util.Map; 56 57 /** 58 * Settings activity for the cell broadcast receiver. 59 */ 60 public class CellBroadcastSettings extends CollapsingToolbarBaseActivity { 61 62 private static final String TAG = "CellBroadcastSettings"; 63 64 private static final boolean DBG = false; 65 66 /** 67 * Keys for user preferences. 68 * When adding a new preference, make sure to clear its value in resetAllPreferences. 69 */ 70 // Preference key for alert header (A text view, not clickable). 71 public static final String KEY_ALERTS_HEADER = "alerts_header"; 72 73 // Preference key for a main toggle to enable/disable all alerts message (default enabled). 74 public static final String KEY_ENABLE_ALERTS_MASTER_TOGGLE = "enable_alerts_master_toggle"; 75 76 // Preference key for whether to enable public safety messages (default enabled). 77 public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES = "enable_public_safety_messages"; 78 79 // Preference key for whether to show full-screen public safety message (pop-up dialog), If set 80 // to false, only display from message history and sms inbox if enabled. A foreground 81 // notification might also be shown if enabled. 82 public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN = 83 "enable_public_safety_messages_full_screen"; 84 85 // Preference key for whether to enable emergency alerts (default enabled). 86 public static final String KEY_ENABLE_EMERGENCY_ALERTS = "enable_emergency_alerts"; 87 88 // Enable vibration on alert (unless main volume is silent). 89 public static final String KEY_ENABLE_ALERT_VIBRATE = "enable_alert_vibrate"; 90 91 // Speak contents of alert after playing the alert sound. 92 public static final String KEY_ENABLE_ALERT_SPEECH = "enable_alert_speech"; 93 94 // Play alert sound in full volume regardless Do Not Disturb is on. 95 public static final String KEY_OVERRIDE_DND = "override_dnd"; 96 97 public static final String KEY_OVERRIDE_DND_SETTINGS_CHANGED = 98 "override_dnd_settings_changed"; 99 100 // Preference category for emergency alert and CMAS settings. 101 public static final String KEY_CATEGORY_EMERGENCY_ALERTS = "category_emergency_alerts"; 102 103 // Preference category for alert preferences. 104 public static final String KEY_CATEGORY_ALERT_PREFERENCES = "category_alert_preferences"; 105 106 // Show checkbox for Presidential alerts in settings 107 // Whether to display CMAS presidential alert notifications (always enabled). 108 public static final String KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS = 109 "enable_cmas_presidential_alerts"; 110 111 // Whether to display CMAS extreme threat notifications (default is enabled). 112 public static final String KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS = 113 "enable_cmas_extreme_threat_alerts"; 114 115 // Whether to display CMAS severe threat notifications (default is enabled). 116 public static final String KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS = 117 "enable_cmas_severe_threat_alerts"; 118 119 // Whether to display CMAS amber alert messages (default is enabled). 120 public static final String KEY_ENABLE_CMAS_AMBER_ALERTS = "enable_cmas_amber_alerts"; 121 122 // Whether to display monthly test messages (default is disabled). 123 public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts"; 124 125 // Whether to display exercise test alerts. 126 public static final String KEY_ENABLE_EXERCISE_ALERTS = "enable_exercise_alerts"; 127 128 // Whether to display operator defined test alerts 129 public static final String KEY_OPERATOR_DEFINED_ALERTS = "enable_operator_defined_alerts"; 130 131 // Whether to display state/local test messages (default disabled). 132 public static final String KEY_ENABLE_STATE_LOCAL_TEST_ALERTS = 133 "enable_state_local_test_alerts"; 134 135 // Preference key for whether to enable area update information notifications 136 // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden. 137 public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS = 138 "enable_area_update_info_alerts"; 139 140 // Preference key for initial opt-in/opt-out dialog. 141 public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; 142 143 // Alert reminder interval ("once" = single 2 minute reminder). 144 public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; 145 146 // Preference key for emergency alerts history 147 public static final String KEY_EMERGENCY_ALERT_HISTORY = "emergency_alert_history"; 148 149 // For watch layout 150 private static final String KEY_WATCH_ALERT_REMINDER = "watch_alert_reminder"; 151 152 // For top introduction info 153 private static final String KEY_PREFS_TOP_INTRO = "alert_prefs_top_intro"; 154 155 // Whether to receive alert in second language code 156 public static final String KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE = 157 "receive_cmas_in_second_language"; 158 159 /* End of user preferences keys section. */ 160 161 // Resource cache per operator 162 private static final Map<String, Resources> sResourcesCacheByOperator = new HashMap<>(); 163 private static final Object sCacheLock = new Object(); 164 165 // Intent sent from cellbroadcastreceiver to notify cellbroadcastservice that area info update 166 // is disabled/enabled. 167 private static final String AREA_INFO_UPDATE_ACTION = 168 "com.android.cellbroadcastreceiver.action.AREA_UPDATE_INFO_ENABLED"; 169 private static final String AREA_INFO_UPDATE_ENABLED_EXTRA = "enable"; 170 171 /** 172 * This permission is only granted to the cellbroadcast mainline module and thus can be 173 * used for permission check within CBR and CBS. 174 */ 175 private static final String CBR_MODULE_PERMISSION = 176 "com.android.cellbroadcastservice.FULL_ACCESS_CELL_BROADCAST_HISTORY"; 177 178 // Key for shared preference which represents whether user has changed any preference 179 private static final String ANY_PREFERENCE_CHANGED_BY_USER = "any_preference_changed_by_user"; 180 181 @Override onCreate(Bundle savedInstanceState)182 public void onCreate(Bundle savedInstanceState) { 183 // for backward compatibility on R devices 184 if (!SdkLevel.isAtLeastS()) { 185 setCustomizeContentView(R.layout.cell_broadcast_list_collapsing_no_toobar); 186 } 187 super.onCreate(savedInstanceState); 188 189 // for backward compatibility on R devices 190 if (!SdkLevel.isAtLeastS()) { 191 ActionBar actionBar = getActionBar(); 192 if (actionBar != null) { 193 // android.R.id.home will be triggered in onOptionsItemSelected() 194 actionBar.setDisplayHomeAsUpEnabled(true); 195 } 196 } 197 198 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 199 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) { 200 setContentView(R.layout.cell_broadcast_disallowed_preference_screen); 201 return; 202 } 203 204 // We only add new CellBroadcastSettingsFragment if no fragment is restored. 205 Fragment fragment = getFragmentManager().findFragmentById( 206 com.android.settingslib.widget.R.id.content_frame); 207 if (fragment == null) { 208 fragment = new CellBroadcastSettingsFragment(); 209 getFragmentManager() 210 .beginTransaction() 211 .add(com.android.settingslib.widget.R.id.content_frame, fragment) 212 .commit(); 213 } 214 } 215 216 @Override onStart()217 public void onStart() { 218 super.onStart(); 219 getWindow().addSystemFlags( 220 android.view.WindowManager.LayoutParams 221 .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 222 } 223 224 @Override onOptionsItemSelected(MenuItem item)225 public boolean onOptionsItemSelected(MenuItem item) { 226 switch (item.getItemId()) { 227 // Respond to the action bar's Up/Home button 228 case android.R.id.home: 229 finish(); 230 return true; 231 } 232 return super.onOptionsItemSelected(item); 233 } 234 235 /** 236 * Reset all user values for preferences (stored in shared preferences). 237 * 238 * @param c the application context 239 */ resetAllPreferences(Context c)240 public static void resetAllPreferences(Context c) { 241 SharedPreferences.Editor e = PreferenceManager.getDefaultSharedPreferences(c).edit(); 242 e.remove(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS) 243 .remove(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS) 244 .remove(KEY_ENABLE_CMAS_AMBER_ALERTS) 245 .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES) 246 .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN) 247 .remove(KEY_ENABLE_EMERGENCY_ALERTS) 248 .remove(KEY_ALERT_REMINDER_INTERVAL) 249 .remove(KEY_ENABLE_ALERT_SPEECH) 250 .remove(KEY_OVERRIDE_DND) 251 .remove(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS) 252 .remove(KEY_ENABLE_TEST_ALERTS) 253 .remove(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS) 254 .remove(KEY_ENABLE_ALERT_VIBRATE) 255 .remove(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS) 256 .remove(KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE) 257 .remove(KEY_ENABLE_EXERCISE_ALERTS) 258 .remove(KEY_OPERATOR_DEFINED_ALERTS); 259 // If the device is in test harness mode, reset main toggle should only happen on the 260 // first boot. 261 if (!ActivityManager.isRunningInUserTestHarness()) { 262 Log.d(TAG, "In not test harness mode. reset main toggle."); 263 e.remove(KEY_ENABLE_ALERTS_MASTER_TOGGLE); 264 } 265 e.commit(); 266 267 PackageManager pm = c.getPackageManager(); 268 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 269 PreferenceManager.setDefaultValues(c, R.xml.watch_preferences, true); 270 } else { 271 PreferenceManager.setDefaultValues(c, R.xml.preferences, true); 272 } 273 setPreferenceChanged(c, false); 274 } 275 276 /** 277 * Return true if user has modified any preference manually. 278 * @param c the application context 279 * @return 280 */ hasAnyPreferenceChanged(Context c)281 public static boolean hasAnyPreferenceChanged(Context c) { 282 return PreferenceManager.getDefaultSharedPreferences(c) 283 .getBoolean(ANY_PREFERENCE_CHANGED_BY_USER, false); 284 } 285 setPreferenceChanged(Context c, boolean changed)286 private static void setPreferenceChanged(Context c, boolean changed) { 287 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c); 288 sp.edit().putBoolean(ANY_PREFERENCE_CHANGED_BY_USER, changed).apply(); 289 } 290 291 /** 292 * New fragment-style implementation of preferences. 293 */ 294 public static class CellBroadcastSettingsFragment extends PreferenceFragment { 295 296 private TwoStatePreference mExtremeCheckBox; 297 private TwoStatePreference mSevereCheckBox; 298 private TwoStatePreference mAmberCheckBox; 299 private MainSwitchPreference mMasterToggle; 300 private TwoStatePreference mPublicSafetyMessagesChannelCheckBox; 301 private TwoStatePreference mPublicSafetyMessagesChannelFullScreenCheckBox; 302 private TwoStatePreference mEmergencyAlertsCheckBox; 303 private ListPreference mReminderInterval; 304 private TwoStatePreference mSpeechCheckBox; 305 private TwoStatePreference mOverrideDndCheckBox; 306 private TwoStatePreference mAreaUpdateInfoCheckBox; 307 private TwoStatePreference mTestCheckBox; 308 private TwoStatePreference mExerciseTestCheckBox; 309 private TwoStatePreference mOperatorDefinedCheckBox; 310 private TwoStatePreference mStateLocalTestCheckBox; 311 private TwoStatePreference mEnableVibrateCheckBox; 312 private Preference mAlertHistory; 313 private Preference mAlertsHeader; 314 private PreferenceCategory mAlertCategory; 315 private PreferenceCategory mAlertPreferencesCategory; 316 private boolean mDisableSevereWhenExtremeDisabled = true; 317 318 // Show checkbox for Presidential alerts in settings 319 private TwoStatePreference mPresidentialCheckBox; 320 321 // on/off switch in settings for receiving alert in second language code 322 private TwoStatePreference mReceiveCmasInSecondLanguageCheckBox; 323 324 // Show the top introduction 325 private Preference mTopIntroPreference; 326 327 private final BroadcastReceiver mTestingModeChangedReceiver = new BroadcastReceiver() { 328 @Override 329 public void onReceive(Context context, Intent intent) { 330 switch (intent.getAction()) { 331 case CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED: 332 updatePreferenceVisibility(); 333 break; 334 } 335 } 336 }; 337 initPreferences()338 private void initPreferences() { 339 mExtremeCheckBox = (TwoStatePreference) 340 findPreference(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS); 341 mSevereCheckBox = (TwoStatePreference) 342 findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS); 343 mAmberCheckBox = (TwoStatePreference) 344 findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS); 345 mMasterToggle = (MainSwitchPreference) 346 findPreference(KEY_ENABLE_ALERTS_MASTER_TOGGLE); 347 mPublicSafetyMessagesChannelCheckBox = (TwoStatePreference) 348 findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES); 349 mPublicSafetyMessagesChannelFullScreenCheckBox = (TwoStatePreference) 350 findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN); 351 mEmergencyAlertsCheckBox = (TwoStatePreference) 352 findPreference(KEY_ENABLE_EMERGENCY_ALERTS); 353 mReminderInterval = (ListPreference) 354 findPreference(KEY_ALERT_REMINDER_INTERVAL); 355 mSpeechCheckBox = (TwoStatePreference) 356 findPreference(KEY_ENABLE_ALERT_SPEECH); 357 mOverrideDndCheckBox = (TwoStatePreference) 358 findPreference(KEY_OVERRIDE_DND); 359 mAreaUpdateInfoCheckBox = (TwoStatePreference) 360 findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS); 361 mTestCheckBox = (TwoStatePreference) 362 findPreference(KEY_ENABLE_TEST_ALERTS); 363 mExerciseTestCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_EXERCISE_ALERTS); 364 mOperatorDefinedCheckBox = (TwoStatePreference) 365 findPreference(KEY_OPERATOR_DEFINED_ALERTS); 366 mStateLocalTestCheckBox = (TwoStatePreference) 367 findPreference(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS); 368 mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY); 369 mAlertsHeader = findPreference(KEY_ALERTS_HEADER); 370 mReceiveCmasInSecondLanguageCheckBox = (TwoStatePreference) findPreference 371 (KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE); 372 mEnableVibrateCheckBox = findPreference(KEY_ENABLE_ALERT_VIBRATE); 373 374 // Show checkbox for Presidential alerts in settings 375 mPresidentialCheckBox = (TwoStatePreference) 376 findPreference(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS); 377 378 PackageManager pm = getActivity().getPackageManager(); 379 if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 380 mAlertPreferencesCategory = (PreferenceCategory) 381 findPreference(KEY_CATEGORY_ALERT_PREFERENCES); 382 mAlertCategory = (PreferenceCategory) 383 findPreference(KEY_CATEGORY_EMERGENCY_ALERTS); 384 } 385 mTopIntroPreference = findPreference(KEY_PREFS_TOP_INTRO); 386 } 387 388 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)389 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 390 391 LocalBroadcastManager.getInstance(getContext()) 392 .registerReceiver(mTestingModeChangedReceiver, new IntentFilter( 393 CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED)); 394 395 // Load the preferences from an XML resource 396 PackageManager pm = getActivity().getPackageManager(); 397 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 398 addPreferencesFromResource(R.xml.watch_preferences); 399 } else { 400 addPreferencesFromResource(R.xml.preferences); 401 } 402 403 initPreferences(); 404 405 Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); 406 407 mDisableSevereWhenExtremeDisabled = res.getBoolean( 408 R.bool.disable_severe_when_extreme_disabled); 409 410 final OnMainSwitchChangeListener mainSwitchListener = new OnMainSwitchChangeListener() { 411 @Override 412 public void onSwitchChanged(Switch switchView, boolean isChecked) { 413 setAlertsEnabled(isChecked); 414 onPreferenceChangedByUser(getContext()); 415 } 416 }; 417 418 // Handler for settings that require us to reconfigure enabled channels in radio 419 Preference.OnPreferenceChangeListener startConfigServiceListener = 420 new Preference.OnPreferenceChangeListener() { 421 @Override 422 public boolean onPreferenceChange(Preference pref, Object newValue) { 423 if (mDisableSevereWhenExtremeDisabled) { 424 if (pref.getKey().equals(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)) { 425 boolean isExtremeAlertChecked = (Boolean) newValue; 426 if (mSevereCheckBox != null) { 427 mSevereCheckBox.setEnabled(isExtremeAlertChecked); 428 mSevereCheckBox.setChecked(false); 429 } 430 } 431 } 432 433 // check if area update was disabled 434 if (pref.getKey().equals(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS)) { 435 boolean isEnabledAlert = (Boolean) newValue; 436 notifyAreaInfoUpdate(isEnabledAlert); 437 } 438 439 onPreferenceChangedByUser(getContext()); 440 return true; 441 } 442 }; 443 444 initReminderIntervalList(); 445 446 if (mMasterToggle != null) { 447 mMasterToggle.addOnSwitchChangeListener(mainSwitchListener); 448 // If allow alerts are disabled, we turn all sub-alerts off. If it's enabled, we 449 // leave them as they are. 450 if (!mMasterToggle.isChecked()) { 451 setAlertsEnabled(false); 452 } 453 } 454 // note that mPresidentialCheckBox does not use the startConfigServiceListener because 455 // the user is never allowed to change the preference 456 if (mAreaUpdateInfoCheckBox != null) { 457 mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 458 } 459 if (mExtremeCheckBox != null) { 460 mExtremeCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 461 } 462 if (mPublicSafetyMessagesChannelCheckBox != null) { 463 mPublicSafetyMessagesChannelCheckBox.setOnPreferenceChangeListener( 464 startConfigServiceListener); 465 } 466 if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) { 467 mPublicSafetyMessagesChannelFullScreenCheckBox.setOnPreferenceChangeListener( 468 startConfigServiceListener); 469 } 470 if (mEmergencyAlertsCheckBox != null) { 471 mEmergencyAlertsCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 472 } 473 if (mSevereCheckBox != null) { 474 mSevereCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 475 if (mDisableSevereWhenExtremeDisabled) { 476 if (mExtremeCheckBox != null) { 477 mSevereCheckBox.setEnabled(mExtremeCheckBox.isChecked()); 478 } 479 } 480 } 481 if (mAmberCheckBox != null) { 482 mAmberCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 483 } 484 if (mTestCheckBox != null) { 485 mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 486 } 487 if (mExerciseTestCheckBox != null) { 488 mExerciseTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 489 } 490 if (mOperatorDefinedCheckBox != null) { 491 mOperatorDefinedCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 492 } 493 if (mStateLocalTestCheckBox != null) { 494 mStateLocalTestCheckBox.setOnPreferenceChangeListener( 495 startConfigServiceListener); 496 } 497 498 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); 499 500 if (mOverrideDndCheckBox != null) { 501 if (!sp.getBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED, false)) { 502 // If the user hasn't changed this settings yet, use the default settings 503 // from resource overlay. 504 mOverrideDndCheckBox.setChecked(res.getBoolean(R.bool.override_dnd_default)); 505 } 506 mOverrideDndCheckBox.setOnPreferenceChangeListener( 507 (pref, newValue) -> { 508 sp.edit().putBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED, 509 true).apply(); 510 updateVibrationPreference((boolean) newValue); 511 return true; 512 }); 513 } 514 515 if (mAlertHistory != null) { 516 mAlertHistory.setOnPreferenceClickListener( 517 preference -> { 518 final Intent intent = new Intent(getContext(), 519 CellBroadcastListActivity.class); 520 startActivity(intent); 521 return true; 522 }); 523 } 524 525 updateVibrationPreference(sp.getBoolean(CellBroadcastSettings.KEY_OVERRIDE_DND, 526 false)); 527 updatePreferenceVisibility(); 528 } 529 530 /** 531 * Update the vibration preference based on override DND. If DND is overridden, then do 532 * not allow users to turn off vibration. 533 * 534 * @param overrideDnd {@code true} if the alert will be played at full volume, regardless 535 * DND settings. 536 */ updateVibrationPreference(boolean overrideDnd)537 private void updateVibrationPreference(boolean overrideDnd) { 538 if (mEnableVibrateCheckBox != null) { 539 if (overrideDnd) { 540 // If DND is enabled, always enable vibration. 541 mEnableVibrateCheckBox.setChecked(true); 542 } 543 // Grey out the preference if DND is overridden. 544 mEnableVibrateCheckBox.setEnabled(!overrideDnd); 545 } 546 } 547 548 /** 549 * Dynamically update each preference's visibility based on configuration. 550 */ updatePreferenceVisibility()551 private void updatePreferenceVisibility() { 552 Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); 553 554 // The settings should be based on the config by the subscription 555 CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager( 556 getContext(), SubscriptionManager.getDefaultSubscriptionId(), null); 557 558 if (mMasterToggle != null) { 559 mMasterToggle.setVisible(res.getBoolean(R.bool.show_main_switch_settings)); 560 } 561 562 if (mPresidentialCheckBox != null) { 563 mPresidentialCheckBox.setVisible( 564 res.getBoolean(R.bool.show_presidential_alerts_settings)); 565 } 566 567 if (mExtremeCheckBox != null) { 568 mExtremeCheckBox.setVisible(res.getBoolean(R.bool.show_extreme_alert_settings) 569 && !channelManager.getCellBroadcastChannelRanges( 570 R.array.cmas_alert_extreme_channels_range_strings).isEmpty()); 571 } 572 573 if (mSevereCheckBox != null) { 574 mSevereCheckBox.setVisible(res.getBoolean(R.bool.show_severe_alert_settings) 575 && !channelManager.getCellBroadcastChannelRanges( 576 R.array.cmas_alerts_severe_range_strings).isEmpty()); 577 } 578 579 if (mAmberCheckBox != null) { 580 mAmberCheckBox.setVisible(res.getBoolean(R.bool.show_amber_alert_settings) 581 && !channelManager.getCellBroadcastChannelRanges( 582 R.array.cmas_amber_alerts_channels_range_strings).isEmpty()); 583 } 584 585 if (mPublicSafetyMessagesChannelCheckBox != null) { 586 mPublicSafetyMessagesChannelCheckBox.setVisible( 587 res.getBoolean(R.bool.show_public_safety_settings) 588 && !channelManager.getCellBroadcastChannelRanges( 589 R.array.public_safety_messages_channels_range_strings) 590 .isEmpty()); 591 } 592 // this is the matching full screen settings for public safety toggle. shown only if 593 // public safety toggle is displayed. 594 if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) { 595 mPublicSafetyMessagesChannelFullScreenCheckBox.setVisible( 596 res.getBoolean(R.bool.show_public_safety_full_screen_settings) 597 && (mPublicSafetyMessagesChannelCheckBox != null 598 && mPublicSafetyMessagesChannelCheckBox.isVisible())); 599 } 600 601 if (mTestCheckBox != null) { 602 mTestCheckBox.setVisible(isTestAlertsToggleVisible(getContext())); 603 } 604 605 if (mExerciseTestCheckBox != null) { 606 boolean visible = false; 607 if (res.getBoolean(R.bool.show_separate_exercise_settings)) { 608 if (res.getBoolean(R.bool.show_exercise_settings) 609 || CellBroadcastReceiver.isTestingMode(getContext())) { 610 if (!channelManager.getCellBroadcastChannelRanges( 611 R.array.exercise_alert_range_strings).isEmpty()) { 612 visible = true; 613 } 614 } 615 } 616 mExerciseTestCheckBox.setVisible(visible); 617 } 618 619 if (mOperatorDefinedCheckBox != null) { 620 boolean visible = false; 621 if (res.getBoolean(R.bool.show_separate_operator_defined_settings)) { 622 if (res.getBoolean(R.bool.show_operator_defined_settings) 623 || CellBroadcastReceiver.isTestingMode(getContext())) { 624 if (!channelManager.getCellBroadcastChannelRanges( 625 R.array.operator_defined_alert_range_strings).isEmpty()) { 626 visible = true; 627 } 628 } 629 } 630 mOperatorDefinedCheckBox.setVisible(visible); 631 } 632 633 if (mEmergencyAlertsCheckBox != null) { 634 mEmergencyAlertsCheckBox.setVisible(!channelManager.getCellBroadcastChannelRanges( 635 R.array.emergency_alerts_channels_range_strings).isEmpty()); 636 } 637 638 if (mStateLocalTestCheckBox != null) { 639 mStateLocalTestCheckBox.setVisible( 640 res.getBoolean(R.bool.show_state_local_test_settings) 641 && !channelManager.getCellBroadcastChannelRanges( 642 R.array.state_local_test_alert_range_strings).isEmpty()); 643 } 644 645 if (mReceiveCmasInSecondLanguageCheckBox != null) { 646 mReceiveCmasInSecondLanguageCheckBox.setVisible(!res.getString( 647 R.string.emergency_alert_second_language_code).isEmpty()); 648 } 649 650 if (mAreaUpdateInfoCheckBox != null) { 651 mAreaUpdateInfoCheckBox.setVisible( 652 res.getBoolean(R.bool.config_showAreaUpdateInfoSettings)); 653 } 654 655 if (mOverrideDndCheckBox != null) { 656 mOverrideDndCheckBox.setVisible(res.getBoolean(R.bool.show_override_dnd_settings)); 657 } 658 659 if (mEnableVibrateCheckBox != null) { 660 // Only show vibrate toggle when override DND toggle is available to users, or when 661 // override DND default is turned off. 662 // In some countries, override DND is always on, which means vibration is always on. 663 // In that case, no need to show vibration toggle for users. 664 mEnableVibrateCheckBox.setVisible(isVibrationToggleVisible(getContext(), res)); 665 } 666 if (mAlertsHeader != null) { 667 mAlertsHeader.setVisible( 668 !getContext().getString(R.string.alerts_header_summary).isEmpty()); 669 } 670 671 if (mSpeechCheckBox != null) { 672 mSpeechCheckBox.setVisible(res.getBoolean(R.bool.show_alert_speech_setting) 673 || getActivity().getPackageManager() 674 .hasSystemFeature(PackageManager.FEATURE_WATCH)); 675 } 676 677 if (mTopIntroPreference != null) { 678 mTopIntroPreference.setTitle(getTopIntroduction()); 679 } 680 } 681 getTopIntroduction()682 private int getTopIntroduction() { 683 // Only set specific top introduction for roaming support now 684 if (!CellBroadcastReceiver.getRoamingOperatorSupported(getContext()).isEmpty()) { 685 return R.string.top_intro_roaming_text; 686 } 687 return R.string.top_intro_default_text; 688 } 689 initReminderIntervalList()690 private void initReminderIntervalList() { 691 Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); 692 693 String[] activeValues = 694 res.getStringArray(R.array.alert_reminder_interval_active_values); 695 String[] allEntries = res.getStringArray(R.array.alert_reminder_interval_entries); 696 String[] newEntries = new String[activeValues.length]; 697 698 // Only add active interval to the list 699 for (int i = 0; i < activeValues.length; i++) { 700 int index = mReminderInterval.findIndexOfValue(activeValues[i]); 701 if (index != -1) { 702 newEntries[i] = allEntries[index]; 703 if (DBG) Log.d(TAG, "Added " + allEntries[index]); 704 } else { 705 Log.e(TAG, "Can't find " + activeValues[i]); 706 } 707 } 708 709 mReminderInterval.setEntries(newEntries); 710 mReminderInterval.setEntryValues(activeValues); 711 mReminderInterval.setSummary(mReminderInterval.getEntry()); 712 mReminderInterval.setOnPreferenceChangeListener( 713 new Preference.OnPreferenceChangeListener() { 714 @Override 715 public boolean onPreferenceChange(Preference pref, Object newValue) { 716 final ListPreference listPref = (ListPreference) pref; 717 final int idx = listPref.findIndexOfValue((String) newValue); 718 listPref.setSummary(listPref.getEntries()[idx]); 719 return true; 720 } 721 }); 722 } 723 724 setAlertsEnabled(boolean alertsEnabled)725 private void setAlertsEnabled(boolean alertsEnabled) { 726 if (mSevereCheckBox != null) { 727 mSevereCheckBox.setEnabled(alertsEnabled); 728 mSevereCheckBox.setChecked(alertsEnabled); 729 } 730 if (mExtremeCheckBox != null) { 731 mExtremeCheckBox.setEnabled(alertsEnabled); 732 mExtremeCheckBox.setChecked(alertsEnabled); 733 } 734 if (mAmberCheckBox != null) { 735 mAmberCheckBox.setEnabled(alertsEnabled); 736 mAmberCheckBox.setChecked(alertsEnabled); 737 } 738 if (mAreaUpdateInfoCheckBox != null) { 739 mAreaUpdateInfoCheckBox.setEnabled(alertsEnabled); 740 mAreaUpdateInfoCheckBox.setChecked(alertsEnabled); 741 notifyAreaInfoUpdate(alertsEnabled); 742 } 743 if (mEmergencyAlertsCheckBox != null) { 744 mEmergencyAlertsCheckBox.setEnabled(alertsEnabled); 745 mEmergencyAlertsCheckBox.setChecked(alertsEnabled); 746 } 747 if (mPublicSafetyMessagesChannelCheckBox != null) { 748 mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled); 749 mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled); 750 } 751 if (mStateLocalTestCheckBox != null) { 752 mStateLocalTestCheckBox.setEnabled(alertsEnabled); 753 mStateLocalTestCheckBox.setChecked(alertsEnabled); 754 } 755 if (mTestCheckBox != null) { 756 mTestCheckBox.setEnabled(alertsEnabled); 757 mTestCheckBox.setChecked(alertsEnabled); 758 } 759 if (mExerciseTestCheckBox != null) { 760 mExerciseTestCheckBox.setEnabled(alertsEnabled); 761 mExerciseTestCheckBox.setChecked(alertsEnabled); 762 } 763 if (mOperatorDefinedCheckBox != null) { 764 mOperatorDefinedCheckBox.setEnabled(alertsEnabled); 765 mOperatorDefinedCheckBox.setChecked(alertsEnabled); 766 } 767 } 768 notifyAreaInfoUpdate(boolean enabled)769 private void notifyAreaInfoUpdate(boolean enabled) { 770 Intent areaInfoIntent = new Intent(AREA_INFO_UPDATE_ACTION); 771 areaInfoIntent.putExtra(AREA_INFO_UPDATE_ENABLED_EXTRA, enabled); 772 // sending broadcast protected by the permission which is only 773 // granted for CBR mainline module. 774 getContext().sendBroadcast(areaInfoIntent, CBR_MODULE_PERMISSION); 775 } 776 777 778 @Override onResume()779 public void onResume() { 780 super.onResume(); 781 updatePreferenceVisibility(); 782 } 783 784 @Override onDestroy()785 public void onDestroy() { 786 super.onDestroy(); 787 LocalBroadcastManager.getInstance(getContext()) 788 .unregisterReceiver(mTestingModeChangedReceiver); 789 } 790 791 /** 792 * Callback to be called when preference or master toggle is changed by user 793 * 794 * @param context Context to use 795 */ onPreferenceChangedByUser(Context context)796 public void onPreferenceChangedByUser(Context context) { 797 CellBroadcastReceiver.startConfigService(context, 798 CellBroadcastConfigService.ACTION_ENABLE_CHANNELS); 799 setPreferenceChanged(context, true); 800 801 // Notify backup manager a backup pass is needed. 802 new BackupManager(context).dataChanged(); 803 } 804 } 805 806 /** 807 * Check whether vibration toggle is visible 808 * @param context Context 809 * @param res resources 810 */ isVibrationToggleVisible(Context context, Resources res)811 public static boolean isVibrationToggleVisible(Context context, Resources res) { 812 Vibrator vibrator = context.getSystemService(Vibrator.class); 813 boolean supportVibration = (vibrator != null) && vibrator.hasVibrator(); 814 boolean isVibrationToggleVisible = supportVibration 815 && (res.getBoolean(R.bool.show_override_dnd_settings) 816 || !res.getBoolean(R.bool.override_dnd)); 817 return isVibrationToggleVisible; 818 } 819 isTestAlertsToggleVisible(Context context)820 public static boolean isTestAlertsToggleVisible(Context context) { 821 return isTestAlertsToggleVisible(context, null); 822 } 823 824 /** 825 * Check whether test alert toggle is visible 826 * @param context Context 827 * @param operator Opeator numeric 828 */ isTestAlertsToggleVisible(Context context, String operator)829 public static boolean isTestAlertsToggleVisible(Context context, String operator) { 830 CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(context, 831 SubscriptionManager.getDefaultSubscriptionId(), operator); 832 Resources res = operator == null ? getResourcesForDefaultSubId(context) 833 : getResourcesByOperator(context, 834 SubscriptionManager.getDefaultSubscriptionId(), operator); 835 boolean isTestAlertsAvailable = !channelManager.getCellBroadcastChannelRanges( 836 R.array.required_monthly_test_range_strings).isEmpty() 837 || (!channelManager.getCellBroadcastChannelRanges( 838 R.array.exercise_alert_range_strings).isEmpty() 839 /** exercise toggle is controlled under the main test toggle */ 840 && (!res.getBoolean(R.bool.show_separate_exercise_settings))) 841 || (!channelManager.getCellBroadcastChannelRanges( 842 R.array.operator_defined_alert_range_strings).isEmpty() 843 /** operator defined toggle is controlled under the main test toggle */ 844 && (!res.getBoolean(R.bool.show_separate_operator_defined_settings))) 845 || !channelManager.getCellBroadcastChannelRanges( 846 R.array.etws_test_alerts_range_strings).isEmpty(); 847 848 return (res.getBoolean(R.bool.show_test_settings) 849 || CellBroadcastReceiver.isTestingMode(context)) 850 && isTestAlertsAvailable; 851 } 852 853 /** 854 * Get the device resource based on SIM 855 * 856 * @param context Context 857 * @param subId Subscription index 858 * 859 * @return The resource 860 */ getResources(@onNull Context context, int subId)861 public static @NonNull Resources getResources(@NonNull Context context, int subId) { 862 863 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID 864 || !SubscriptionManager.isValidSubscriptionId(subId)) { 865 return context.getResources(); 866 } 867 868 return SubscriptionManager.getResourcesForSubId(context, subId); 869 } 870 871 /** 872 * Get the resources using the default subscription ID. 873 * @param context Context 874 * @return the Resources for the default subscription ID, or if there is no default subscription 875 * from SubscriptionManager, the resources for the latest loaded SIM. 876 */ getResourcesForDefaultSubId(@onNull Context context)877 public static @NonNull Resources getResourcesForDefaultSubId(@NonNull Context context) { 878 return getResources(context, SubscriptionManager.getDefaultSubscriptionId()); 879 } 880 881 /** 882 * Get the resources per network operator 883 * @param context Context 884 * @param operator Opeator numeric 885 * @return the Resources based on network operator 886 */ getResourcesByOperator( @onNull Context context, int subId, @NonNull String operator)887 public static @NonNull Resources getResourcesByOperator( 888 @NonNull Context context, int subId, @NonNull String operator) { 889 if (operator == null || operator.isEmpty()) { 890 return getResources(context, subId); 891 } 892 893 synchronized (sCacheLock) { 894 Resources res = sResourcesCacheByOperator.get(operator); 895 if (res != null) { 896 return res; 897 } 898 899 Configuration overrideConfig = new Configuration(); 900 try { 901 int mcc = Integer.parseInt(operator.substring(0, 3)); 902 int mnc = operator.length() > 3 ? Integer.parseInt(operator.substring(3)) 903 : Configuration.MNC_ZERO; 904 905 overrideConfig.mcc = mcc; 906 overrideConfig.mnc = mnc; 907 } catch (NumberFormatException e) { 908 // should not happen 909 Log.e(TAG, "invalid operator: " + operator); 910 return context.getResources(); 911 } 912 913 Context newContext = context.createConfigurationContext(overrideConfig); 914 res = newContext.getResources(); 915 916 sResourcesCacheByOperator.put(operator, res); 917 return res; 918 } 919 } 920 921 /** 922 * Get the resources id which is used for the default value of the preference 923 * @param key the preference key 924 * @return a valid resources id if the key is valid and the default value is 925 * defined, otherwise 0 926 */ getResourcesIdForDefaultPrefValue(String key)927 public static int getResourcesIdForDefaultPrefValue(String key) { 928 switch (key) { 929 case KEY_ENABLE_ALERTS_MASTER_TOGGLE: 930 return R.bool.master_toggle_enabled_default; 931 case KEY_ENABLE_PUBLIC_SAFETY_MESSAGES: 932 return R.bool.public_safety_messages_enabled_default; 933 case KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN: 934 return R.bool.public_safety_messages_full_screen_enabled_default; 935 case KEY_ENABLE_EMERGENCY_ALERTS: 936 return R.bool.emergency_alerts_enabled_default; 937 case KEY_ENABLE_ALERT_SPEECH: 938 return R.bool.enable_alert_speech_default; 939 case KEY_OVERRIDE_DND: 940 return R.bool.override_dnd_default; 941 case KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS: 942 return R.bool.extreme_threat_alerts_enabled_default; 943 case KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS: 944 return R.bool.severe_threat_alerts_enabled_default; 945 case KEY_ENABLE_CMAS_AMBER_ALERTS: 946 return R.bool.amber_alerts_enabled_default; 947 case KEY_ENABLE_TEST_ALERTS: 948 return R.bool.test_alerts_enabled_default; 949 case KEY_ENABLE_EXERCISE_ALERTS: 950 return R.bool.test_exercise_alerts_enabled_default; 951 case KEY_OPERATOR_DEFINED_ALERTS: 952 return R.bool.test_operator_defined_alerts_enabled_default; 953 case KEY_ENABLE_STATE_LOCAL_TEST_ALERTS: 954 return R.bool.state_local_test_alerts_enabled_default; 955 case KEY_ENABLE_AREA_UPDATE_INFO_ALERTS: 956 return R.bool.area_update_info_alerts_enabled_default; 957 default: 958 return 0; 959 } 960 } 961 962 /** 963 * Reset the resources cache. 964 */ 965 @VisibleForTesting resetResourcesCache()966 public static void resetResourcesCache() { 967 synchronized (sCacheLock) { 968 sResourcesCacheByOperator.clear(); 969 } 970 } 971 } 972