diff options
author | 2024-12-16 21:45:41 -0500 | |
---|---|---|
committer | 2025-01-11 14:17:35 -0500 | |
commit | e9a0e66716dab4dd3184d009d8920de1961efdfa (patch) | |
tree | 02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/com/android/inputmethod/accessibility | |
parent | fb3b9360d70596d7e921de8bf7d3ca99564a077e (diff) | |
download | latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip |
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility')
7 files changed, 0 insertions, 1776 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java deleted file mode 100644 index 37d910edb..000000000 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2014 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.inputmethod.accessibility; - -import android.content.Context; -import android.os.Handler; -import android.os.Message; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.R; - -// Handling long press timer to show a more keys keyboard. -final class AccessibilityLongPressTimer extends Handler { - public interface LongPressTimerCallback { - public void performLongClickOn(Key key); - } - - private static final int MSG_LONG_PRESS = 1; - - private final LongPressTimerCallback mCallback; - private final long mConfigAccessibilityLongPressTimeout; - - public AccessibilityLongPressTimer(final LongPressTimerCallback callback, - final Context context) { - super(); - mCallback = callback; - mConfigAccessibilityLongPressTimeout = context.getResources().getInteger( - R.integer.config_accessibility_long_press_key_timeout); - } - - @Override - public void handleMessage(final Message msg) { - switch (msg.what) { - case MSG_LONG_PRESS: - cancelLongPress(); - mCallback.performLongClickOn((Key)msg.obj); - return; - default: - super.handleMessage(msg); - return; - } - } - - public void startLongPress(final Key key) { - cancelLongPress(); - final Message longPressMessage = obtainMessage(MSG_LONG_PRESS, key); - sendMessageDelayed(longPressMessage, mConfigAccessibilityLongPressTimeout); - } - - public void cancelLongPress() { - removeMessages(MSG_LONG_PRESS); - } -} diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java deleted file mode 100644 index 31e142e72..000000000 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * 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.inputmethod.accessibility; - -import android.content.Context; -import android.media.AudioManager; -import android.os.Build; -import android.os.SystemClock; -import android.provider.Settings; -import androidx.core.view.accessibility.AccessibilityEventCompat; -import android.text.TextUtils; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.inputmethod.EditorInfo; - -import com.android.inputmethod.compat.SettingsSecureCompatUtils; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.utils.InputTypeUtils; - -public final class AccessibilityUtils { - private static final String TAG = AccessibilityUtils.class.getSimpleName(); - private static final String CLASS = AccessibilityUtils.class.getName(); - private static final String PACKAGE = - AccessibilityUtils.class.getPackage().getName(); - - private static final AccessibilityUtils sInstance = new AccessibilityUtils(); - - private Context mContext; - private AccessibilityManager mAccessibilityManager; - private AudioManager mAudioManager; - - /** The most recent auto-correction. */ - private String mAutoCorrectionWord; - - /** The most recent typed word for auto-correction. */ - private String mTypedWord; - - /* - * Setting this constant to {@code false} will disable all keyboard - * accessibility code, regardless of whether Accessibility is turned on in - * the system settings. It should ONLY be used in the event of an emergency. - */ - private static final boolean ENABLE_ACCESSIBILITY = true; - - public static void init(final Context context) { - if (!ENABLE_ACCESSIBILITY) return; - - // These only need to be initialized if the kill switch is off. - sInstance.initInternal(context); - } - - public static AccessibilityUtils getInstance() { - return sInstance; - } - - private AccessibilityUtils() { - // This class is not publicly instantiable. - } - - private void initInternal(final Context context) { - mContext = context; - mAccessibilityManager = - (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - } - - /** - * Returns {@code true} if accessibility is enabled. Currently, this means - * that the kill switch is off and system accessibility is turned on. - * - * @return {@code true} if accessibility is enabled. - */ - public boolean isAccessibilityEnabled() { - return ENABLE_ACCESSIBILITY && mAccessibilityManager.isEnabled(); - } - - /** - * Returns {@code true} if touch exploration is enabled. Currently, this - * means that the kill switch is off, the device supports touch exploration, - * and system accessibility is turned on. - * - * @return {@code true} if touch exploration is enabled. - */ - public boolean isTouchExplorationEnabled() { - return isAccessibilityEnabled() && mAccessibilityManager.isTouchExplorationEnabled(); - } - - /** - * Returns {@true} if the provided event is a touch exploration (e.g. hover) - * event. This is used to determine whether the event should be processed by - * the touch exploration code within the keyboard. - * - * @param event The event to check. - * @return {@true} is the event is a touch exploration event - */ - public static boolean isTouchExplorationEvent(final MotionEvent event) { - final int action = event.getAction(); - return action == MotionEvent.ACTION_HOVER_ENTER - || action == MotionEvent.ACTION_HOVER_EXIT - || action == MotionEvent.ACTION_HOVER_MOVE; - } - - /** - * Returns whether the device should obscure typed password characters. - * Typically this means speaking "dot" in place of non-control characters. - * - * @return {@code true} if the device should obscure password characters. - */ - @SuppressWarnings("deprecation") - public boolean shouldObscureInput(final EditorInfo editorInfo) { - if (editorInfo == null) return false; - - // The user can optionally force speaking passwords. - if (SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD != null) { - final boolean speakPassword = Settings.Secure.getInt(mContext.getContentResolver(), - SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0; - if (speakPassword) return false; - } - - // Always speak if the user is listening through headphones. - if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) { - return false; - } - - // Don't speak if the IME is connected to a password field. - return InputTypeUtils.isPasswordInputType(editorInfo.inputType); - } - - /** - * Sets the current auto-correction word and typed word. These may be used - * to provide the user with a spoken description of what auto-correction - * will occur when a key is typed. - * - * @param suggestedWords the list of suggested auto-correction words - */ - public void setAutoCorrection(final SuggestedWords suggestedWords) { - if (suggestedWords.mWillAutoCorrect) { - mAutoCorrectionWord = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); - final SuggestedWords.SuggestedWordInfo typedWordInfo = suggestedWords.mTypedWordInfo; - if (null == typedWordInfo) { - mTypedWord = null; - } else { - mTypedWord = typedWordInfo.mWord; - } - } else { - mAutoCorrectionWord = null; - mTypedWord = null; - } - } - - /** - * Obtains a description for an auto-correction key, taking into account the - * currently typed word and auto-correction. - * - * @param keyCodeDescription spoken description of the key that will insert - * an auto-correction - * @param shouldObscure whether the key should be obscured - * @return a description including a description of the auto-correction, if - * needed - */ - public String getAutoCorrectionDescription( - final String keyCodeDescription, final boolean shouldObscure) { - if (!TextUtils.isEmpty(mAutoCorrectionWord)) { - if (!TextUtils.equals(mAutoCorrectionWord, mTypedWord)) { - if (shouldObscure) { - // This should never happen, but just in case... - return mContext.getString(R.string.spoken_auto_correct_obscured, - keyCodeDescription); - } - return mContext.getString(R.string.spoken_auto_correct, keyCodeDescription, - mTypedWord, mAutoCorrectionWord); - } - } - - return keyCodeDescription; - } - - /** - * Sends the specified text to the {@link AccessibilityManager} to be - * spoken. - * - * @param view The source view. - * @param text The text to speak. - */ - public void announceForAccessibility(final View view, final CharSequence text) { - if (!mAccessibilityManager.isEnabled()) { - Log.e(TAG, "Attempted to speak when accessibility was disabled!"); - return; - } - - // The following is a hack to avoid using the heavy-weight TextToSpeech - // class. Instead, we're just forcing a fake AccessibilityEvent into - // the screen reader to make it speak. - final AccessibilityEvent event = AccessibilityEvent.obtain(); - - event.setPackageName(PACKAGE); - event.setClassName(CLASS); - event.setEventTime(SystemClock.uptimeMillis()); - event.setEnabled(true); - event.getText().add(text); - - // Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use - // announce events. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT); - } else { - event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED); - } - - final ViewParent viewParent = view.getParent(); - if ((viewParent == null) || !(viewParent instanceof ViewGroup)) { - Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility"); - return; - } - - viewParent.requestSendAccessibilityEvent(view, event); - } - - /** - * Handles speaking the "connect a headset to hear passwords" notification - * when connecting to a password field. - * - * @param view The source view. - * @param editorInfo The input connection's editor info attribute. - * @param restarting Whether the connection is being restarted. - */ - public void onStartInputViewInternal(final View view, final EditorInfo editorInfo, - final boolean restarting) { - if (shouldObscureInput(editorInfo)) { - final CharSequence text = mContext.getText(R.string.spoken_use_headphones); - announceForAccessibility(view, text); - } - } - - /** - * Sends the specified {@link AccessibilityEvent} if accessibility is - * enabled. No operation if accessibility is disabled. - * - * @param event The event to send. - */ - public void requestSendAccessibilityEvent(final AccessibilityEvent event) { - if (mAccessibilityManager.isEnabled()) { - mAccessibilityManager.sendAccessibilityEvent(event); - } - } -} diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java deleted file mode 100644 index bbda9f8e2..000000000 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * 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.inputmethod.accessibility; - -import android.content.Context; -import android.content.res.Resources; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseIntArray; -import android.view.inputmethod.EditorInfo; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.common.StringUtils; - -import java.util.Locale; - -final class KeyCodeDescriptionMapper { - private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName(); - private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X"; - private static final String SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT = "spoken_symbol_%04X"; - private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X"; - private static final String SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX = "spoken_emoticon"; - private static final String SPOKEN_EMOTICON_CODE_POINT_FORMAT = "_%02X"; - - // The resource ID of the string spoken for obscured keys - private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot; - - private static final KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); - - public static KeyCodeDescriptionMapper getInstance() { - return sInstance; - } - - // Sparse array of spoken description resource IDs indexed by key codes - private final SparseIntArray mKeyCodeMap = new SparseIntArray(); - - private KeyCodeDescriptionMapper() { - // Special non-character codes defined in Keyboard - mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space); - mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete); - mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return); - mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings); - mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift); - mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic); - mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); - mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab); - mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH, - R.string.spoken_description_language_switch); - mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next); - mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS, - R.string.spoken_description_action_previous); - mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji); - // Because the upper-case and lower-case mappings of the following letters is depending on - // the locale, the upper case descriptions should be defined here. The lower case - // descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}. - // U+0049: "I" LATIN CAPITAL LETTER I - // U+0069: "i" LATIN SMALL LETTER I - // U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE - // U+0131: "ı" LATIN SMALL LETTER DOTLESS I - mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049); - mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130); - } - - /** - * Returns the localized description of the action performed by a specified - * key based on the current keyboard state. - * - * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @param key The key from which to obtain a description. - * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. - * @return a character sequence describing the action performed by pressing the key - */ - public String getDescriptionForKey(final Context context, final Keyboard keyboard, - final Key key, final boolean shouldObscure) { - final int code = key.getCode(); - - if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { - final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard); - if (description != null) { - return description; - } - } - - if (code == Constants.CODE_SHIFT) { - return getDescriptionForShiftKey(context, keyboard); - } - - if (code == Constants.CODE_ENTER) { - // The following function returns the correct description in all action and - // regular enter cases, taking care of all modes. - return getDescriptionForActionKey(context, keyboard, key); - } - - if (code == Constants.CODE_OUTPUT_TEXT) { - final String outputText = key.getOutputText(); - final String description = getSpokenEmoticonDescription(context, outputText); - return TextUtils.isEmpty(description) ? outputText : description; - } - - // Just attempt to speak the description. - if (code != Constants.CODE_UNSPECIFIED) { - // If the key description should be obscured, now is the time to do it. - final boolean isDefinedNonCtrl = Character.isDefined(code) - && !Character.isISOControl(code); - if (shouldObscure && isDefinedNonCtrl) { - return context.getString(OBSCURED_KEY_RES_ID); - } - final String description = getDescriptionForCodePoint(context, code); - if (description != null) { - return description; - } - if (!TextUtils.isEmpty(key.getLabel())) { - return key.getLabel(); - } - return context.getString(R.string.spoken_description_unknown); - } - return null; - } - - /** - * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL - * key or {@code null} if there is not a description provided for the - * current keyboard context. - * - * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @return a character sequence describing the action performed by pressing the key - */ - private static String getDescriptionForSwitchAlphaSymbol(final Context context, - final Keyboard keyboard) { - final KeyboardId keyboardId = keyboard.mId; - final int elementId = keyboardId.mElementId; - final int resId; - - switch (elementId) { - case KeyboardId.ELEMENT_ALPHABET: - case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: - resId = R.string.spoken_description_to_symbol; - break; - case KeyboardId.ELEMENT_SYMBOLS: - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: - resId = R.string.spoken_description_to_alpha; - break; - case KeyboardId.ELEMENT_PHONE: - resId = R.string.spoken_description_to_symbol; - break; - case KeyboardId.ELEMENT_PHONE_SYMBOLS: - resId = R.string.spoken_description_to_numeric; - break; - default: - Log.e(TAG, "Missing description for keyboard element ID:" + elementId); - return null; - } - return context.getString(resId); - } - - /** - * Returns a context-sensitive description of the "Shift" key. - * - * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @return A context-sensitive description of the "Shift" key. - */ - private static String getDescriptionForShiftKey(final Context context, - final Keyboard keyboard) { - final KeyboardId keyboardId = keyboard.mId; - final int elementId = keyboardId.mElementId; - final int resId; - - switch (elementId) { - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: - resId = R.string.spoken_description_caps_lock; - break; - case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - resId = R.string.spoken_description_shift_shifted; - break; - case KeyboardId.ELEMENT_SYMBOLS: - resId = R.string.spoken_description_symbols_shift; - break; - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: - resId = R.string.spoken_description_symbols_shift_shifted; - break; - default: - resId = R.string.spoken_description_shift; - } - return context.getString(resId); - } - - /** - * Returns a context-sensitive description of the "Enter" action key. - * - * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @param key The key to describe. - * @return Returns a context-sensitive description of the "Enter" action key. - */ - private static String getDescriptionForActionKey(final Context context, final Keyboard keyboard, - final Key key) { - final KeyboardId keyboardId = keyboard.mId; - final int actionId = keyboardId.imeAction(); - final int resId; - - // Always use the label, if available. - if (!TextUtils.isEmpty(key.getLabel())) { - return key.getLabel().trim(); - } - - // Otherwise, use the action ID. - switch (actionId) { - case EditorInfo.IME_ACTION_SEARCH: - resId = R.string.spoken_description_search; - break; - case EditorInfo.IME_ACTION_GO: - resId = R.string.label_go_key; - break; - case EditorInfo.IME_ACTION_SEND: - resId = R.string.label_send_key; - break; - case EditorInfo.IME_ACTION_NEXT: - resId = R.string.label_next_key; - break; - case EditorInfo.IME_ACTION_DONE: - resId = R.string.label_done_key; - break; - case EditorInfo.IME_ACTION_PREVIOUS: - resId = R.string.label_previous_key; - break; - default: - resId = R.string.spoken_description_return; - } - return context.getString(resId); - } - - /** - * Returns a localized character sequence describing what will happen when - * the specified key is pressed based on its key code point. - * - * @param context The package's context. - * @param codePoint The code point from which to obtain a description. - * @return a character sequence describing the code point. - */ - public String getDescriptionForCodePoint(final Context context, final int codePoint) { - // If the key description should be obscured, now is the time to do it. - final int index = mKeyCodeMap.indexOfKey(codePoint); - if (index >= 0) { - return context.getString(mKeyCodeMap.valueAt(index)); - } - final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint); - if (accentedLetter != null) { - return accentedLetter; - } - // Here, <code>code</code> may be a base (non-accented) letter. - final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint); - if (unsupportedSymbol != null) { - return unsupportedSymbol; - } - final String emojiDescription = getSpokenEmojiDescription(context, codePoint); - if (emojiDescription != null) { - return emojiDescription; - } - if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) { - return StringUtils.newSingleCodePointString(codePoint); - } - return null; - } - - // TODO: Remove this method once TTS supports those accented letters' verbalization. - private String getSpokenAccentedLetterDescription(final Context context, final int code) { - final boolean isUpperCase = Character.isUpperCase(code); - final int baseCode = isUpperCase ? Character.toLowerCase(code) : code; - final int baseIndex = mKeyCodeMap.indexOfKey(baseCode); - final int resId = (baseIndex >= 0) ? mKeyCodeMap.valueAt(baseIndex) - : getSpokenDescriptionId(context, baseCode, SPOKEN_LETTER_RESOURCE_NAME_FORMAT); - if (resId == 0) { - return null; - } - final String spokenText = context.getString(resId); - return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText) - : spokenText; - } - - // TODO: Remove this method once TTS supports those symbols' verbalization. - private String getSpokenSymbolDescription(final Context context, final int code) { - final int resId = getSpokenDescriptionId(context, code, SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT); - if (resId == 0) { - return null; - } - final String spokenText = context.getString(resId); - if (!TextUtils.isEmpty(spokenText)) { - return spokenText; - } - // If a translated description is empty, fall back to unknown symbol description. - return context.getString(R.string.spoken_symbol_unknown); - } - - // TODO: Remove this method once TTS supports emoji verbalization. - private String getSpokenEmojiDescription(final Context context, final int code) { - final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); - if (resId == 0) { - return null; - } - final String spokenText = context.getString(resId); - if (!TextUtils.isEmpty(spokenText)) { - return spokenText; - } - // If a translated description is empty, fall back to unknown emoji description. - return context.getString(R.string.spoken_emoji_unknown); - } - - private int getSpokenDescriptionId(final Context context, final int code, - final String resourceNameFormat) { - final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code); - final Resources resources = context.getResources(); - // Note that the resource package name may differ from the context package name. - final String resourcePackageName = resources.getResourcePackageName( - R.string.spoken_description_unknown); - final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName); - if (resId != 0) { - mKeyCodeMap.append(code, resId); - } - return resId; - } - - // TODO: Remove this method once TTS supports emoticon verbalization. - private static String getSpokenEmoticonDescription(final Context context, - final String outputText) { - final StringBuilder sb = new StringBuilder(SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX); - final int textLength = outputText.length(); - for (int index = 0; index < textLength; index = outputText.offsetByCodePoints(index, 1)) { - final int codePoint = outputText.codePointAt(index); - sb.append(String.format(Locale.ROOT, SPOKEN_EMOTICON_CODE_POINT_FORMAT, codePoint)); - } - final String resourceName = sb.toString(); - final Resources resources = context.getResources(); - // Note that the resource package name may differ from the context package name. - final String resourcePackageName = resources.getResourcePackageName( - R.string.spoken_description_unknown); - final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName); - return (resId == 0) ? null : resources.getString(resId); - } -} diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java deleted file mode 100644 index 5c03d26a9..000000000 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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.inputmethod.accessibility; - -import android.content.Context; -import android.os.SystemClock; -import androidx.core.view.AccessibilityDelegateCompat; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardView; - -/** - * This class represents a delegate that can be registered in a class that extends - * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance. - * - * To implement accessibility mode, the target keyboard view has to:<p> - * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view. - * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}. - * - * @param <KV> The keyboard view class type. - */ -public class KeyboardAccessibilityDelegate<KV extends KeyboardView> - extends AccessibilityDelegateCompat { - private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName(); - protected static final boolean DEBUG_HOVER = false; - - protected final KV mKeyboardView; - protected final KeyDetector mKeyDetector; - private Keyboard mKeyboard; - private KeyboardAccessibilityNodeProvider<KV> mAccessibilityNodeProvider; - private Key mLastHoverKey; - - public static final int HOVER_EVENT_POINTER_ID = 0; - - public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) { - super(); - mKeyboardView = keyboardView; - mKeyDetector = keyDetector; - - // Ensure that the view has an accessibility delegate. - ViewCompat.setAccessibilityDelegate(keyboardView, this); - } - - /** - * Called when the keyboard layout changes. - * <p> - * <b>Note:</b> This method will be called even if accessibility is not - * enabled. - * @param keyboard The keyboard that is being set to the wrapping view. - */ - public void setKeyboard(final Keyboard keyboard) { - if (keyboard == null) { - return; - } - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setKeyboard(keyboard); - } - mKeyboard = keyboard; - } - - protected final Keyboard getKeyboard() { - return mKeyboard; - } - - protected final void setLastHoverKey(final Key key) { - mLastHoverKey = key; - } - - protected final Key getLastHoverKey() { - return mLastHoverKey; - } - - /** - * Sends a window state change event with the specified string resource id. - * - * @param resId The string resource id of the text to send with the event. - */ - protected void sendWindowStateChanged(final int resId) { - if (resId == 0) { - return; - } - final Context context = mKeyboardView.getContext(); - sendWindowStateChanged(context.getString(resId)); - } - - /** - * Sends a window state change event with the specified text. - * - * @param text The text to send with the event. - */ - protected void sendWindowStateChanged(final String text) { - final AccessibilityEvent stateChange = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - mKeyboardView.onInitializeAccessibilityEvent(stateChange); - stateChange.getText().add(text); - stateChange.setContentDescription(null); - - final ViewParent parent = mKeyboardView.getParent(); - if (parent != null) { - parent.requestSendAccessibilityEvent(mKeyboardView, stateChange); - } - } - - /** - * Delegate method for View.getAccessibilityNodeProvider(). This method is called in SDK - * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual - * node hierarchy provider. - * - * @param host The host view for the provider. - * @return The accessibility node provider for the current keyboard. - */ - @Override - public KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider(final View host) { - return getAccessibilityNodeProvider(); - } - - /** - * @return A lazily-instantiated node provider for this view delegate. - */ - protected KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider() { - // Instantiate the provide only when requested. Since the system - // will call this method multiple times it is a good practice to - // cache the provider instance. - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = - new KeyboardAccessibilityNodeProvider<>(mKeyboardView, this); - } - return mAccessibilityNodeProvider; - } - - /** - * Get a key that a hover event is on. - * - * @param event The hover event. - * @return key The key that the <code>event</code> is on. - */ - protected final Key getHoverKeyOf(final MotionEvent event) { - final int actionIndex = event.getActionIndex(); - final int x = (int)event.getX(actionIndex); - final int y = (int)event.getY(actionIndex); - return mKeyDetector.detectHitKey(x, y); - } - - /** - * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. - * - * @param event The hover event. - * @return {@code true} if the event is handled. - */ - public boolean onHoverEvent(final MotionEvent event) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_HOVER_ENTER: - onHoverEnter(event); - break; - case MotionEvent.ACTION_HOVER_MOVE: - onHoverMove(event); - break; - case MotionEvent.ACTION_HOVER_EXIT: - onHoverExit(event); - break; - default: - Log.w(getClass().getSimpleName(), "Unknown hover event: " + event); - break; - } - return true; - } - - /** - * Process {@link MotionEvent#ACTION_HOVER_ENTER} event. - * - * @param event A hover enter event. - */ - protected void onHoverEnter(final MotionEvent event) { - final Key key = getHoverKeyOf(event); - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverEnter: key=" + key); - } - if (key != null) { - onHoverEnterTo(key); - } - setLastHoverKey(key); - } - - /** - * Process {@link MotionEvent#ACTION_HOVER_MOVE} event. - * - * @param event A hover move event. - */ - protected void onHoverMove(final MotionEvent event) { - final Key lastKey = getLastHoverKey(); - final Key key = getHoverKeyOf(event); - if (key != lastKey) { - if (lastKey != null) { - onHoverExitFrom(lastKey); - } - if (key != null) { - onHoverEnterTo(key); - } - } - if (key != null) { - onHoverMoveWithin(key); - } - setLastHoverKey(key); - } - - /** - * Process {@link MotionEvent#ACTION_HOVER_EXIT} event. - * - * @param event A hover exit event. - */ - protected void onHoverExit(final MotionEvent event) { - final Key lastKey = getLastHoverKey(); - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); - } - if (lastKey != null) { - onHoverExitFrom(lastKey); - } - final Key key = getHoverKeyOf(event); - // Make sure we're not getting an EXIT event because the user slid - // off the keyboard area, then force a key press. - if (key != null) { - onHoverExitFrom(key); - } - setLastHoverKey(null); - } - - /** - * Perform click on a key. - * - * @param key A key to be registered. - */ - public void performClickOn(final Key key) { - if (DEBUG_HOVER) { - Log.d(TAG, "performClickOn: key=" + key); - } - simulateTouchEvent(MotionEvent.ACTION_DOWN, key); - simulateTouchEvent(MotionEvent.ACTION_UP, key); - } - - /** - * Simulating a touch event by injecting a synthesized touch event into {@link KeyboardView}. - * - * @param touchAction The action of the synthesizing touch event. - * @param key The key that a synthesized touch event is on. - */ - private void simulateTouchEvent(final int touchAction, final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final long eventTime = SystemClock.uptimeMillis(); - final MotionEvent touchEvent = MotionEvent.obtain( - eventTime, eventTime, touchAction, x, y, 0 /* metaState */); - mKeyboardView.onTouchEvent(touchEvent); - touchEvent.recycle(); - } - - /** - * Handles a hover enter event on a key. - * - * @param key The currently hovered key. - */ - protected void onHoverEnterTo(final Key key) { - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverEnterTo: key=" + key); - } - key.onPressed(); - mKeyboardView.invalidateKey(key); - final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider(); - provider.onHoverEnterTo(key); - provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); - } - - /** - * Handles a hover move event on a key. - * - * @param key The currently hovered key. - */ - protected void onHoverMoveWithin(final Key key) { } - - /** - * Handles a hover exit event on a key. - * - * @param key The currently hovered key. - */ - protected void onHoverExitFrom(final Key key) { - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverExitFrom: key=" + key); - } - key.onReleased(); - mKeyboardView.invalidateKey(key); - final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider(); - provider.onHoverExitFrom(key); - } - - /** - * Perform long click on a key. - * - * @param key A key to be long pressed on. - */ - public void performLongClickOn(final Key key) { - // A extended class should override this method to implement long press. - } -} diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java deleted file mode 100644 index cc244c305..000000000 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2012 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.inputmethod.accessibility; - -import android.graphics.Rect; -import android.os.Bundle; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityEventCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; -import androidx.core.view.accessibility.AccessibilityRecordCompat; -import android.util.Log; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.inputmethod.EditorInfo; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.latin.common.CoordinateUtils; -import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.settings.SettingsValues; - -import java.util.List; - -/** - * Exposes a virtual view sub-tree for {@link KeyboardView} and generates - * {@link AccessibilityEvent}s for individual {@link Key}s. - * <p> - * A virtual sub-tree is composed of imaginary {@link View}s that are reported - * as a part of the view hierarchy for accessibility purposes. This enables - * custom views that draw complex content to report them selves as a tree of - * virtual views, thus conveying their logical structure. - * </p> - */ -final class KeyboardAccessibilityNodeProvider<KV extends KeyboardView> - extends AccessibilityNodeProviderCompat { - private static final String TAG = KeyboardAccessibilityNodeProvider.class.getSimpleName(); - - // From {@link android.view.accessibility.AccessibilityNodeInfo#UNDEFINED_ITEM_ID}. - private static final int UNDEFINED = Integer.MAX_VALUE; - - private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; - private final AccessibilityUtils mAccessibilityUtils; - - /** Temporary rect used to calculate in-screen bounds. */ - private final Rect mTempBoundsInScreen = new Rect(); - - /** The parent view's cached on-screen location. */ - private final int[] mParentLocation = CoordinateUtils.newInstance(); - - /** The virtual view identifier for the focused node. */ - private int mAccessibilityFocusedView = UNDEFINED; - - /** The virtual view identifier for the hovering node. */ - private int mHoveringNodeId = UNDEFINED; - - /** The keyboard view to provide an accessibility node info. */ - private final KV mKeyboardView; - /** The accessibility delegate. */ - private final KeyboardAccessibilityDelegate<KV> mDelegate; - - /** The current keyboard. */ - private Keyboard mKeyboard; - - public KeyboardAccessibilityNodeProvider(final KV keyboardView, - final KeyboardAccessibilityDelegate<KV> delegate) { - super(); - mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance(); - mAccessibilityUtils = AccessibilityUtils.getInstance(); - mKeyboardView = keyboardView; - mDelegate = delegate; - - // Since this class is constructed lazily, we might not get a subsequent - // call to setKeyboard() and therefore need to call it now. - setKeyboard(keyboardView.getKeyboard()); - } - - /** - * Sets the keyboard represented by this node provider. - * - * @param keyboard The keyboard that is being set to the keyboard view. - */ - public void setKeyboard(final Keyboard keyboard) { - mKeyboard = keyboard; - } - - private Key getKeyOf(final int virtualViewId) { - if (mKeyboard == null) { - return null; - } - final List<Key> sortedKeys = mKeyboard.getSortedKeys(); - // Use a virtual view id as an index of the sorted keys list. - if (virtualViewId >= 0 && virtualViewId < sortedKeys.size()) { - return sortedKeys.get(virtualViewId); - } - return null; - } - - private int getVirtualViewIdOf(final Key key) { - if (mKeyboard == null) { - return View.NO_ID; - } - final List<Key> sortedKeys = mKeyboard.getSortedKeys(); - final int size = sortedKeys.size(); - for (int index = 0; index < size; index++) { - if (sortedKeys.get(index) == key) { - // Use an index of the sorted keys list as a virtual view id. - return index; - } - } - return View.NO_ID; - } - - /** - * Creates and populates an {@link AccessibilityEvent} for the specified key - * and event type. - * - * @param key A key on the host keyboard view. - * @param eventType The event type to create. - * @return A populated {@link AccessibilityEvent} for the key. - * @see AccessibilityEvent - */ - public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) { - final int virtualViewId = getVirtualViewIdOf(key); - final String keyDescription = getKeyDescription(key); - final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - event.setPackageName(mKeyboardView.getContext().getPackageName()); - event.setClassName(key.getClass().getName()); - event.setContentDescription(keyDescription); - event.setEnabled(true); - final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); - record.setSource(mKeyboardView, virtualViewId); - return event; - } - - public void onHoverEnterTo(final Key key) { - final int id = getVirtualViewIdOf(key); - if (id == View.NO_ID) { - return; - } - // Start hovering on the key. Because our accessibility model is lift-to-type, we should - // report the node info without click and long click actions to avoid unnecessary - // announcements. - mHoveringNodeId = id; - // Invalidate the node info of the key. - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); - } - - public void onHoverExitFrom(final Key key) { - mHoveringNodeId = UNDEFINED; - // Invalidate the node info of the key to be able to revert the change we have done - // in {@link #onHoverEnterTo(Key)}. - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); - sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); - } - - /** - * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual - * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or - * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}. - * <p> - * A virtual descendant is an imaginary View that is reported as a part of - * the view hierarchy for accessibility purposes. This enables custom views - * that draw complex content to report them selves as a tree of virtual - * views, thus conveying their logical structure. - * </p> - * <p> - * The implementer is responsible for obtaining an accessibility node info - * from the pool of reusable instances and setting the desired properties of - * the node info before returning it. - * </p> - * - * @param virtualViewId A client defined virtual view id. - * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant or the host - * View. - * @see AccessibilityNodeInfoCompat - */ - @Override - public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(final int virtualViewId) { - if (virtualViewId == UNDEFINED) { - return null; - } - if (virtualViewId == View.NO_ID) { - // We are requested to create an AccessibilityNodeInfo describing - // this View, i.e. the root of the virtual sub-tree. - final AccessibilityNodeInfoCompat rootInfo = - AccessibilityNodeInfoCompat.obtain(mKeyboardView); - ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo); - updateParentLocation(); - - // Add the virtual children of the root View. - final List<Key> sortedKeys = mKeyboard.getSortedKeys(); - final int size = sortedKeys.size(); - for (int index = 0; index < size; index++) { - final Key key = sortedKeys.get(index); - if (key.isSpacer()) { - continue; - } - // Use an index of the sorted keys list as a virtual view id. - rootInfo.addChild(mKeyboardView, index); - } - return rootInfo; - } - - // Find the key that corresponds to the given virtual view id. - final Key key = getKeyOf(virtualViewId); - if (key == null) { - Log.e(TAG, "Invalid virtual view ID: " + virtualViewId); - return null; - } - final String keyDescription = getKeyDescription(key); - final Rect boundsInParent = key.getHitBox(); - - // Calculate the key's in-screen bounds. - mTempBoundsInScreen.set(boundsInParent); - mTempBoundsInScreen.offset( - CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation)); - final Rect boundsInScreen = mTempBoundsInScreen; - - // Obtain and initialize an AccessibilityNodeInfo with information about the virtual view. - final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); - info.setPackageName(mKeyboardView.getContext().getPackageName()); - // info.setTextEntryKey(true); - info.setClassName(key.getClass().getName()); - info.setContentDescription(keyDescription); - info.setBoundsInParent(boundsInParent); - info.setBoundsInScreen(boundsInScreen); - info.setParent(mKeyboardView); - info.setSource(mKeyboardView, virtualViewId); - info.setEnabled(key.isEnabled()); - info.setVisibleToUser(true); - info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); - if (key.isLongPressEnabled()) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK); - } - - if (mAccessibilityFocusedView == virtualViewId) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); - } else { - info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); - } - return info; - } - - @Override - public boolean performAction(final int virtualViewId, final int action, - final Bundle arguments) { - final Key key = getKeyOf(virtualViewId); - if (key == null) { - return false; - } - return performActionForKey(key, action); - } - - /** - * Performs the specified accessibility action for the given key. - * - * @param key The on which to perform the action. - * @param action The action to perform. - * @return The result of performing the action, or false if the action is not supported. - */ - boolean performActionForKey(final Key key, final int action) { - switch (action) { - case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: - mAccessibilityFocusedView = getVirtualViewIdOf(key); - sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); - return true; - case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: - mAccessibilityFocusedView = UNDEFINED; - sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); - return true; - case AccessibilityNodeInfoCompat.ACTION_CLICK: - sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_CLICKED); - mDelegate.performClickOn(key); - return true; - case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK: - sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); - mDelegate.performLongClickOn(key); - return true; - default: - return false; - } - } - - /** - * Sends an accessibility event for the given {@link Key}. - * - * @param key The key that's sending the event. - * @param eventType The type of event to send. - */ - void sendAccessibilityEventForKey(final Key key, final int eventType) { - final AccessibilityEvent event = createAccessibilityEvent(key, eventType); - mAccessibilityUtils.requestSendAccessibilityEvent(event); - } - - /** - * Returns the context-specific description for a {@link Key}. - * - * @param key The key to describe. - * @return The context-specific description of the key. - */ - private String getKeyDescription(final Key key) { - final EditorInfo editorInfo = mKeyboard.mId.mEditorInfo; - final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo); - final SettingsValues currentSettings = Settings.getInstance().getCurrent(); - final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey( - mKeyboardView.getContext(), mKeyboard, key, shouldObscure); - if (currentSettings.isWordSeparator(key.getCode())) { - return mAccessibilityUtils.getAutoCorrectionDescription( - keyCodeDescription, shouldObscure); - } - return keyCodeDescription; - } - - /** - * Updates the parent's on-screen location. - */ - private void updateParentLocation() { - mKeyboardView.getLocationOnScreen(mParentLocation); - } -} diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java deleted file mode 100644 index 3234993cf..000000000 --- a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2014 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.inputmethod.accessibility; - -import android.content.Context; -import android.graphics.Rect; -import android.os.SystemClock; -import android.util.Log; -import android.util.SparseIntArray; -import android.view.MotionEvent; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; - -/** - * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance - * accessibility support via composition rather via inheritance. - */ -public final class MainKeyboardAccessibilityDelegate - extends KeyboardAccessibilityDelegate<MainKeyboardView> - implements AccessibilityLongPressTimer.LongPressTimerCallback { - private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName(); - - /** Map of keyboard modes to resource IDs. */ - private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray(); - - static { - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url); - } - - /** The most recently set keyboard mode. */ - private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN; - private static final int KEYBOARD_IS_HIDDEN = -1; - // The rectangle region to ignore hover events. - private final Rect mBoundsToIgnoreHoverEvent = new Rect(); - - - public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView, - final KeyDetector keyDetector) { - super(mainKeyboardView, keyDetector); - } - - /** - * {@inheritDoc} - */ - @Override - public void setKeyboard(final Keyboard keyboard) { - if (keyboard == null) { - return; - } - final Keyboard lastKeyboard = getKeyboard(); - super.setKeyboard(keyboard); - final int lastKeyboardMode = mLastKeyboardMode; - mLastKeyboardMode = keyboard.mId.mMode; - - // Since this method is called even when accessibility is off, make sure - // to check the state before announcing anything. - if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) { - return; - } - // Announce the language name only when the language is changed. - if (lastKeyboard == null || !keyboard.mId.mSubtype.equals(lastKeyboard.mId.mSubtype)) { - announceKeyboardLanguage(keyboard); - return; - } - // Announce the mode only when the mode is changed. - if (keyboard.mId.mMode != lastKeyboardMode) { - announceKeyboardMode(keyboard); - return; - } - // Announce the keyboard type only when the type is changed. - if (keyboard.mId.mElementId != lastKeyboard.mId.mElementId) { - announceKeyboardType(keyboard, lastKeyboard); - return; - } - } - - /** - * Called when the keyboard is hidden and accessibility is enabled. - */ - public void onHideWindow() { - if (mLastKeyboardMode != KEYBOARD_IS_HIDDEN) { - announceKeyboardHidden(); - } - mLastKeyboardMode = KEYBOARD_IS_HIDDEN; - } - - /** - * Announces which language of keyboard is being displayed. - * - * @param keyboard The new keyboard. - */ - private void announceKeyboardLanguage(final Keyboard keyboard) { - final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( - keyboard.mId.mSubtype.getRawSubtype()); - sendWindowStateChanged(languageText); - } - - /** - * Announces which type of keyboard is being displayed. - * If the keyboard type is unknown, no announcement is made. - * - * @param keyboard The new keyboard. - */ - private void announceKeyboardMode(final Keyboard keyboard) { - final Context context = mKeyboardView.getContext(); - final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(keyboard.mId.mMode); - if (modeTextResId == 0) { - return; - } - final String modeText = context.getString(modeTextResId); - final String text = context.getString(R.string.announce_keyboard_mode, modeText); - sendWindowStateChanged(text); - } - - /** - * Announces which type of keyboard is being displayed. - * - * @param keyboard The new keyboard. - * @param lastKeyboard The last keyboard. - */ - private void announceKeyboardType(final Keyboard keyboard, final Keyboard lastKeyboard) { - final int lastElementId = lastKeyboard.mId.mElementId; - final int resId; - switch (keyboard.mId.mElementId) { - case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET: - if (lastElementId == KeyboardId.ELEMENT_ALPHABET - || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { - // Transition between alphabet mode and automatic shifted mode should be silently - // ignored because it can be determined by each key's talk back announce. - return; - } - resId = R.string.spoken_description_mode_alpha; - break; - case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { - // Resetting automatic shifted mode by pressing the shift key causes the transition - // from automatic shifted to manual shifted that should be silently ignored. - return; - } - resId = R.string.spoken_description_shiftmode_on; - break; - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: - if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) { - // Resetting caps locked mode by pressing the shift key causes the transition - // from shift locked to shift lock shifted that should be silently ignored. - return; - } - resId = R.string.spoken_description_shiftmode_locked; - break; - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: - resId = R.string.spoken_description_shiftmode_locked; - break; - case KeyboardId.ELEMENT_SYMBOLS: - resId = R.string.spoken_description_mode_symbol; - break; - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: - resId = R.string.spoken_description_mode_symbol_shift; - break; - case KeyboardId.ELEMENT_PHONE: - resId = R.string.spoken_description_mode_phone; - break; - case KeyboardId.ELEMENT_PHONE_SYMBOLS: - resId = R.string.spoken_description_mode_phone_shift; - break; - default: - return; - } - sendWindowStateChanged(resId); - } - - /** - * Announces that the keyboard has been hidden. - */ - private void announceKeyboardHidden() { - sendWindowStateChanged(R.string.announce_keyboard_hidden); - } - - @Override - public void performClickOn(final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - if (DEBUG_HOVER) { - Log.d(TAG, "performClickOn: key=" + key - + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); - } - if (mBoundsToIgnoreHoverEvent.contains(x, y)) { - // This hover exit event points to the key that should be ignored. - // Clear the ignoring region to handle further hover events. - mBoundsToIgnoreHoverEvent.setEmpty(); - return; - } - super.performClickOn(key); - } - - @Override - protected void onHoverEnterTo(final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverEnterTo: key=" + key - + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); - } - if (mBoundsToIgnoreHoverEvent.contains(x, y)) { - return; - } - // This hover enter event points to the key that isn't in the ignoring region. - // Further hover events should be handled. - mBoundsToIgnoreHoverEvent.setEmpty(); - super.onHoverEnterTo(key); - } - - @Override - protected void onHoverExitFrom(final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverExitFrom: key=" + key - + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); - } - super.onHoverExitFrom(key); - } - - @Override - public void performLongClickOn(final Key key) { - if (DEBUG_HOVER) { - Log.d(TAG, "performLongClickOn: key=" + key); - } - final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID); - final long eventTime = SystemClock.uptimeMillis(); - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final MotionEvent downEvent = MotionEvent.obtain( - eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */); - // Inject a fake down event to {@link PointerTracker} to handle a long press correctly. - tracker.processMotionEvent(downEvent, mKeyDetector); - downEvent.recycle(); - // Invoke {@link PointerTracker#onLongPressed()} as if a long press timeout has passed. - tracker.onLongPressed(); - // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout) - // or a key invokes IME switcher dialog, we should just ignore the next - // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether - // {@link PointerTracker} is in operation or not. - if (tracker.isInOperation()) { - // This long press shows a more keys keyboard and further hover events should be - // handled. - mBoundsToIgnoreHoverEvent.setEmpty(); - return; - } - // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}. - // We should ignore further hover events on this key. - mBoundsToIgnoreHoverEvent.set(key.getHitBox()); - if (key.hasNoPanelAutoMoreKey()) { - // This long press has registered a code point without showing a more keys keyboard. - // We should talk back the code point if possible. - final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys()[0].mCode; - final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint( - mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey); - if (text != null) { - sendWindowStateChanged(text); - } - } - } -} diff --git a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java deleted file mode 100644 index 4022da343..000000000 --- a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2014 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.inputmethod.accessibility; - -import android.graphics.Rect; -import android.util.Log; -import android.view.MotionEvent; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; -import com.android.inputmethod.keyboard.MoreKeysKeyboardView; -import com.android.inputmethod.keyboard.PointerTracker; - -/** - * This class represents a delegate that can be registered in {@link MoreKeysKeyboardView} to - * enhance accessibility support via composition rather via inheritance. - */ -public class MoreKeysKeyboardAccessibilityDelegate - extends KeyboardAccessibilityDelegate<MoreKeysKeyboardView> { - private static final String TAG = MoreKeysKeyboardAccessibilityDelegate.class.getSimpleName(); - - private final Rect mMoreKeysKeyboardValidBounds = new Rect(); - private static final int CLOSING_INSET_IN_PIXEL = 1; - private int mOpenAnnounceResId; - private int mCloseAnnounceResId; - - public MoreKeysKeyboardAccessibilityDelegate(final MoreKeysKeyboardView moreKeysKeyboardView, - final KeyDetector keyDetector) { - super(moreKeysKeyboardView, keyDetector); - } - - public void setOpenAnnounce(final int resId) { - mOpenAnnounceResId = resId; - } - - public void setCloseAnnounce(final int resId) { - mCloseAnnounceResId = resId; - } - - public void onShowMoreKeysKeyboard() { - sendWindowStateChanged(mOpenAnnounceResId); - } - - public void onDismissMoreKeysKeyboard() { - sendWindowStateChanged(mCloseAnnounceResId); - } - - @Override - protected void onHoverEnter(final MotionEvent event) { - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverEnter: key=" + getHoverKeyOf(event)); - } - super.onHoverEnter(event); - final int actionIndex = event.getActionIndex(); - final int x = (int)event.getX(actionIndex); - final int y = (int)event.getY(actionIndex); - final int pointerId = event.getPointerId(actionIndex); - final long eventTime = event.getEventTime(); - mKeyboardView.onDownEvent(x, y, pointerId, eventTime); - } - - @Override - protected void onHoverMove(final MotionEvent event) { - super.onHoverMove(event); - final int actionIndex = event.getActionIndex(); - final int x = (int)event.getX(actionIndex); - final int y = (int)event.getY(actionIndex); - final int pointerId = event.getPointerId(actionIndex); - final long eventTime = event.getEventTime(); - mKeyboardView.onMoveEvent(x, y, pointerId, eventTime); - } - - @Override - protected void onHoverExit(final MotionEvent event) { - final Key lastKey = getLastHoverKey(); - if (DEBUG_HOVER) { - Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); - } - if (lastKey != null) { - super.onHoverExitFrom(lastKey); - } - setLastHoverKey(null); - final int actionIndex = event.getActionIndex(); - final int x = (int)event.getX(actionIndex); - final int y = (int)event.getY(actionIndex); - final int pointerId = event.getPointerId(actionIndex); - final long eventTime = event.getEventTime(); - // A hover exit event at one pixel width or height area on the edges of more keys keyboard - // are treated as closing. - mMoreKeysKeyboardValidBounds.set(0, 0, mKeyboardView.getWidth(), mKeyboardView.getHeight()); - mMoreKeysKeyboardValidBounds.inset(CLOSING_INSET_IN_PIXEL, CLOSING_INSET_IN_PIXEL); - if (mMoreKeysKeyboardValidBounds.contains(x, y)) { - // Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover - // exit event selects a key. - mKeyboardView.onUpEvent(x, y, pointerId, eventTime); - // TODO: Should fix this reference. This is a hack to clear the state of - // {@link PointerTracker}. - PointerTracker.dismissAllMoreKeysPanels(); - return; - } - // Close the more keys keyboard. - // TODO: Should fix this reference. This is a hack to clear the state of - // {@link PointerTracker}. - PointerTracker.dismissAllMoreKeysPanels(); - } -} |