/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.cellbroadcastreceiver; import android.annotation.NonNull; import android.app.ActionBar; import android.app.ActivityManager; import android.app.Fragment; import android.app.backup.BackupManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; import android.os.UserManager; import android.os.Vibrator; import android.telephony.SubscriptionManager; import android.util.Log; import android.view.MenuItem; import android.widget.Switch; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceFragment; import androidx.preference.PreferenceManager; import androidx.preference.TwoStatePreference; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; import com.android.settingslib.widget.MainSwitchPreference; import com.android.settingslib.widget.OnMainSwitchChangeListener; import java.util.HashMap; import java.util.Map; /** * Settings activity for the cell broadcast receiver. */ public class CellBroadcastSettings extends CollapsingToolbarBaseActivity { private static final String TAG = "CellBroadcastSettings"; private static final boolean DBG = false; /** * Keys for user preferences. * When adding a new preference, make sure to clear its value in resetAllPreferences. */ // Preference key for alert header (A text view, not clickable). public static final String KEY_ALERTS_HEADER = "alerts_header"; // Preference key for a main toggle to enable/disable all alerts message (default enabled). public static final String KEY_ENABLE_ALERTS_MASTER_TOGGLE = "enable_alerts_master_toggle"; // Preference key for whether to enable public safety messages (default enabled). public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES = "enable_public_safety_messages"; // Preference key for whether to show full-screen public safety message (pop-up dialog), If set // to false, only display from message history and sms inbox if enabled. A foreground // notification might also be shown if enabled. public static final String KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN = "enable_public_safety_messages_full_screen"; // Preference key for whether to enable emergency alerts (default enabled). public static final String KEY_ENABLE_EMERGENCY_ALERTS = "enable_emergency_alerts"; // Enable vibration on alert (unless main volume is silent). public static final String KEY_ENABLE_ALERT_VIBRATE = "enable_alert_vibrate"; // Speak contents of alert after playing the alert sound. public static final String KEY_ENABLE_ALERT_SPEECH = "enable_alert_speech"; // Play alert sound in full volume regardless Do Not Disturb is on. public static final String KEY_OVERRIDE_DND = "override_dnd"; public static final String KEY_OVERRIDE_DND_SETTINGS_CHANGED = "override_dnd_settings_changed"; // Preference category for emergency alert and CMAS settings. public static final String KEY_CATEGORY_EMERGENCY_ALERTS = "category_emergency_alerts"; // Preference category for alert preferences. public static final String KEY_CATEGORY_ALERT_PREFERENCES = "category_alert_preferences"; // Show checkbox for Presidential alerts in settings // Whether to display CMAS presidential alert notifications (always enabled). public static final String KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS = "enable_cmas_presidential_alerts"; // Whether to display CMAS extreme threat notifications (default is enabled). public static final String KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS = "enable_cmas_extreme_threat_alerts"; // Whether to display CMAS severe threat notifications (default is enabled). public static final String KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS = "enable_cmas_severe_threat_alerts"; // Whether to display CMAS amber alert messages (default is enabled). public static final String KEY_ENABLE_CMAS_AMBER_ALERTS = "enable_cmas_amber_alerts"; // Whether to display monthly test messages (default is disabled). public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts"; // Whether to display exercise test alerts. public static final String KEY_ENABLE_EXERCISE_ALERTS = "enable_exercise_alerts"; // Whether to display operator defined test alerts public static final String KEY_OPERATOR_DEFINED_ALERTS = "enable_operator_defined_alerts"; // Whether to display state/local test messages (default disabled). public static final String KEY_ENABLE_STATE_LOCAL_TEST_ALERTS = "enable_state_local_test_alerts"; // Preference key for whether to enable area update information notifications // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden. public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS = "enable_area_update_info_alerts"; // Preference key for initial opt-in/opt-out dialog. public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; // Alert reminder interval ("once" = single 2 minute reminder). public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; // Preference key for emergency alerts history public static final String KEY_EMERGENCY_ALERT_HISTORY = "emergency_alert_history"; // For watch layout private static final String KEY_WATCH_ALERT_REMINDER = "watch_alert_reminder"; // For top introduction info private static final String KEY_PREFS_TOP_INTRO = "alert_prefs_top_intro"; // Whether to receive alert in second language code public static final String KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE = "receive_cmas_in_second_language"; /* End of user preferences keys section. */ // Resource cache per operator private static final Map sResourcesCacheByOperator = new HashMap<>(); private static final Object sCacheLock = new Object(); // Intent sent from cellbroadcastreceiver to notify cellbroadcastservice that area info update // is disabled/enabled. private static final String AREA_INFO_UPDATE_ACTION = "com.android.cellbroadcastreceiver.action.AREA_UPDATE_INFO_ENABLED"; private static final String AREA_INFO_UPDATE_ENABLED_EXTRA = "enable"; /** * This permission is only granted to the cellbroadcast mainline module and thus can be * used for permission check within CBR and CBS. */ private static final String CBR_MODULE_PERMISSION = "com.android.cellbroadcastservice.FULL_ACCESS_CELL_BROADCAST_HISTORY"; // Key for shared preference which represents whether user has changed any preference private static final String ANY_PREFERENCE_CHANGED_BY_USER = "any_preference_changed_by_user"; @Override public void onCreate(Bundle savedInstanceState) { // for backward compatibility on R devices if (!SdkLevel.isAtLeastS()) { setCustomizeContentView(R.layout.cell_broadcast_list_collapsing_no_toobar); } super.onCreate(savedInstanceState); // for backward compatibility on R devices if (!SdkLevel.isAtLeastS()) { ActionBar actionBar = getActionBar(); if (actionBar != null) { // android.R.id.home will be triggered in onOptionsItemSelected() actionBar.setDisplayHomeAsUpEnabled(true); } } UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) { setContentView(R.layout.cell_broadcast_disallowed_preference_screen); return; } // We only add new CellBroadcastSettingsFragment if no fragment is restored. Fragment fragment = getFragmentManager().findFragmentById( com.android.settingslib.collapsingtoolbar.R.id.content_frame); if (fragment == null) { fragment = new CellBroadcastSettingsFragment(); getFragmentManager() .beginTransaction() .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame, fragment) .commit(); } } @Override public void onStart() { super.onStart(); getWindow().addSystemFlags( android.view.WindowManager.LayoutParams .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { // Respond to the action bar's Up/Home button case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } /** * Reset all user values for preferences (stored in shared preferences). * * @param c the application context */ public static void resetAllPreferences(Context c) { SharedPreferences.Editor e = PreferenceManager.getDefaultSharedPreferences(c).edit(); e.remove(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS) .remove(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS) .remove(KEY_ENABLE_CMAS_AMBER_ALERTS) .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES) .remove(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN) .remove(KEY_ENABLE_EMERGENCY_ALERTS) .remove(KEY_ALERT_REMINDER_INTERVAL) .remove(KEY_ENABLE_ALERT_SPEECH) .remove(KEY_OVERRIDE_DND) .remove(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS) .remove(KEY_ENABLE_TEST_ALERTS) .remove(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS) .remove(KEY_ENABLE_ALERT_VIBRATE) .remove(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS) .remove(KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE) .remove(KEY_ENABLE_EXERCISE_ALERTS) .remove(KEY_OPERATOR_DEFINED_ALERTS); // If the device is in test harness mode, reset main toggle should only happen on the // first boot. if (!ActivityManager.isRunningInUserTestHarness()) { Log.d(TAG, "In not test harness mode. reset main toggle."); e.remove(KEY_ENABLE_ALERTS_MASTER_TOGGLE); } e.commit(); PackageManager pm = c.getPackageManager(); if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { PreferenceManager.setDefaultValues(c, R.xml.watch_preferences, true); } else { PreferenceManager.setDefaultValues(c, R.xml.preferences, true); } setPreferenceChanged(c, false); } /** * Return true if user has modified any preference manually. * @param c the application context * @return */ public static boolean hasAnyPreferenceChanged(Context c) { return PreferenceManager.getDefaultSharedPreferences(c) .getBoolean(ANY_PREFERENCE_CHANGED_BY_USER, false); } private static void setPreferenceChanged(Context c, boolean changed) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c); sp.edit().putBoolean(ANY_PREFERENCE_CHANGED_BY_USER, changed).apply(); } /** * New fragment-style implementation of preferences. */ public static class CellBroadcastSettingsFragment extends PreferenceFragment { private TwoStatePreference mExtremeCheckBox; private TwoStatePreference mSevereCheckBox; private TwoStatePreference mAmberCheckBox; private MainSwitchPreference mMasterToggle; private TwoStatePreference mPublicSafetyMessagesChannelCheckBox; private TwoStatePreference mPublicSafetyMessagesChannelFullScreenCheckBox; private TwoStatePreference mEmergencyAlertsCheckBox; private ListPreference mReminderInterval; private TwoStatePreference mSpeechCheckBox; private TwoStatePreference mOverrideDndCheckBox; private TwoStatePreference mAreaUpdateInfoCheckBox; private TwoStatePreference mTestCheckBox; private TwoStatePreference mExerciseTestCheckBox; private TwoStatePreference mOperatorDefinedCheckBox; private TwoStatePreference mStateLocalTestCheckBox; private TwoStatePreference mEnableVibrateCheckBox; private Preference mAlertHistory; private Preference mAlertsHeader; private PreferenceCategory mAlertCategory; private PreferenceCategory mAlertPreferencesCategory; private boolean mDisableSevereWhenExtremeDisabled = true; // Show checkbox for Presidential alerts in settings private TwoStatePreference mPresidentialCheckBox; // on/off switch in settings for receiving alert in second language code private TwoStatePreference mReceiveCmasInSecondLanguageCheckBox; // Show the top introduction private Preference mTopIntroPreference; private final BroadcastReceiver mTestingModeChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED: updatePreferenceVisibility(); break; } } }; private void initPreferences() { mExtremeCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS); mSevereCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS); mAmberCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS); mMasterToggle = (MainSwitchPreference) findPreference(KEY_ENABLE_ALERTS_MASTER_TOGGLE); mPublicSafetyMessagesChannelCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES); mPublicSafetyMessagesChannelFullScreenCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN); mEmergencyAlertsCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_EMERGENCY_ALERTS); mReminderInterval = (ListPreference) findPreference(KEY_ALERT_REMINDER_INTERVAL); mSpeechCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_ALERT_SPEECH); mOverrideDndCheckBox = (TwoStatePreference) findPreference(KEY_OVERRIDE_DND); mAreaUpdateInfoCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS); mTestCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_TEST_ALERTS); mExerciseTestCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_EXERCISE_ALERTS); mOperatorDefinedCheckBox = (TwoStatePreference) findPreference(KEY_OPERATOR_DEFINED_ALERTS); mStateLocalTestCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS); mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY); mAlertsHeader = findPreference(KEY_ALERTS_HEADER); mReceiveCmasInSecondLanguageCheckBox = (TwoStatePreference) findPreference (KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE); mEnableVibrateCheckBox = findPreference(KEY_ENABLE_ALERT_VIBRATE); // Show checkbox for Presidential alerts in settings mPresidentialCheckBox = (TwoStatePreference) findPreference(KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS); PackageManager pm = getActivity().getPackageManager(); if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { mAlertPreferencesCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_ALERT_PREFERENCES); mAlertCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_EMERGENCY_ALERTS); } mTopIntroPreference = findPreference(KEY_PREFS_TOP_INTRO); } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { LocalBroadcastManager.getInstance(getContext()) .registerReceiver(mTestingModeChangedReceiver, new IntentFilter( CellBroadcastReceiver.ACTION_TESTING_MODE_CHANGED)); // Load the preferences from an XML resource PackageManager pm = getActivity().getPackageManager(); if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { addPreferencesFromResource(R.xml.watch_preferences); } else { addPreferencesFromResource(R.xml.preferences); } initPreferences(); Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); mDisableSevereWhenExtremeDisabled = res.getBoolean( R.bool.disable_severe_when_extreme_disabled); final OnMainSwitchChangeListener mainSwitchListener = new OnMainSwitchChangeListener() { @Override public void onSwitchChanged(Switch switchView, boolean isChecked) { setAlertsEnabled(isChecked); onPreferenceChangedByUser(getContext()); } }; // Handler for settings that require us to reconfigure enabled channels in radio Preference.OnPreferenceChangeListener startConfigServiceListener = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object newValue) { if (mDisableSevereWhenExtremeDisabled) { if (pref.getKey().equals(KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS)) { boolean isExtremeAlertChecked = (Boolean) newValue; if (mSevereCheckBox != null) { mSevereCheckBox.setEnabled(isExtremeAlertChecked); mSevereCheckBox.setChecked(false); } } } // check if area update was disabled if (pref.getKey().equals(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS)) { boolean isEnabledAlert = (Boolean) newValue; notifyAreaInfoUpdate(isEnabledAlert); } onPreferenceChangedByUser(getContext()); return true; } }; initReminderIntervalList(); if (mMasterToggle != null) { mMasterToggle.addOnSwitchChangeListener(mainSwitchListener); // If allow alerts are disabled, we turn all sub-alerts off. If it's enabled, we // leave them as they are. if (!mMasterToggle.isChecked()) { setAlertsEnabled(false); } } // note that mPresidentialCheckBox does not use the startConfigServiceListener because // the user is never allowed to change the preference if (mAreaUpdateInfoCheckBox != null) { mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mExtremeCheckBox != null) { mExtremeCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mPublicSafetyMessagesChannelCheckBox != null) { mPublicSafetyMessagesChannelCheckBox.setOnPreferenceChangeListener( startConfigServiceListener); } if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) { mPublicSafetyMessagesChannelFullScreenCheckBox.setOnPreferenceChangeListener( startConfigServiceListener); } if (mEmergencyAlertsCheckBox != null) { mEmergencyAlertsCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mSevereCheckBox != null) { mSevereCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); if (mDisableSevereWhenExtremeDisabled) { if (mExtremeCheckBox != null) { mSevereCheckBox.setEnabled(mExtremeCheckBox.isChecked()); } } } if (mAmberCheckBox != null) { mAmberCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mTestCheckBox != null) { mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mExerciseTestCheckBox != null) { mExerciseTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mOperatorDefinedCheckBox != null) { mOperatorDefinedCheckBox.setOnPreferenceChangeListener(startConfigServiceListener); } if (mStateLocalTestCheckBox != null) { mStateLocalTestCheckBox.setOnPreferenceChangeListener( startConfigServiceListener); } SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); if (mOverrideDndCheckBox != null) { if (!sp.getBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED, false)) { // If the user hasn't changed this settings yet, use the default settings // from resource overlay. mOverrideDndCheckBox.setChecked(res.getBoolean(R.bool.override_dnd_default)); } mOverrideDndCheckBox.setOnPreferenceChangeListener( (pref, newValue) -> { sp.edit().putBoolean(KEY_OVERRIDE_DND_SETTINGS_CHANGED, true).apply(); updateVibrationPreference((boolean) newValue); return true; }); } if (mAlertHistory != null) { mAlertHistory.setOnPreferenceClickListener( preference -> { final Intent intent = new Intent(getContext(), CellBroadcastListActivity.class); startActivity(intent); return true; }); } updateVibrationPreference(sp.getBoolean(CellBroadcastSettings.KEY_OVERRIDE_DND, false)); updatePreferenceVisibility(); } /** * Update the vibration preference based on override DND. If DND is overridden, then do * not allow users to turn off vibration. * * @param overrideDnd {@code true} if the alert will be played at full volume, regardless * DND settings. */ private void updateVibrationPreference(boolean overrideDnd) { if (mEnableVibrateCheckBox != null) { if (overrideDnd) { // If DND is enabled, always enable vibration. mEnableVibrateCheckBox.setChecked(true); } // Grey out the preference if DND is overridden. mEnableVibrateCheckBox.setEnabled(!overrideDnd); } } /** * Dynamically update each preference's visibility based on configuration. */ private void updatePreferenceVisibility() { Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); // The settings should be based on the config by the subscription CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager( getContext(), SubscriptionManager.getDefaultSubscriptionId(), null); if (mMasterToggle != null) { mMasterToggle.setVisible(res.getBoolean(R.bool.show_main_switch_settings)); } if (mPresidentialCheckBox != null) { mPresidentialCheckBox.setVisible( res.getBoolean(R.bool.show_presidential_alerts_settings)); } if (mExtremeCheckBox != null) { mExtremeCheckBox.setVisible(res.getBoolean(R.bool.show_extreme_alert_settings) && !channelManager.getCellBroadcastChannelRanges( R.array.cmas_alert_extreme_channels_range_strings).isEmpty()); } if (mSevereCheckBox != null) { mSevereCheckBox.setVisible(res.getBoolean(R.bool.show_severe_alert_settings) && !channelManager.getCellBroadcastChannelRanges( R.array.cmas_alerts_severe_range_strings).isEmpty()); } if (mAmberCheckBox != null) { mAmberCheckBox.setVisible(res.getBoolean(R.bool.show_amber_alert_settings) && !channelManager.getCellBroadcastChannelRanges( R.array.cmas_amber_alerts_channels_range_strings).isEmpty()); } if (mPublicSafetyMessagesChannelCheckBox != null) { mPublicSafetyMessagesChannelCheckBox.setVisible( res.getBoolean(R.bool.show_public_safety_settings) && !channelManager.getCellBroadcastChannelRanges( R.array.public_safety_messages_channels_range_strings) .isEmpty()); } // this is the matching full screen settings for public safety toggle. shown only if // public safety toggle is displayed. if (mPublicSafetyMessagesChannelFullScreenCheckBox != null) { mPublicSafetyMessagesChannelFullScreenCheckBox.setVisible( res.getBoolean(R.bool.show_public_safety_full_screen_settings) && (mPublicSafetyMessagesChannelCheckBox != null && mPublicSafetyMessagesChannelCheckBox.isVisible())); } if (mTestCheckBox != null) { mTestCheckBox.setVisible(isTestAlertsToggleVisible(getContext())); } if (mExerciseTestCheckBox != null) { boolean visible = false; if (res.getBoolean(R.bool.show_separate_exercise_settings)) { if (res.getBoolean(R.bool.show_exercise_settings) || CellBroadcastReceiver.isTestingMode(getContext())) { if (!channelManager.getCellBroadcastChannelRanges( R.array.exercise_alert_range_strings).isEmpty()) { visible = true; } } } mExerciseTestCheckBox.setVisible(visible); } if (mOperatorDefinedCheckBox != null) { boolean visible = false; if (res.getBoolean(R.bool.show_separate_operator_defined_settings)) { if (res.getBoolean(R.bool.show_operator_defined_settings) || CellBroadcastReceiver.isTestingMode(getContext())) { if (!channelManager.getCellBroadcastChannelRanges( R.array.operator_defined_alert_range_strings).isEmpty()) { visible = true; } } } mOperatorDefinedCheckBox.setVisible(visible); } if (mEmergencyAlertsCheckBox != null) { mEmergencyAlertsCheckBox.setVisible(!channelManager.getCellBroadcastChannelRanges( R.array.emergency_alerts_channels_range_strings).isEmpty()); } if (mStateLocalTestCheckBox != null) { mStateLocalTestCheckBox.setVisible( res.getBoolean(R.bool.show_state_local_test_settings) && !channelManager.getCellBroadcastChannelRanges( R.array.state_local_test_alert_range_strings).isEmpty()); } if (mReceiveCmasInSecondLanguageCheckBox != null) { mReceiveCmasInSecondLanguageCheckBox.setVisible(!res.getString( R.string.emergency_alert_second_language_code).isEmpty()); } if (mAreaUpdateInfoCheckBox != null) { mAreaUpdateInfoCheckBox.setVisible( res.getBoolean(R.bool.config_showAreaUpdateInfoSettings)); } if (mOverrideDndCheckBox != null) { mOverrideDndCheckBox.setVisible(res.getBoolean(R.bool.show_override_dnd_settings)); } if (mEnableVibrateCheckBox != null) { // Only show vibrate toggle when override DND toggle is available to users, or when // override DND default is turned off. // In some countries, override DND is always on, which means vibration is always on. // In that case, no need to show vibration toggle for users. Vibrator vibrator = getContext().getSystemService(Vibrator.class); boolean supportVibration = (vibrator != null) && vibrator.hasVibrator(); mEnableVibrateCheckBox.setVisible(supportVibration && (res.getBoolean(R.bool.show_override_dnd_settings) || !res.getBoolean(R.bool.override_dnd))); } if (mAlertsHeader != null) { mAlertsHeader.setVisible( !getContext().getString(R.string.alerts_header_summary).isEmpty()); } if (mSpeechCheckBox != null) { mSpeechCheckBox.setVisible(res.getBoolean(R.bool.show_alert_speech_setting) || getActivity().getPackageManager() .hasSystemFeature(PackageManager.FEATURE_WATCH)); } if (mTopIntroPreference != null) { mTopIntroPreference.setTitle(getTopIntroduction()); } } private int getTopIntroduction() { // Only set specific top introduction for roaming support now if (!CellBroadcastReceiver.getRoamingOperatorSupported(getContext()).isEmpty()) { return R.string.top_intro_roaming_text; } return R.string.top_intro_default_text; } private void initReminderIntervalList() { Resources res = CellBroadcastSettings.getResourcesForDefaultSubId(getContext()); String[] activeValues = res.getStringArray(R.array.alert_reminder_interval_active_values); String[] allEntries = res.getStringArray(R.array.alert_reminder_interval_entries); String[] newEntries = new String[activeValues.length]; // Only add active interval to the list for (int i = 0; i < activeValues.length; i++) { int index = mReminderInterval.findIndexOfValue(activeValues[i]); if (index != -1) { newEntries[i] = allEntries[index]; if (DBG) Log.d(TAG, "Added " + allEntries[index]); } else { Log.e(TAG, "Can't find " + activeValues[i]); } } mReminderInterval.setEntries(newEntries); mReminderInterval.setEntryValues(activeValues); mReminderInterval.setSummary(mReminderInterval.getEntry()); mReminderInterval.setOnPreferenceChangeListener( new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object newValue) { final ListPreference listPref = (ListPreference) pref; final int idx = listPref.findIndexOfValue((String) newValue); listPref.setSummary(listPref.getEntries()[idx]); return true; } }); } private void setAlertsEnabled(boolean alertsEnabled) { if (mSevereCheckBox != null) { mSevereCheckBox.setEnabled(alertsEnabled); mSevereCheckBox.setChecked(alertsEnabled); } if (mExtremeCheckBox != null) { mExtremeCheckBox.setEnabled(alertsEnabled); mExtremeCheckBox.setChecked(alertsEnabled); } if (mAmberCheckBox != null) { mAmberCheckBox.setEnabled(alertsEnabled); mAmberCheckBox.setChecked(alertsEnabled); } if (mAreaUpdateInfoCheckBox != null) { mAreaUpdateInfoCheckBox.setEnabled(alertsEnabled); mAreaUpdateInfoCheckBox.setChecked(alertsEnabled); notifyAreaInfoUpdate(alertsEnabled); } if (mEmergencyAlertsCheckBox != null) { mEmergencyAlertsCheckBox.setEnabled(alertsEnabled); mEmergencyAlertsCheckBox.setChecked(alertsEnabled); } if (mPublicSafetyMessagesChannelCheckBox != null) { mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled); mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled); } if (mStateLocalTestCheckBox != null) { mStateLocalTestCheckBox.setEnabled(alertsEnabled); mStateLocalTestCheckBox.setChecked(alertsEnabled); } if (mTestCheckBox != null) { mTestCheckBox.setEnabled(alertsEnabled); mTestCheckBox.setChecked(alertsEnabled); } if (mExerciseTestCheckBox != null) { mExerciseTestCheckBox.setEnabled(alertsEnabled); mExerciseTestCheckBox.setChecked(alertsEnabled); } if (mOperatorDefinedCheckBox != null) { mOperatorDefinedCheckBox.setEnabled(alertsEnabled); mOperatorDefinedCheckBox.setChecked(alertsEnabled); } } private void notifyAreaInfoUpdate(boolean enabled) { Intent areaInfoIntent = new Intent(AREA_INFO_UPDATE_ACTION); areaInfoIntent.putExtra(AREA_INFO_UPDATE_ENABLED_EXTRA, enabled); // sending broadcast protected by the permission which is only // granted for CBR mainline module. getContext().sendBroadcast(areaInfoIntent, CBR_MODULE_PERMISSION); } @Override public void onResume() { super.onResume(); updatePreferenceVisibility(); } @Override public void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(getContext()) .unregisterReceiver(mTestingModeChangedReceiver); } /** * Callback to be called when preference or master toggle is changed by user * * @param context Context to use */ public void onPreferenceChangedByUser(Context context) { CellBroadcastReceiver.startConfigService(context, CellBroadcastConfigService.ACTION_ENABLE_CHANNELS); setPreferenceChanged(context, true); // Notify backup manager a backup pass is needed. new BackupManager(context).dataChanged(); } } public static boolean isTestAlertsToggleVisible(Context context) { return isTestAlertsToggleVisible(context, null); } /** * Check whether test alert toggle is visible * @param context Context * @param operator Opeator numeric */ public static boolean isTestAlertsToggleVisible(Context context, String operator) { CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager(context, SubscriptionManager.getDefaultSubscriptionId(), operator); Resources res = operator == null ? getResourcesForDefaultSubId(context) : getResourcesByOperator(context, SubscriptionManager.getDefaultSubscriptionId(), operator); boolean isTestAlertsAvailable = !channelManager.getCellBroadcastChannelRanges( R.array.required_monthly_test_range_strings).isEmpty() || (!channelManager.getCellBroadcastChannelRanges( R.array.exercise_alert_range_strings).isEmpty() /** exercise toggle is controlled under the main test toggle */ && (!res.getBoolean(R.bool.show_separate_exercise_settings))) || (!channelManager.getCellBroadcastChannelRanges( R.array.operator_defined_alert_range_strings).isEmpty() /** operator defined toggle is controlled under the main test toggle */ && (!res.getBoolean(R.bool.show_separate_operator_defined_settings))) || !channelManager.getCellBroadcastChannelRanges( R.array.etws_test_alerts_range_strings).isEmpty(); return (res.getBoolean(R.bool.show_test_settings) || CellBroadcastReceiver.isTestingMode(context)) && isTestAlertsAvailable; } /** * Get the device resource based on SIM * * @param context Context * @param subId Subscription index * * @return The resource */ public static @NonNull Resources getResources(@NonNull Context context, int subId) { if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID || !SubscriptionManager.isValidSubscriptionId(subId)) { return context.getResources(); } return SubscriptionManager.getResourcesForSubId(context, subId); } /** * Get the resources using the default subscription ID. * @param context Context * @return the Resources for the default subscription ID, or if there is no default subscription * from SubscriptionManager, the resources for the latest loaded SIM. */ public static @NonNull Resources getResourcesForDefaultSubId(@NonNull Context context) { return getResources(context, SubscriptionManager.getDefaultSubscriptionId()); } /** * Get the resources per network operator * @param context Context * @param operator Opeator numeric * @return the Resources based on network operator */ public static @NonNull Resources getResourcesByOperator( @NonNull Context context, int subId, @NonNull String operator) { if (operator == null || operator.isEmpty()) { return getResources(context, subId); } synchronized (sCacheLock) { Resources res = sResourcesCacheByOperator.get(operator); if (res != null) { return res; } Configuration overrideConfig = new Configuration(); try { int mcc = Integer.parseInt(operator.substring(0, 3)); int mnc = operator.length() > 3 ? Integer.parseInt(operator.substring(3)) : Configuration.MNC_ZERO; overrideConfig.mcc = mcc; overrideConfig.mnc = mnc; } catch (NumberFormatException e) { // should not happen Log.e(TAG, "invalid operator: " + operator); return context.getResources(); } Context newContext = context.createConfigurationContext(overrideConfig); res = newContext.getResources(); sResourcesCacheByOperator.put(operator, res); return res; } } /** * Get the resources id which is used for the default value of the preference * @param key the preference key * @return a valid resources id if the key is valid and the default value is * defined, otherwise 0 */ public static int getResourcesIdForDefaultPrefValue(String key) { switch (key) { case KEY_ENABLE_ALERTS_MASTER_TOGGLE: return R.bool.master_toggle_enabled_default; case KEY_ENABLE_PUBLIC_SAFETY_MESSAGES: return R.bool.public_safety_messages_enabled_default; case KEY_ENABLE_PUBLIC_SAFETY_MESSAGES_FULL_SCREEN: return R.bool.public_safety_messages_full_screen_enabled_default; case KEY_ENABLE_EMERGENCY_ALERTS: return R.bool.emergency_alerts_enabled_default; case KEY_ENABLE_ALERT_SPEECH: return R.bool.enable_alert_speech_default; case KEY_OVERRIDE_DND: return R.bool.override_dnd_default; case KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS: return R.bool.extreme_threat_alerts_enabled_default; case KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS: return R.bool.severe_threat_alerts_enabled_default; case KEY_ENABLE_CMAS_AMBER_ALERTS: return R.bool.amber_alerts_enabled_default; case KEY_ENABLE_TEST_ALERTS: return R.bool.test_alerts_enabled_default; case KEY_ENABLE_EXERCISE_ALERTS: return R.bool.test_exercise_alerts_enabled_default; case KEY_OPERATOR_DEFINED_ALERTS: return R.bool.test_operator_defined_alerts_enabled_default; case KEY_ENABLE_STATE_LOCAL_TEST_ALERTS: return R.bool.state_local_test_alerts_enabled_default; case KEY_ENABLE_AREA_UPDATE_INFO_ALERTS: return R.bool.area_update_info_alerts_enabled_default; default: return 0; } } /** * Reset the resources cache. */ @VisibleForTesting public static void resetResourcesCache() { synchronized (sCacheLock) { sResourcesCacheByOperator.clear(); } } }