com.android.talkback.TalkBackKeyboardShortcutPreferencesActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.android.talkback.TalkBackKeyboardShortcutPreferencesActivity.java

Source

/*
 * Copyright 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.talkback;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.support.v4.os.BuildCompat;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;

import com.android.talkback.keyboard.DefaultKeyComboModel;
import com.android.talkback.keyboard.KeyComboModel;
import com.google.android.marvin.talkback.TalkBackService;

import java.util.HashSet;
import java.util.Set;

/**
 * Activity used to set TalkBack's keyboard shortcut preferences.
 */
public class TalkBackKeyboardShortcutPreferencesActivity extends Activity {

    /**
     * Utility method for announcing text via accessibility event.
     */
    public static void announceText(String text, Context context) {
        AccessibilityManager accessibilityManager = (AccessibilityManager) context
                .getSystemService(Context.ACCESSIBILITY_SERVICE);
        if (accessibilityManager.isEnabled()) {
            AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT);
            event.setContentDescription(text);
            accessibilityManager.sendAccessibilityEvent(event);
        }
    }

    private static void focusCancelButton(AlertDialog alertDialog) {
        Button cancelButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
        cancelButton.setFocusableInTouchMode(true);
        cancelButton.requestFocus();
    }

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

        setTitle(getString(R.string.title_pref_manage_keyboard_shortcuts));

        ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        TalkBackService talkBackService = TalkBackService.getInstance();
        KeyComboManager keyComboManager = talkBackService == null ? KeyComboManager.create(this)
                : talkBackService.getKeyComboManager();
        TalkBackKeyboardShortcutPreferenceFragment fragment = TalkBackKeyboardShortcutPreferenceFragment
                .createFor(keyComboManager.getKeymap());
        getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            finish();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    public static class TalkBackKeyboardShortcutPreferenceFragment extends PreferenceFragment {
        private static final String BUNDLE_KEYMAP = "bundle_keymap";

        private static final boolean IS_IN_ARC = TalkBackService.isInArc();

        private static final int[] HIDDEN_SHORTCUT_KEY_IDS_IN_ARC = { R.string.keycombo_shortcut_global_suspend,
                R.string.keycombo_shortcut_global_home, R.string.keycombo_shortcut_global_recents,
                R.string.keycombo_shortcut_global_notifications, R.string.keycombo_shortcut_navigate_next_window,
                R.string.keycombo_shortcut_navigate_previous_window };

        private static final int[] HIDDEN_SHORTCUT_KEY_IDS_IN_NON_ARC = {
                R.string.keycombo_shortcut_open_manage_keyboard_shortcuts,
                R.string.keycombo_shortcut_open_talkback_settings };

        public static TalkBackKeyboardShortcutPreferenceFragment createFor(String keymap) {
            TalkBackKeyboardShortcutPreferenceFragment preferenceFragment = new TalkBackKeyboardShortcutPreferenceFragment();

            Bundle bundle = new Bundle();
            bundle.putString(BUNDLE_KEYMAP, keymap);
            preferenceFragment.setArguments(bundle);

            return preferenceFragment;
        }

        private String mKeymap;

        private final OnPreferenceChangeListener mPreferenceChangeListener = new OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                String preferenceKeyForTriggerModifier = getKeyComboManager().getKeyComboModel()
                        .getPreferenceKeyForTriggerModifier();
                if (preference instanceof KeyboardShortcutDialogPreference && newValue instanceof Long) {
                    KeyboardShortcutDialogPreference keyBoardPreference = (KeyboardShortcutDialogPreference) preference;
                    keyBoardPreference.setKeyComboCode((Long) newValue);
                    keyBoardPreference.notifyChanged();
                } else if (preference.getKey() != null
                        && preference.getKey().equals(getString(R.string.pref_select_keymap_key))
                        && newValue instanceof String) {
                    String newKeymap = (String) newValue;

                    // Do nothing if keymap is the same.
                    if (mKeymap.equals(newKeymap)) {
                        return false;
                    }

                    // Replace preference fragment.
                    TalkBackKeyboardShortcutPreferenceFragment fragment = TalkBackKeyboardShortcutPreferenceFragment
                            .createFor(newKeymap);
                    getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();

                    // Set new key combo model.
                    KeyComboManager keyComboManager = getKeyComboManager();
                    keyComboManager.setKeyComboModel(keyComboManager.createKeyComboModelFor(newKeymap));

                    // Announce new keymap.
                    announceText(String.format(getString(R.string.keycombo_menu_announce_active_keymap),
                            getKeymapName(newKeymap)), getActivity());
                } else if (preference.getKey() != null
                        && preference.getKey().equals(preferenceKeyForTriggerModifier)
                        && newValue instanceof String) {
                    mTriggerModifierToBeSet = (String) newValue;

                    ListPreference listPreference = (ListPreference) preference;
                    if (listPreference.getValue().equals(mTriggerModifierToBeSet)) {
                        return false;
                    }

                    CharSequence[] entries = listPreference.getEntries();
                    CharSequence newTriggerModifier = entries[listPreference
                            .findIndexOfValue(mTriggerModifierToBeSet)];

                    // Show alert dialog.
                    AlertDialog dialog = new AlertDialog.Builder(getActivity())
                            .setTitle(R.string.keycombo_menu_alert_title_trigger_modifier)
                            .setMessage(getString(R.string.keycombo_menu_alert_message_trigger_modifier,
                                    newTriggerModifier))
                            .setPositiveButton(android.R.string.ok, mChooseTriggerModifierConfirmDialogPositive)
                            .setNegativeButton(android.R.string.cancel, mChooseTriggerModifierConfirmDialogNegative)
                            .show();

                    focusCancelButton(dialog);

                    return false;
                }

                return true;
            }
        };

        private String mTriggerModifierToBeSet;

        private final DialogInterface.OnClickListener mChooseTriggerModifierConfirmDialogPositive = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                resetKeymap();

                KeyComboModel keyComboModel = getKeyComboManager().getKeyComboModel();

                // Update preference.
                String preferenceKeyForTriggerModifier = keyComboModel.getPreferenceKeyForTriggerModifier();
                ListPreference listPreference = (ListPreference) findPreference(preferenceKeyForTriggerModifier);
                listPreference.setValue(mTriggerModifierToBeSet);

                // Update KeyComboModel.
                keyComboModel.notifyTriggerModifierChanged();

                // Update UI.
                Set<String> keySet = getKeyComboManager().getKeyComboModel().getKeyComboCodeMap().keySet();
                for (String key : keySet) {
                    KeyboardShortcutDialogPreference preference = (KeyboardShortcutDialogPreference) findPreference(
                            key);
                    preference.onTriggerModifierChanged();
                }

                // Announce that trigger modifier has changed.
                CharSequence[] entries = listPreference.getEntries();
                CharSequence newTriggerModifier = entries[listPreference.findIndexOfValue(mTriggerModifierToBeSet)];
                announceText(getString(R.string.keycombo_menu_announce_new_trigger_modifier, newTriggerModifier),
                        getActivity());

                mTriggerModifierToBeSet = null;
            }
        };

        private final DialogInterface.OnClickListener mChooseTriggerModifierConfirmDialogNegative = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mTriggerModifierToBeSet = null;
            }
        };

        private final Preference.OnPreferenceClickListener mResetKeymapPreferenceClickListener = new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                // Show confirm dialog.
                AlertDialog dialog = new AlertDialog.Builder(getActivity())
                        .setTitle(getString(R.string.keycombo_menu_reset_keymap))
                        .setMessage(getString(R.string.message_in_reset_keymap_confirm_dialog))
                        .setPositiveButton(R.string.reset_button_in_reset_keymap_confirm_dialog,
                                mResetKeymapConfirmDialogPositive)
                        .setNegativeButton(android.R.string.cancel, mResetKeymapConfirmDialogNegative).show();

                focusCancelButton(dialog);

                return true;
            }
        };

        private final DialogInterface.OnClickListener mResetKeymapConfirmDialogPositive = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                resetKeymap();

                dialogInterface.dismiss();

                announceText(getString(R.string.keycombo_menu_announce_reset_keymap), getActivity());
            }
        };

        private void resetKeymap() {
            KeyComboModel keyComboModel = getKeyComboManager().getKeyComboModel();

            for (String key : keyComboModel.getKeyComboCodeMap().keySet()) {
                long defaultKeyComboCode = keyComboModel.getDefaultKeyComboCode(key);

                // Do nothing if key combo code is not changed from default one.
                if (defaultKeyComboCode == keyComboModel.getKeyComboCodeForKey(key)) {
                    continue;
                }

                // Save with default key combo code.
                keyComboModel.saveKeyComboCode(key, defaultKeyComboCode);

                // Update UI.
                KeyboardShortcutDialogPreference keyboardShortcutDialogPreference = (KeyboardShortcutDialogPreference) findPreference(
                        key);
                keyboardShortcutDialogPreference.setKeyComboCode(defaultKeyComboCode);
                keyboardShortcutDialogPreference.notifyChanged();
            }
        }

        private final DialogInterface.OnClickListener mResetKeymapConfirmDialogNegative = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.cancel();
            }
        };

        void performClickOnResetKeymapForTesting() {
            Preference resetKeymapPreference = findPreference(getString(R.string.pref_reset_keymap_key));
            mResetKeymapPreferenceClickListener.onPreferenceClick(resetKeymapPreference);
        }

        private KeyComboManager getKeyComboManager() {
            TalkBackService talkBackService = TalkBackService.getInstance();
            return talkBackService == null ? KeyComboManager.create(getActivity())
                    : talkBackService.getKeyComboManager();
        }

        private String getKeymapName(String keymap) {
            if (keymap.equals(getString(R.string.classic_keymap_entry_value))) {
                return getString(R.string.value_classic_keymap);
            } else if (keymap.equals(getString(R.string.default_keymap_entry_value))) {
                return getString(R.string.value_default_keymap);
            }
            return null;
        }

        private int getPreferenceResourceId(String keymap) {
            if (keymap.equals(getString(R.string.classic_keymap_entry_value))) {
                return R.xml.key_combo_preferences;
            } else if (keymap.equals(getString(R.string.default_keymap_entry_value))) {
                return R.xml.default_key_combo_preferences;
            }
            return R.xml.key_combo_preferences;
        }

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

            // Set preferences to use device-protected storage.
            if (BuildCompat.isAtLeastN()) {
                getPreferenceManager().setStorageDeviceProtected();
            }

            mKeymap = getArguments().getString(BUNDLE_KEYMAP);
            addPreferencesFromResource(getPreferenceResourceId(mKeymap));

            PreferenceScreen resetKeymapPreferenceScreen = (PreferenceScreen) findPreference(
                    getString(R.string.pref_reset_keymap_key));
            resetKeymapPreferenceScreen.setOnPreferenceClickListener(mResetKeymapPreferenceClickListener);

            // Hide select keymap preference in Arc if current keymap is already set to default
            // keymap.
            if (IS_IN_ARC && getKeyComboManager().getKeyComboModel() instanceof DefaultKeyComboModel) {
                PreferenceCategory keymapPreferenceCategory = (PreferenceCategory) getPreferenceScreen()
                        .findPreference(getString(R.string.pref_keymap_category_key));
                ListPreference keymapListPreference = (ListPreference) keymapPreferenceCategory
                        .findPreference(getString(R.string.pref_select_keymap_key));
                keymapPreferenceCategory.removePreference(keymapListPreference);
            }

            int[] hiddenShortcutKeyIds = IS_IN_ARC ? HIDDEN_SHORTCUT_KEY_IDS_IN_ARC
                    : HIDDEN_SHORTCUT_KEY_IDS_IN_NON_ARC;
            Set<String> hiddenShortcutKeys = new HashSet<>();
            for (int id : hiddenShortcutKeyIds) {
                hiddenShortcutKeys.add(getString(id));
            }

            initPreferenceUIs(getPreferenceScreen(), hiddenShortcutKeys);
        }

        /**
         * Initialize preference UIs.
         * @param root Root element of preference UIs.
         * @param hiddenShortcutKeys Set of shortcut keys which will be made hidden. Note that
         *                           preference is made hidden only when its shortcut is disabled in
         *                           the key combo model.
         */
        private void initPreferenceUIs(PreferenceGroup root, Set<String> hiddenShortcutKeys) {
            if (root == null) {
                return;
            }

            final KeyComboModel keyComboModel = getKeyComboManager().getKeyComboModel();
            final String preferenceKeyForTriggerModifier = keyComboModel.getPreferenceKeyForTriggerModifier();

            for (int i = 0; i < root.getPreferenceCount(); i++) {
                final Preference preference = root.getPreference(i);
                final String key = preference.getKey();

                if (key != null && preference instanceof KeyboardShortcutDialogPreference
                        && !keyComboModel.getKeyComboCodeMap().containsKey(key)) {
                    // Disable or hide preference of unavailable key combo on this device.
                    if (hiddenShortcutKeys.contains(key)) {
                        root.removePreference(preference);
                        i--;
                    } else {
                        preference.setEnabled(false);
                    }
                } else if (preference instanceof KeyboardShortcutDialogPreference
                        || (key != null && key.equals(getString(R.string.pref_select_keymap_key)))
                        || (key != null && key.equals(preferenceKeyForTriggerModifier))) {
                    // Set onPreferenceChangeListener.
                    preference.setOnPreferenceChangeListener(mPreferenceChangeListener);
                } else if (preference instanceof PreferenceGroup) {
                    initPreferenceUIs((PreferenceGroup) preference, hiddenShortcutKeys);
                }
            }
        }
    }
}