/*
 * 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<String, Resources> 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.widget.R.id.content_frame);
        if (fragment == null) {
            fragment = new CellBroadcastSettingsFragment();
            getFragmentManager()
                    .beginTransaction()
                    .add(com.android.settingslib.widget.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.
                mEnableVibrateCheckBox.setVisible(isVibrationToggleVisible(getContext(), res));
            }
            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();
        }
    }

    /**
     * Check whether vibration toggle is visible
     * @param context Context
     * @param res resources
     */
    public static boolean isVibrationToggleVisible(Context context, Resources res) {
        Vibrator vibrator = context.getSystemService(Vibrator.class);
        boolean supportVibration = (vibrator != null) && vibrator.hasVibrator();
        boolean isVibrationToggleVisible = supportVibration
                && (res.getBoolean(R.bool.show_override_dnd_settings)
                || !res.getBoolean(R.bool.override_dnd));
        return isVibrationToggleVisible;
    }

    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();
        }
    }
}
