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