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