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.app.ActionBar; 20 import android.app.Activity; 21 import android.app.Fragment; 22 import android.app.backup.BackupManager; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.os.Bundle; 28 import android.os.PersistableBundle; 29 import android.os.UserManager; 30 import android.provider.Settings; 31 import android.support.v14.preference.PreferenceFragment; 32 import android.support.v7.preference.ListPreference; 33 import android.support.v7.preference.Preference; 34 import android.support.v7.preference.PreferenceCategory; 35 import android.support.v7.preference.PreferenceScreen; 36 import android.support.v7.preference.TwoStatePreference; 37 import android.telephony.CarrierConfigManager; 38 import android.telephony.SubscriptionManager; 39 import android.util.Log; 40 import android.view.MenuItem; 41 42 43 /** 44 * Settings activity for the cell broadcast receiver. 45 */ 46 public class CellBroadcastSettings extends Activity { 47 48 private static final String TAG = "CellBroadcastSettings"; 49 50 private static final boolean DBG = false; 51 52 // Preference key for a master toggle to enable/disable all alerts message (default enabled). 53 public static final String KEY_ENABLE_ALERTS_MASTER_TOGGLE = "enable_alerts_master_toggle"; 54 55 // Preference key for whether to enable public safety messages (default enabled). 56 public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES = "enable_public_safety_messages"; 57 58 // Preference key for whether to enable emergency alerts (default enabled). 59 public static final String KEY_ENABLE_EMERGENCY_ALERTS = "enable_emergency_alerts"; 60 61 // Enable vibration on alert (unless master volume is silent). 62 public static final String KEY_ENABLE_ALERT_VIBRATE = "enable_alert_vibrate"; 63 64 // Speak contents of alert after playing the alert sound. 65 public static final String KEY_ENABLE_ALERT_SPEECH = "enable_alert_speech"; 66 67 // Always play at full volume when playing the alert sound. 68 public static final String KEY_USE_FULL_VOLUME = "use_full_volume"; 69 70 // Preference category for emergency alert and CMAS settings. 71 public static final String KEY_CATEGORY_EMERGENCY_ALERTS = "category_emergency_alerts"; 72 73 // Preference category for alert preferences. 74 public static final String KEY_CATEGORY_ALERT_PREFERENCES = "category_alert_preferences"; 75 76 // Whether to display CMAS extreme threat notifications (default is enabled). 77 public static final String KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS = 78 "enable_cmas_extreme_threat_alerts"; 79 80 // Whether to display CMAS severe threat notifications (default is enabled). 81 public static final String KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS = 82 "enable_cmas_severe_threat_alerts"; 83 84 // Whether to display CMAS amber alert messages (default is enabled). 85 public static final String KEY_ENABLE_CMAS_AMBER_ALERTS = "enable_cmas_amber_alerts"; 86 87 // Preference category for development settings (enabled by settings developer options toggle). 88 public static final String KEY_CATEGORY_DEV_SETTINGS = "category_dev_settings"; 89 90 // Whether to display monthly test messages (default is disabled). 91 public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts"; 92 93 // Preference key for whether to enable area update information notifications 94 // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden. 95 public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS = 96 "enable_area_update_info_alerts"; 97 98 // Preference key for initial opt-in/opt-out dialog. 99 public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; 100 101 // Alert reminder interval ("once" = single 2 minute reminder). 102 public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; 103 104 // Preference key for emergency alerts history 105 public static final String KEY_EMERGENCY_ALERT_HISTORY = "emergency_alert_history"; 106 107 // For watch layout 108 private static final String KEY_WATCH_ALERT_REMINDER = "watch_alert_reminder"; 109 110 @Override 111 public void onCreate(Bundle savedInstanceState) { 112 super.onCreate(savedInstanceState); 113 114 ActionBar actionBar = getActionBar(); 115 if (actionBar != null) { 116 // android.R.id.home will be triggered in onOptionsItemSelected() 117 actionBar.setDisplayHomeAsUpEnabled(true); 118 } 119 120 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 121 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) { 122 setContentView(R.layout.cell_broadcast_disallowed_preference_screen); 123 return; 124 } 125 126 // We only add new CellBroadcastSettingsFragment if no fragment is restored. 127 Fragment fragment = getFragmentManager().findFragmentById(android.R.id.content); 128 if (fragment == null) { 129 getFragmentManager().beginTransaction().add(android.R.id.content, 130 new CellBroadcastSettingsFragment()).commit(); 131 } 132 } 133 134 @Override 135 public boolean onOptionsItemSelected(MenuItem item) { 136 switch (item.getItemId()) { 137 // Respond to the action bar's Up/Home button 138 case android.R.id.home: 139 finish(); 140 return true; 141 } 142 return super.onOptionsItemSelected(item); 143 } 144 145 /** 146 * New fragment-style implementation of preferences. 147 */ 148 public static class CellBroadcastSettingsFragment extends PreferenceFragment { 149 150 private TwoStatePreference mExtremeCheckBox; 151 private TwoStatePreference mSevereCheckBox; 152 private TwoStatePreference mAmberCheckBox; 153 private TwoStatePreference mMasterToggle; 154 private TwoStatePreference mPublicSafetyMessagesChannelCheckBox; 155 private TwoStatePreference mEmergencyAlertsCheckBox; 156 private ListPreference mReminderInterval; 157 private TwoStatePreference mSpeechCheckBox; 158 private TwoStatePreference mFullVolumeCheckBox; 159 private TwoStatePreference mAreaUpdateInfoCheckBox; 160 private TwoStatePreference mTestCheckBox; 161 private Preference mAlertHistory; 162 private PreferenceCategory mAlertCategory; 163 private PreferenceCategory mAlertPreferencesCategory; 164 private PreferenceCategory mDevSettingCategory; 165 private boolean mDisableSevereWhenExtremeDisabled = true; 166 167 // WATCH 168 private TwoStatePreference mAlertReminder; 169 170 @Override 171 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 172 173 // Load the preferences from an XML resource 174 PackageManager pm = getActivity().getPackageManager(); 175 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 176 addPreferencesFromResource(R.xml.watch_preferences); 177 } else { 178 addPreferencesFromResource(R.xml.preferences); 179 } 180 181 PreferenceScreen preferenceScreen = getPreferenceScreen(); 182 183 mExtremeCheckBox = (TwoStatePreference) 184 findPreference(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS); 185 mSevereCheckBox = (TwoStatePreference) 186 findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS); 187 mAmberCheckBox = (TwoStatePreference) 188 findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS); 189 mMasterToggle = (TwoStatePreference) 190 findPreference(KEY_ENABLE_ALERTS_MASTER_TOGGLE); 191 mPublicSafetyMessagesChannelCheckBox = (TwoStatePreference) 192 findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES); 193 mEmergencyAlertsCheckBox = (TwoStatePreference) 194 findPreference(KEY_ENABLE_EMERGENCY_ALERTS); 195 mReminderInterval = (ListPreference) 196 findPreference(KEY_ALERT_REMINDER_INTERVAL); 197 mSpeechCheckBox = (TwoStatePreference) 198 findPreference(KEY_ENABLE_ALERT_SPEECH); 199 mFullVolumeCheckBox = (TwoStatePreference) 200 findPreference(KEY_USE_FULL_VOLUME); 201 mAreaUpdateInfoCheckBox = (TwoStatePreference) 202 findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS); 203 mTestCheckBox = (TwoStatePreference) 204 findPreference(KEY_ENABLE_TEST_ALERTS); 205 mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY); 206 mDevSettingCategory = (PreferenceCategory) 207 findPreference(KEY_CATEGORY_DEV_SETTINGS); 208 209 if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 210 mAlertReminder = (TwoStatePreference) 211 findPreference(KEY_WATCH_ALERT_REMINDER); 212 if (Integer.valueOf(mReminderInterval.getValue()) == 0) { 213 mAlertReminder.setChecked(false); 214 } else { 215 mAlertReminder.setChecked(true); 216 } 217 mAlertReminder.setOnPreferenceChangeListener((p, newVal) -> { 218 try { 219 mReminderInterval.setValueIndex((Boolean) newVal ? 1 : 3); 220 } catch (IndexOutOfBoundsException e) { 221 mReminderInterval.setValue(String.valueOf(0)); 222 Log.w(TAG, "Setting default value"); 223 } 224 return true; 225 }); 226 PreferenceScreen watchScreen = (PreferenceScreen) 227 findPreference(KEY_CATEGORY_ALERT_PREFERENCES); 228 watchScreen.removePreference(mReminderInterval); 229 } else { 230 mAlertPreferencesCategory = (PreferenceCategory) 231 findPreference(KEY_CATEGORY_ALERT_PREFERENCES); 232 mAlertCategory = (PreferenceCategory) 233 findPreference(KEY_CATEGORY_EMERGENCY_ALERTS); 234 } 235 236 237 mDisableSevereWhenExtremeDisabled = isFeatureEnabled(getContext(), 238 CarrierConfigManager.KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true); 239 240 // Handler for settings that require us to reconfigure enabled channels in radio 241 Preference.OnPreferenceChangeListener startConfigServiceListener = 242 new Preference.OnPreferenceChangeListener() { 243 @Override 244 public boolean onPreferenceChange(Preference pref, Object newValue) { 245 CellBroadcastReceiver.startConfigService(pref.getContext()); 246 247 if (mDisableSevereWhenExtremeDisabled) { 248 if (pref.getKey().equals(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)) { 249 boolean isExtremeAlertChecked = (Boolean) newValue; 250 if (mSevereCheckBox != null) { 251 mSevereCheckBox.setEnabled(isExtremeAlertChecked); 252 mSevereCheckBox.setChecked(false); 253 } 254 } 255 } 256 257 if (pref.getKey().equals(KEY_ENABLE_ALERTS_MASTER_TOGGLE)) { 258 boolean isEnableAlerts = (Boolean) newValue; 259 setAlertsEnabled(isEnableAlerts); 260 } 261 262 // Notify backup manager a backup pass is needed. 263 new BackupManager(getContext()).dataChanged(); 264 return true; 265 } 266 }; 267 268 // Show extra settings when developer options is enabled in settings. 269 boolean enableDevSettings = Settings.Global.getInt(getContext().getContentResolver(), 270 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; 271 272 Resources res = getResources(); 273 274 initReminderIntervalList(); 275 276 boolean emergencyAlertOnOffOptionEnabled = isFeatureEnabled(getContext(), 277 CarrierConfigManager.KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); 278 279 if (enableDevSettings || emergencyAlertOnOffOptionEnabled) { 280 // enable/disable all alerts except CMAS presidential alerts. 281 if (mMasterToggle != null) { 282 mMasterToggle.setOnPreferenceChangeListener(startConfigServiceListener); 283 // If allow alerts are disabled, we turn all sub-alerts off. If it's enabled, we 284 // leave them as they are. 285 if (!mMasterToggle.isChecked()) { 286 setAlertsEnabled(false); 287 } 288 } 289 } else { 290 if (mMasterToggle != null) preferenceScreen.removePreference(mMasterToggle); 291 } 292 293 boolean hideTestAlertMenu = CellBroadcastSettings.isFeatureEnabled(getContext(), 294 CarrierConfigManager.KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false); 295 296 // Check if we want to hide the test alert toggle. 297 if (hideTestAlertMenu || !enableDevSettings || !isTestAlertsAvailable()) { 298 if (mTestCheckBox != null) { 299 mAlertCategory.removePreference(mTestCheckBox); 300 } 301 } 302 303 if (!enableDevSettings && !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { 304 if (mDevSettingCategory != null) { 305 preferenceScreen.removePreference(mDevSettingCategory); 306 } 307 } 308 309 // Remove preferences 310 if (!res.getBoolean(R.bool.show_cmas_settings)) { 311 // Remove CMAS preference items in emergency alert category. 312 if (mAlertCategory != null) { 313 if (mExtremeCheckBox != null) mAlertCategory.removePreference(mExtremeCheckBox); 314 if (mSevereCheckBox != null) mAlertCategory.removePreference(mSevereCheckBox); 315 if (mAmberCheckBox != null) mAlertCategory.removePreference(mAmberCheckBox); 316 } 317 } 318 319 if (!Resources.getSystem().getBoolean( 320 com.android.internal.R.bool.config_showAreaUpdateInfoSettings)) { 321 if (mAlertCategory != null) { 322 if (mAreaUpdateInfoCheckBox != null) { 323 mAlertCategory.removePreference(mAreaUpdateInfoCheckBox); 324 } 325 } 326 } 327 328 // Remove preferences based on range configurations 329 if (CellBroadcastChannelManager.getCellBroadcastChannelRanges( 330 this.getContext(), 331 R.array.public_safety_messages_channels_range_strings).isEmpty()) { 332 // Remove public safety messages 333 if (mAlertCategory != null) { 334 if (mPublicSafetyMessagesChannelCheckBox != null) { 335 mAlertCategory.removePreference(mPublicSafetyMessagesChannelCheckBox); 336 } 337 } 338 } 339 340 if (CellBroadcastChannelManager.getCellBroadcastChannelRanges( 341 this.getContext(), R.array.emergency_alerts_channels_range_strings).isEmpty()) { 342 // Remove emergency alert messages 343 if (mAlertCategory != null) { 344 if (mEmergencyAlertsCheckBox != null) { 345 mAlertCategory.removePreference(mEmergencyAlertsCheckBox); 346 } 347 } 348 } 349 350 if (mAreaUpdateInfoCheckBox != null) { 351 mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 352 } 353 if (mExtremeCheckBox != null) { 354 mExtremeCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 355 } 356 if (mPublicSafetyMessagesChannelCheckBox != null) { 357 mPublicSafetyMessagesChannelCheckBox.setOnPreferenceChangeListener( 358 startConfigServiceListener); 359 } 360 if (mEmergencyAlertsCheckBox != null) { 361 mEmergencyAlertsCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 362 } 363 if (mSevereCheckBox != null) { 364 mSevereCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 365 if (mDisableSevereWhenExtremeDisabled) { 366 if (mExtremeCheckBox != null) { 367 mSevereCheckBox.setEnabled(mExtremeCheckBox.isChecked()); 368 } 369 } 370 } 371 if (mAmberCheckBox != null) { 372 mAmberCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 373 } 374 if (mTestCheckBox != null) { 375 mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); 376 } 377 378 if (mAlertHistory != null) { 379 mAlertHistory.setOnPreferenceClickListener( 380 new Preference.OnPreferenceClickListener() { 381 @Override 382 public boolean onPreferenceClick(final Preference preference) { 383 final Intent intent = new Intent(getContext(), 384 CellBroadcastListActivity.class); 385 startActivity(intent); 386 return true; 387 } 388 }); 389 } 390 } 391 392 private boolean isTestAlertsAvailable() { 393 return !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 394 this.getContext(), R.array.required_monthly_test_range_strings).isEmpty() 395 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 396 this.getContext(), R.array.exercise_alert_range_strings).isEmpty() 397 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 398 this.getContext(), R.array.operator_defined_alert_range_strings) 399 .isEmpty() 400 || !CellBroadcastChannelManager.getCellBroadcastChannelRanges( 401 this.getContext(), R.array.etws_test_alerts_range_strings).isEmpty(); 402 } 403 404 private void initReminderIntervalList() { 405 406 String[] activeValues = 407 getResources().getStringArray(R.array.alert_reminder_interval_active_values); 408 String[] allEntries = 409 getResources().getStringArray(R.array.alert_reminder_interval_entries); 410 String[] newEntries = new String[activeValues.length]; 411 412 // Only add active interval to the list 413 for (int i = 0; i < activeValues.length; i++) { 414 int index = mReminderInterval.findIndexOfValue(activeValues[i]); 415 if (index != -1) { 416 newEntries[i] = allEntries[index]; 417 if (DBG) Log.d(TAG, "Added " + allEntries[index]); 418 } else { 419 Log.e(TAG, "Can't find " + activeValues[i]); 420 } 421 } 422 423 mReminderInterval.setEntries(newEntries); 424 mReminderInterval.setEntryValues(activeValues); 425 mReminderInterval.setSummary(mReminderInterval.getEntry()); 426 mReminderInterval.setOnPreferenceChangeListener( 427 new Preference.OnPreferenceChangeListener() { 428 @Override 429 public boolean onPreferenceChange(Preference pref, Object newValue) { 430 final ListPreference listPref = (ListPreference) pref; 431 final int idx = listPref.findIndexOfValue((String) newValue); 432 listPref.setSummary(listPref.getEntries()[idx]); 433 return true; 434 } 435 }); 436 } 437 438 439 private void setAlertsEnabled(boolean alertsEnabled) { 440 if (mSevereCheckBox != null) { 441 mSevereCheckBox.setEnabled(alertsEnabled); 442 mSevereCheckBox.setChecked(alertsEnabled); 443 } 444 if (mExtremeCheckBox != null) { 445 mExtremeCheckBox.setEnabled(alertsEnabled); 446 mExtremeCheckBox.setChecked(alertsEnabled); 447 } 448 if (mAmberCheckBox != null) { 449 mAmberCheckBox.setEnabled(alertsEnabled); 450 mAmberCheckBox.setChecked(alertsEnabled); 451 } 452 if (mAreaUpdateInfoCheckBox != null) { 453 mAreaUpdateInfoCheckBox.setEnabled(alertsEnabled); 454 mAreaUpdateInfoCheckBox.setChecked(alertsEnabled); 455 } 456 if (mAlertPreferencesCategory != null) { 457 mAlertPreferencesCategory.setEnabled(alertsEnabled); 458 } 459 if (mDevSettingCategory != null) { 460 mDevSettingCategory.setEnabled(alertsEnabled); 461 } 462 if (mEmergencyAlertsCheckBox != null) { 463 mEmergencyAlertsCheckBox.setEnabled(alertsEnabled); 464 mEmergencyAlertsCheckBox.setChecked(alertsEnabled); 465 } 466 if (mPublicSafetyMessagesChannelCheckBox != null) { 467 mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled); 468 mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled); 469 } 470 } 471 } 472 473 public static boolean isFeatureEnabled(Context context, String feature, boolean defaultValue) { 474 int subId = SubscriptionManager.getDefaultSmsSubscriptionId(); 475 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 476 subId = SubscriptionManager.getDefaultSubscriptionId(); 477 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 478 return defaultValue; 479 } 480 } 481 482 CarrierConfigManager configManager = 483 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 484 485 if (configManager != null) { 486 PersistableBundle carrierConfig = configManager.getConfigForSubId(subId); 487 488 if (carrierConfig != null) { 489 return carrierConfig.getBoolean(feature, defaultValue); 490 } 491 } 492 493 return defaultValue; 494 } 495 } 496