com.android.switchaccess.SwitchAccessPreferenceActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.android.switchaccess.SwitchAccessPreferenceActivity.java

Source

/*
 * Copyright (C) 2015 Google Inc.
 *
 * 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.switchaccess;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.ListPreference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.support.v4.os.BuildCompat;
import android.text.TextUtils;

import com.android.talkback.R;
import com.android.utils.SharedPreferencesUtils;

import android.os.Bundle;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Preference activity for switch access.
 *
 * PreferenceActivity contains various deprecated methods because
 * PreferenceFragment is preferred. PreferenceFragment, however,
 * can only be added to activities, not services.
 */
@SuppressWarnings("deprecation")
public class SwitchAccessPreferenceActivity extends PreferenceActivity implements OnPreferenceChangeListener {

    private static final int SINGLE_VALUE = 1;

    private static final int MANY_VALUE = 2;

    private static final double PRECISION = 0.01;

    /**
     * Check if option scanning is enabled
     *
     * @param context The current context
     * @return {@code true} if option scanning is enabled in the preferences, {@code false}
     * otherwise
     */
    public static boolean isOptionScanningEnabled(Context context) {
        String optionScanKey = context.getString(R.string.option_scanning_key);
        String scanPref = SharedPreferencesUtils.getSharedPreferences(context).getString(
                context.getString(R.string.pref_scanning_methods_key),
                context.getString(R.string.pref_scanning_methods_default));
        return TextUtils.equals(scanPref, optionScanKey);
    }

    /**
     * Check if auto-scanning is enabled
     *
     * @param context The current context
     * @return {@code true} if auto scan is enabled in the preferences, {@code false} otherwise
     */
    public static boolean isAutoScanEnabled(Context context) {
        final SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(context);
        return prefs.getBoolean(context.getString(R.string.pref_key_auto_scan_enabled),
                Boolean.parseBoolean(context.getString(R.string.pref_auto_scan_default_value)));
    }

    /**
     * Check the global menu preference for auto-select
     *
     * @param context The current context
     * @return {@code true} auto-selecting is controlled from the auto-scan menu,
     * {@code false} otherwise
     */
    public static boolean isGlobalMenuAutoselectOn(Context context) {
        SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(context);
        String autoselectGlobalMenuPrefValue = prefs.getString(
                context.getString(R.string.switch_access_choose_action_global_menu_behavior_key),
                context.getString(R.string.switch_access_pref_choose_action_behavior_default));
        return TextUtils.equals(autoselectGlobalMenuPrefValue,
                context.getString(R.string.switch_access_choose_action_auto_select_key));
    }

    /**
     * Set the global menu preference for auto-select
     *
     * @param context The current context
     * @param autoselectOn {@code true} to enable auto-select when the global menu controls
     * the preference, {@code false} to disable it.
     */
    public static void setGlobalMenuAutoselectOn(Context context, boolean autoselectOn) {
        SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(context);
        String newStringValue = (autoselectOn)
                ? context.getString(R.string.switch_access_choose_action_auto_select_key)
                : context.getString(R.string.switch_access_choose_action_show_menu_key);
        prefs.edit().putString(context.getString(R.string.switch_access_choose_action_global_menu_behavior_key),
                newStringValue).apply();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (BuildCompat.isAtLeastN()) {
            getPreferenceManager().setStorageDeviceProtected();
        }

        addPreferencesFromResource(R.xml.switch_access_preferences);

        /* Set the summary to be the current value of the preference */
        final Preference autoScanDelayPref = findPreference(getString(R.string.pref_key_auto_scan_time_delay));
        double currentValue;
        try {
            SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(this);
            currentValue = Double.parseDouble(prefs.getString(getString(R.string.pref_key_auto_scan_time_delay),
                    getString(R.string.pref_auto_scan_time_delay_default_value)));
        } catch (NumberFormatException e) {
            currentValue = Double.parseDouble(getString(R.string.pref_auto_scan_time_delay_default_value));
        }
        final int count = Math.abs(currentValue - SINGLE_VALUE) < PRECISION ? SINGLE_VALUE : MANY_VALUE;
        autoScanDelayPref.setSummary(getResources().getQuantityString(R.plurals.auto_scan_time_delay_summary_format,
                count, currentValue));

        autoScanDelayPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
            /**
             * Update the current summary of the delay preference. The newValue for the delay is
             * guaranteed to be an integer (or an empty string which is not handled).
             */
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                final String newValueString = newValue.toString();
                if (TextUtils.isEmpty(newValueString)) {
                    return false;
                }
                try {
                    final double value = Double.parseDouble(newValueString);
                    final int count = Math.abs(value - SINGLE_VALUE) < PRECISION ? SINGLE_VALUE : MANY_VALUE;
                    preference.setSummary(getResources()
                            .getQuantityString(R.plurals.auto_scan_time_delay_summary_format, count, value));
                    return true;
                } catch (NumberFormatException e) {
                    return false;
                }
            }
        });

        int[] prefsToListenTo = { R.string.pref_scanning_methods_key, R.string.pref_key_auto_scan_enabled,
                R.string.pref_key_mapped_to_click_key, R.string.pref_key_mapped_to_next_key,
                R.string.pref_key_mapped_to_switch_3_key, R.string.pref_key_mapped_to_switch_4_key,
                R.string.pref_key_mapped_to_switch_5_key };
        for (int prefToListenTo : prefsToListenTo) {
            findPreference(getString(prefToListenTo)).setOnPreferenceChangeListener(this);
        }
        adjustHighlightingPrefs();
        adjustKeysForScanning();
        adjustAutoscanPrefs();

        // Add listener to "Help & feedback" preference.
        Preference helpAndFeedbackPreference = findPreference(getString(R.string.pref_help_feedback_key));
        helpAndFeedbackPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                HelpUtils.launchHelp(SwitchAccessPreferenceActivity.this);
                return true;
            }
        });

    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        recreate();
        return true;
    }

    /*
     * Adjust the highlighting preferences to show appropriate values for option scanning or
     * row-column.
     */
    private void adjustHighlightingPrefs() {
        PreferenceScreen mainPrefScreen = (PreferenceScreen) findPreference(
                getString(R.string.main_pref_screen_key));
        if (isOptionScanningEnabled(this)) {
            // Configure the switch names. User-facing numbers start at 1
            int[] highlightPrefKeys = { R.string.pref_highlight_0_key, R.string.pref_highlight_1_key,
                    R.string.pref_highlight_2_key, R.string.pref_highlight_3_key, R.string.pref_highlight_4_key };
            for (int i = 0; i < highlightPrefKeys.length; i++) {
                findPreference(getString(highlightPrefKeys[i]))
                        .setTitle(String.format(getString(R.string.option_scan_switch_format), i + 1));
            }
            mainPrefScreen.removePreference(findPreference(getString(R.string.pref_standard_highlight_key)));
        } else {
            mainPrefScreen.removePreference(findPreference(getString(R.string.pref_highlights_key)));
        }
    }

    /*
     * If option scanning is disabled, remove all keys related to it. Keep all preferences with
     * keys assigned (otherwise there's no way to clear those assignments). If option scanning
     * is enabled, assign all titles for the applicable key assignments.
     */
    private void adjustKeysForScanning() {
        int[] optionScanKeyAssignmentKeys = { R.string.pref_key_mapped_to_click_key,
                R.string.pref_key_mapped_to_next_key, R.string.pref_key_mapped_to_switch_3_key,
                R.string.pref_key_mapped_to_switch_4_key, R.string.pref_key_mapped_to_switch_5_key };
        PreferenceScreen keyAssignmentScreen = (PreferenceScreen) findPreference(
                getString(R.string.pref_category_scan_mappings_key));

        if (!isOptionScanningEnabled(this)) {
            for (int i = 2; i < optionScanKeyAssignmentKeys.length; i++) {
                findPreference(getString(optionScanKeyAssignmentKeys[i]))
                        .setTitle(String.format(getString(R.string.option_scan_switch_format), i + 1));
                removeKeyAssignmentPressIfEmpty(keyAssignmentScreen, optionScanKeyAssignmentKeys[i]);
            }
        } else {
            int numSwitchesConfigured = 0;
            for (int i = 0; i < optionScanKeyAssignmentKeys.length; i++) {
                int key = optionScanKeyAssignmentKeys[i];
                numSwitchesConfigured += prefHasKeyAssigned(key) ? 1 : 0;
                findPreference(getString(key))
                        .setTitle(String.format(getString(R.string.option_scan_switch_format), i + 1));
                /* Limit key assignment options to those that are useful given current config */
                if ((i >= 2) && (numSwitchesConfigured < i)) {
                    removeKeyAssignmentPressIfEmpty(keyAssignmentScreen, key);
                }
            }
        }
    }

    /*
     * If auto-scanning is disabled, remove all preferences related to it except for key assignment
     * for actions that are already configured.
     * If auto-scannning is enabled, don't allow option scanning to be enabled.
     * If option scanning is enabled, don't allow auto-scanning to be enabled.
     * If both are enabled (which shouldn't be possible, disable option scanning).
     */
    private void adjustAutoscanPrefs() {
        if (isAutoScanEnabled(this)) {
            ListPreference scanMethodsPref = (ListPreference) findPreference(
                    getString(R.string.pref_scanning_methods_key));
            String optionScanKey = getString(R.string.option_scanning_key);
            if (isOptionScanningEnabled(this)) {
                /* If somehow both autoscan and option scan are enabled, turn off option scan */
                scanMethodsPref.setValue(getString(R.string.row_col_scanning_key));
                SharedPreferencesUtils.getSharedPreferences(this).edit()
                        .putString(getString(R.string.pref_scanning_methods_key),
                                getString(R.string.row_col_scanning_key))
                        .commit();
            }
            ArrayList<String> entries = new ArrayList<>(
                    Arrays.asList(getResources().getStringArray(R.array.switch_access_scanning_methods_entries)));
            ArrayList<String> entryValues = new ArrayList<>(
                    Arrays.asList(getResources().getStringArray(R.array.switch_access_scanning_methods_values)));
            int optionScanIndex = entryValues.indexOf(optionScanKey);
            entries.remove(optionScanIndex);
            entryValues.remove(optionScanIndex);
            scanMethodsPref.setEntries(entries.toArray(new String[entries.size()]));
            scanMethodsPref.setEntryValues(entryValues.toArray(new String[entries.size()]));
        } else {
            PreferenceScreen mainPrefScreen = (PreferenceScreen) findPreference(
                    getString(R.string.main_pref_screen_key));
            PreferenceScreen keyAssignmentScreen = (PreferenceScreen) findPreference(
                    getString(R.string.pref_category_scan_mappings_key));
            mainPrefScreen.removePreference(findPreference(getString(R.string.pref_key_auto_scan_time_delay)));
            removeKeyAssignmentPressIfEmpty(keyAssignmentScreen, R.string.pref_key_mapped_to_auto_scan_key);
            removeKeyAssignmentPressIfEmpty(keyAssignmentScreen, R.string.pref_key_mapped_to_reverse_auto_scan_key);
            if (isOptionScanningEnabled(this)) {
                mainPrefScreen.removePreference(findPreference(getString(R.string.pref_key_auto_scan_enabled)));
            }
        }
    }

    /**
     * @return {@code true} if preference was empty and has been removed
     */
    private void removeKeyAssignmentPressIfEmpty(PreferenceScreen keyAssignmentScreen, int prefKeyStringId) {
        if (!prefHasKeyAssigned(prefKeyStringId)) {
            keyAssignmentScreen.removePreference(findPreference(getString(prefKeyStringId)));
        }
    }

    private boolean prefHasKeyAssigned(int prefKeyStringId) {
        return (KeyComboPreference.getKeyCodesForPreference(this, getString(prefKeyStringId)).size() != 0);
    }
}