Merge "Added support for speaking keys through accessibility service."
commit
52e96f1e89
|
@ -28,4 +28,24 @@
|
||||||
<integer name="key_delete">-5</integer>
|
<integer name="key_delete">-5</integer>
|
||||||
<integer name="key_settings">-100</integer>
|
<integer name="key_settings">-100</integer>
|
||||||
<integer name="key_voice">-102</integer>
|
<integer name="key_voice">-102</integer>
|
||||||
|
|
||||||
|
<!-- Array used for mapping key codes to description strings. -->
|
||||||
|
<array name="key_descriptions">
|
||||||
|
<item>@integer/key_tab</item>
|
||||||
|
<item>@string/description_tab_key</item>
|
||||||
|
<item>@integer/key_return</item>
|
||||||
|
<item>@string/description_return_key</item>
|
||||||
|
<item>@integer/key_space</item>
|
||||||
|
<item>@string/description_space_key</item>
|
||||||
|
<item>@integer/key_shift</item>
|
||||||
|
<item>@string/description_shift_key</item>
|
||||||
|
<item>@integer/key_switch_alpha_symbol</item>
|
||||||
|
<item>@string/description_switch_alpha_symbol_key</item>
|
||||||
|
<item>@integer/key_delete</item>
|
||||||
|
<item>@string/description_delete_key</item>
|
||||||
|
<item>@integer/key_settings</item>
|
||||||
|
<item>@string/description_settings_key</item>
|
||||||
|
<item>@integer/key_voice</item>
|
||||||
|
<item>@string/description_voice_key</item>
|
||||||
|
</array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -102,6 +102,31 @@
|
||||||
<!-- Label for "Wait" key of phone number keyboard. Must be short to fit on key! [CHAR LIMIT=5]-->
|
<!-- Label for "Wait" key of phone number keyboard. Must be short to fit on key! [CHAR LIMIT=5]-->
|
||||||
<string name="label_wait_key">Wait</string>
|
<string name="label_wait_key">Wait</string>
|
||||||
|
|
||||||
|
<!-- Spoken text description for delete key. -->
|
||||||
|
<string name="description_delete_key">Delete</string>
|
||||||
|
<!-- Spoken text description for return key. -->
|
||||||
|
<string name="description_return_key">Return</string>
|
||||||
|
<!-- Spoken text description for settings key. -->
|
||||||
|
<string name="description_settings_key">Settings</string>
|
||||||
|
<!-- Spoken text description for shift key. -->
|
||||||
|
<string name="description_shift_key">Shift</string>
|
||||||
|
<!-- Spoken text description for space key. -->
|
||||||
|
<string name="description_space_key">Space</string>
|
||||||
|
<!-- Spoken text description for symbols key. -->
|
||||||
|
<string name="description_switch_alpha_symbol_key">Symbols</string>
|
||||||
|
<!-- Spoken text description for tab key. -->
|
||||||
|
<string name="description_tab_key">Tab</string>
|
||||||
|
<!-- Spoken text description for voice input key. -->
|
||||||
|
<string name="description_voice_key">Voice Input</string>
|
||||||
|
<!-- Spoken text description for symbols mode on. -->
|
||||||
|
<string name="description_symbols_on">Symbols on</string>
|
||||||
|
<!-- Spoken text description for symbols mode off. -->
|
||||||
|
<string name="description_symbols_off">Symbols off</string>
|
||||||
|
<!-- Spoken text description for shift mode on. -->
|
||||||
|
<string name="description_shift_on">Shift on</string>
|
||||||
|
<!-- Spoken text description for shift mode off. -->
|
||||||
|
<string name="description_shift_off">Shift off</string>
|
||||||
|
|
||||||
<!-- Voice related labels -->
|
<!-- Voice related labels -->
|
||||||
|
|
||||||
<!-- Title of the warning dialog that shows when a user initiates voice input for
|
<!-- Title of the warning dialog that shows when a user initiates voice input for
|
||||||
|
|
|
@ -344,7 +344,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
// state when shift key is pressed to go to normal mode.
|
// state when shift key is pressed to go to normal mode.
|
||||||
// On the other hand, on distinct multi touch panel device, turning off the shift locked
|
// On the other hand, on distinct multi touch panel device, turning off the shift locked
|
||||||
// state with shift key pressing is handled by onReleaseShift().
|
// state with shift key pressing is handled by onReleaseShift().
|
||||||
if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
|
if ((!hasDistinctMultitouch() || isAccessibilityEnabled())
|
||||||
|
&& !shifted && latinKeyboard.isShiftLocked()) {
|
||||||
latinKeyboard.setShiftLocked(false);
|
latinKeyboard.setShiftLocked(false);
|
||||||
}
|
}
|
||||||
if (latinKeyboard.setShifted(shifted)) {
|
if (latinKeyboard.setShifted(shifted)) {
|
||||||
|
@ -442,6 +443,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
public void onPressShift() {
|
public void onPressShift() {
|
||||||
if (!isKeyboardAvailable())
|
if (!isKeyboardAvailable())
|
||||||
return;
|
return;
|
||||||
|
// If accessibility is enabled, disable momentary shift lock.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return;
|
||||||
ShiftKeyState shiftKeyState = mShiftKeyState;
|
ShiftKeyState shiftKeyState = mShiftKeyState;
|
||||||
if (DEBUG_STATE)
|
if (DEBUG_STATE)
|
||||||
Log.d(TAG, "onPressShift:"
|
Log.d(TAG, "onPressShift:"
|
||||||
|
@ -477,6 +481,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
public void onReleaseShift() {
|
public void onReleaseShift() {
|
||||||
if (!isKeyboardAvailable())
|
if (!isKeyboardAvailable())
|
||||||
return;
|
return;
|
||||||
|
// If accessibility is enabled, disable momentary shift lock.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return;
|
||||||
ShiftKeyState shiftKeyState = mShiftKeyState;
|
ShiftKeyState shiftKeyState = mShiftKeyState;
|
||||||
if (DEBUG_STATE)
|
if (DEBUG_STATE)
|
||||||
Log.d(TAG, "onReleaseShift:"
|
Log.d(TAG, "onReleaseShift:"
|
||||||
|
@ -502,6 +509,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPressSymbol() {
|
public void onPressSymbol() {
|
||||||
|
// If accessibility is enabled, disable momentary symbol lock.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return;
|
||||||
if (DEBUG_STATE)
|
if (DEBUG_STATE)
|
||||||
Log.d(TAG, "onPressSymbol:"
|
Log.d(TAG, "onPressSymbol:"
|
||||||
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
||||||
|
@ -512,6 +522,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReleaseSymbol() {
|
public void onReleaseSymbol() {
|
||||||
|
// If accessibility is enabled, disable momentary symbol lock.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return;
|
||||||
if (DEBUG_STATE)
|
if (DEBUG_STATE)
|
||||||
Log.d(TAG, "onReleaseSymbol:"
|
Log.d(TAG, "onReleaseSymbol:"
|
||||||
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
||||||
|
@ -524,6 +537,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOtherKeyPressed() {
|
public void onOtherKeyPressed() {
|
||||||
|
// If accessibility is enabled, disable momentary mode locking.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return;
|
||||||
if (DEBUG_STATE)
|
if (DEBUG_STATE)
|
||||||
Log.d(TAG, "onOtherKeyPressed:"
|
Log.d(TAG, "onOtherKeyPressed:"
|
||||||
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
|
||||||
|
@ -582,6 +598,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAccessibilityEnabled() {
|
||||||
|
return mInputView != null && mInputView.isAccessibilityEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasDistinctMultitouch() {
|
public boolean hasDistinctMultitouch() {
|
||||||
return mInputView != null && mInputView.hasDistinctMultitouch();
|
return mInputView != null && mInputView.hasDistinctMultitouch();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
@ -146,6 +147,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
private final boolean mHasDistinctMultitouch;
|
private final boolean mHasDistinctMultitouch;
|
||||||
private int mOldPointerCount = 1;
|
private int mOldPointerCount = 1;
|
||||||
|
|
||||||
|
// Accessibility
|
||||||
|
private boolean mIsAccessibilityEnabled;
|
||||||
|
|
||||||
protected KeyDetector mKeyDetector = new ProximityKeyDetector();
|
protected KeyDetector mKeyDetector = new ProximityKeyDetector();
|
||||||
|
|
||||||
// Swipe gesture detector
|
// Swipe gesture detector
|
||||||
|
@ -523,7 +527,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the device has distinct multi-touch panel.
|
* Returns whether the device has distinct multi-touch panel.
|
||||||
* @return true if the device has distinct multi-touch panel.
|
* @return true if the device has distinct multi-touch panel.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -531,6 +535,28 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
return mHasDistinctMultitouch;
|
return mHasDistinctMultitouch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables accessibility.
|
||||||
|
* @param accessibilityEnabled whether or not to enable accessibility
|
||||||
|
*/
|
||||||
|
public void setAccessibilityEnabled(boolean accessibilityEnabled) {
|
||||||
|
mIsAccessibilityEnabled = accessibilityEnabled;
|
||||||
|
|
||||||
|
// Propagate this change to all existing pointer trackers.
|
||||||
|
for (PointerTracker tracker : mPointerTrackers) {
|
||||||
|
tracker.setAccessibilityEnabled(accessibilityEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the device has accessibility enabled.
|
||||||
|
* @return true if the device has accessibility enabled.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAccessibilityEnabled() {
|
||||||
|
return mIsAccessibilityEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables the key feedback popup. This is a popup that shows a magnified
|
* Enables or disables the key feedback popup. This is a popup that shows a magnified
|
||||||
* version of the depressed key. By default the preview is enabled.
|
* version of the depressed key. By default the preview is enabled.
|
||||||
|
@ -1212,15 +1238,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
|
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
|
||||||
// If the device does not have distinct multi-touch support panel, ignore all multi-touch
|
// If the device does not have distinct multi-touch support panel, ignore all multi-touch
|
||||||
// events except a transition from/to single-touch.
|
// events except a transition from/to single-touch.
|
||||||
if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
|
if ((!mHasDistinctMultitouch || mIsAccessibilityEnabled)
|
||||||
|
&& pointerCount > 1 && oldPointerCount > 1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track the last few movements to look for spurious swipes.
|
// Track the last few movements to look for spurious swipes.
|
||||||
mSwipeTracker.addMovement(me);
|
mSwipeTracker.addMovement(me);
|
||||||
|
|
||||||
// Gesture detector must be enabled only when mini-keyboard is not on the screen.
|
// Gesture detector must be enabled only when mini-keyboard is not on the screen and
|
||||||
if (mMiniKeyboardView == null
|
// accessibility is not enabled.
|
||||||
|
// TODO: Reconcile gesture detection and accessibility features.
|
||||||
|
if (mMiniKeyboardView == null && !mIsAccessibilityEnabled
|
||||||
&& mGestureDetector != null && mGestureDetector.onTouchEvent(me)) {
|
&& mGestureDetector != null && mGestureDetector.onTouchEvent(me)) {
|
||||||
dismissKeyPreview();
|
dismissKeyPreview();
|
||||||
mHandler.cancelKeyTimers();
|
mHandler.cancelKeyTimers();
|
||||||
|
@ -1265,7 +1294,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
|
// TODO: cleanup this code into a multi-touch to single-touch event converter class?
|
||||||
// Translate mutli-touch event to single-touch events on the device that has no distinct
|
// Translate mutli-touch event to single-touch events on the device that has no distinct
|
||||||
// multi-touch panel.
|
// multi-touch panel.
|
||||||
if (!mHasDistinctMultitouch) {
|
if (!mHasDistinctMultitouch || mIsAccessibilityEnabled) {
|
||||||
// Use only main (id=0) pointer tracker.
|
// Use only main (id=0) pointer tracker.
|
||||||
PointerTracker tracker = getPointerTracker(0);
|
PointerTracker tracker = getPointerTracker(0);
|
||||||
if (pointerCount == 1 && oldPointerCount == 2) {
|
if (pointerCount == 1 && oldPointerCount == 2) {
|
||||||
|
|
|
@ -145,6 +145,10 @@ public class LatinKeyboardView extends KeyboardView {
|
||||||
// If device has distinct multi touch panel, there is no need to check sudden jump.
|
// If device has distinct multi touch panel, there is no need to check sudden jump.
|
||||||
if (hasDistinctMultitouch())
|
if (hasDistinctMultitouch())
|
||||||
return false;
|
return false;
|
||||||
|
// If accessibiliy is enabled, stop looking for sudden jumps because it interferes
|
||||||
|
// with touch exploration of the keyboard.
|
||||||
|
if (isAccessibilityEnabled())
|
||||||
|
return false;
|
||||||
final int action = me.getAction();
|
final int action = me.getAction();
|
||||||
final int x = (int) me.getX();
|
final int x = (int) me.getX();
|
||||||
final int y = (int) me.getY();
|
final int y = (int) me.getY();
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class PointerTracker {
|
||||||
public void invalidateKey(Key key);
|
public void invalidateKey(Key key);
|
||||||
public void showPreview(int keyIndex, PointerTracker tracker);
|
public void showPreview(int keyIndex, PointerTracker tracker);
|
||||||
public boolean hasDistinctMultitouch();
|
public boolean hasDistinctMultitouch();
|
||||||
|
public boolean isAccessibilityEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int mPointerId;
|
public final int mPointerId;
|
||||||
|
@ -68,6 +69,9 @@ public class PointerTracker {
|
||||||
|
|
||||||
private final PointerTrackerKeyState mKeyState;
|
private final PointerTrackerKeyState mKeyState;
|
||||||
|
|
||||||
|
// true if accessibility is enabled in the parent keyboard
|
||||||
|
private boolean mIsAccessibilityEnabled;
|
||||||
|
|
||||||
// true if keyboard layout has been changed.
|
// true if keyboard layout has been changed.
|
||||||
private boolean mKeyboardLayoutHasBeenChanged;
|
private boolean mKeyboardLayoutHasBeenChanged;
|
||||||
|
|
||||||
|
@ -112,6 +116,7 @@ public class PointerTracker {
|
||||||
mKeyDetector = keyDetector;
|
mKeyDetector = keyDetector;
|
||||||
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
||||||
mKeyState = new PointerTrackerKeyState(keyDetector);
|
mKeyState = new PointerTrackerKeyState(keyDetector);
|
||||||
|
mIsAccessibilityEnabled = proxy.isAccessibilityEnabled();
|
||||||
mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
|
mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
|
||||||
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
|
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
|
||||||
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
|
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
|
||||||
|
@ -128,6 +133,10 @@ public class PointerTracker {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAccessibilityEnabled(boolean accessibilityEnabled) {
|
||||||
|
mIsAccessibilityEnabled = accessibilityEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if keyboard has been changed by this callback.
|
// Returns true if keyboard has been changed by this callback.
|
||||||
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
|
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
|
||||||
if (DEBUG_LISTENER)
|
if (DEBUG_LISTENER)
|
||||||
|
@ -312,9 +321,10 @@ public class PointerTracker {
|
||||||
private void onDownEventInternal(int x, int y, long eventTime) {
|
private void onDownEventInternal(int x, int y, long eventTime) {
|
||||||
int keyIndex = mKeyState.onDownKey(x, y, eventTime);
|
int keyIndex = mKeyState.onDownKey(x, y, eventTime);
|
||||||
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
|
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
|
||||||
// from modifier key, or 3) this pointer is on mini-keyboard.
|
// from modifier key, 3) this pointer is on mini-keyboard, or 4) accessibility is enabled.
|
||||||
mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
|
mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
|
||||||
|| mKeyDetector instanceof MiniKeyboardKeyDetector;
|
|| mKeyDetector instanceof MiniKeyboardKeyDetector
|
||||||
|
|| mIsAccessibilityEnabled;
|
||||||
mKeyboardLayoutHasBeenChanged = false;
|
mKeyboardLayoutHasBeenChanged = false;
|
||||||
mKeyAlreadyProcessed = false;
|
mKeyAlreadyProcessed = false;
|
||||||
mIsRepeatableKey = false;
|
mIsRepeatableKey = false;
|
||||||
|
@ -327,7 +337,9 @@ public class PointerTracker {
|
||||||
keyIndex = mKeyState.onDownKey(x, y, eventTime);
|
keyIndex = mKeyState.onDownKey(x, y, eventTime);
|
||||||
}
|
}
|
||||||
if (isValidKeyIndex(keyIndex)) {
|
if (isValidKeyIndex(keyIndex)) {
|
||||||
if (mKeys[keyIndex].mRepeatable) {
|
// Accessibility disables key repeat because users may need to pause on a key to hear
|
||||||
|
// its spoken description.
|
||||||
|
if (mKeys[keyIndex].mRepeatable && !mIsAccessibilityEnabled) {
|
||||||
repeatKey(keyIndex);
|
repeatKey(keyIndex);
|
||||||
mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
|
mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
|
||||||
mIsRepeatableKey = true;
|
mIsRepeatableKey = true;
|
||||||
|
@ -517,8 +529,9 @@ public class PointerTracker {
|
||||||
updateKeyGraphics(keyIndex);
|
updateKeyGraphics(keyIndex);
|
||||||
// The modifier key, such as shift key, should not be shown as preview when multi-touch is
|
// The modifier key, such as shift key, should not be shown as preview when multi-touch is
|
||||||
// supported. On the other hand, if multi-touch is not supported, the modifier key should
|
// supported. On the other hand, if multi-touch is not supported, the modifier key should
|
||||||
// be shown as preview.
|
// be shown as preview. If accessibility is turned on, the modifier key should be shown as
|
||||||
if (mHasDistinctMultitouch && isModifier()) {
|
// preview.
|
||||||
|
if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) {
|
||||||
mProxy.showPreview(NOT_A_KEY, this);
|
mProxy.showPreview(NOT_A_KEY, this);
|
||||||
} else {
|
} else {
|
||||||
mProxy.showPreview(keyIndex, this);
|
mProxy.showPreview(keyIndex, this);
|
||||||
|
@ -526,6 +539,11 @@ public class PointerTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLongPressTimer(int keyIndex) {
|
private void startLongPressTimer(int keyIndex) {
|
||||||
|
// Accessibility disables long press because users are likely to need to pause on a key
|
||||||
|
// for an unspecified duration in order to hear the key's spoken description.
|
||||||
|
if (mIsAccessibilityEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Key key = getKey(keyIndex);
|
Key key = getKey(keyIndex);
|
||||||
if (key.mCode == Keyboard.CODE_SHIFT) {
|
if (key.mCode == Keyboard.CODE_SHIFT) {
|
||||||
mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
|
mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* 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.latin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions for accessibility support.
|
||||||
|
*/
|
||||||
|
public class AccessibilityUtils {
|
||||||
|
/** Shared singleton instance. */
|
||||||
|
private static final AccessibilityUtils sInstance = new AccessibilityUtils();
|
||||||
|
private /* final */ LatinIME mService;
|
||||||
|
private /* final */ AccessibilityManager mAccessibilityManager;
|
||||||
|
private /* final */ Map<Integer, CharSequence> mDescriptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a shared instance of AccessibilityUtils.
|
||||||
|
*
|
||||||
|
* @return A shared instance of AccessibilityUtils.
|
||||||
|
*/
|
||||||
|
public static AccessibilityUtils getInstance() {
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes (or re-initializes) the shared instance of AccessibilityUtils
|
||||||
|
* with the specified parent service and preferences.
|
||||||
|
*
|
||||||
|
* @param service The parent input method service.
|
||||||
|
* @param prefs The parent preferences.
|
||||||
|
*/
|
||||||
|
public static void init(LatinIME service, SharedPreferences prefs) {
|
||||||
|
sInstance.initialize(service, prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessibilityUtils() {
|
||||||
|
// This class is not publicly instantiable.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes (or re-initializes) with the specified parent service and
|
||||||
|
* preferences.
|
||||||
|
*
|
||||||
|
* @param service The parent input method service.
|
||||||
|
* @param prefs The parent preferences.
|
||||||
|
*/
|
||||||
|
private void initialize(LatinIME service, SharedPreferences prefs) {
|
||||||
|
mService = service;
|
||||||
|
mAccessibilityManager = (AccessibilityManager) service.getSystemService(
|
||||||
|
Context.ACCESSIBILITY_SERVICE);
|
||||||
|
mDescriptions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if accessibility is enabled.
|
||||||
|
*
|
||||||
|
* @return {@code true} if accessibility is enabled.
|
||||||
|
*/
|
||||||
|
public boolean isAccessibilityEnabled() {
|
||||||
|
return mAccessibilityManager.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaks a key's action after it has been released. Does not speak letter
|
||||||
|
* keys since typed keys are already spoken aloud by TalkBack.
|
||||||
|
* <p>
|
||||||
|
* No-op if accessibility is not enabled.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param primaryCode The primary code of the released key.
|
||||||
|
* @param switcher The input method's {@link KeyboardSwitcher}.
|
||||||
|
*/
|
||||||
|
public void onRelease(int primaryCode, KeyboardSwitcher switcher) {
|
||||||
|
if (!isAccessibilityEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resId = -1;
|
||||||
|
|
||||||
|
switch (primaryCode) {
|
||||||
|
case Keyboard.CODE_SHIFT: {
|
||||||
|
if (switcher.isShiftedOrShiftLocked()) {
|
||||||
|
resId = R.string.description_shift_on;
|
||||||
|
} else {
|
||||||
|
resId = R.string.description_shift_off;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: {
|
||||||
|
if (switcher.isAlphabetMode()) {
|
||||||
|
resId = R.string.description_symbols_off;
|
||||||
|
} else {
|
||||||
|
resId = R.string.description_symbols_on;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resId >= 0) {
|
||||||
|
speakDescription(mService.getResources().getText(resId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaks a key's description for accessibility. If a key has an explicit
|
||||||
|
* description defined in keycodes.xml, that will be used. Otherwise, if the
|
||||||
|
* key is a Unicode character, then its character will be used.
|
||||||
|
* <p>
|
||||||
|
* No-op if accessibility is not enabled.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param primaryCode The primary code of the pressed key.
|
||||||
|
* @param switcher The input method's {@link KeyboardSwitcher}.
|
||||||
|
*/
|
||||||
|
public void onPress(int primaryCode, KeyboardSwitcher switcher) {
|
||||||
|
if (!isAccessibilityEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Use the current keyboard state to read "Switch to symbols"
|
||||||
|
// instead of just "Symbols" (and similar for shift key).
|
||||||
|
CharSequence description = describeKey(primaryCode);
|
||||||
|
if (description == null && Character.isDefined((char) primaryCode)) {
|
||||||
|
description = Character.toString((char) primaryCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
speakDescription(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a text description for a given key code. If the key does not have
|
||||||
|
* an explicit description, returns <code>null</code>.
|
||||||
|
*
|
||||||
|
* @param keyCode An integer key code.
|
||||||
|
* @return A {@link CharSequence} describing the key or <code>null</code> if
|
||||||
|
* no description is available.
|
||||||
|
*/
|
||||||
|
private CharSequence describeKey(int keyCode) {
|
||||||
|
// If not loaded yet, load key descriptions from XML file.
|
||||||
|
if (mDescriptions == null) {
|
||||||
|
mDescriptions = loadDescriptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mDescriptions.get(keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads key descriptions from resources.
|
||||||
|
*/
|
||||||
|
private Map<Integer, CharSequence> loadDescriptions() {
|
||||||
|
final Map<Integer, CharSequence> descriptions = new HashMap<Integer, CharSequence>();
|
||||||
|
final TypedArray array = mService.getResources().obtainTypedArray(R.array.key_descriptions);
|
||||||
|
|
||||||
|
// Key descriptions are stored as a key code followed by a string.
|
||||||
|
for (int i = 0; i < array.length() - 1; i += 2) {
|
||||||
|
int code = array.getInteger(i, 0);
|
||||||
|
CharSequence desc = array.getText(i + 1);
|
||||||
|
|
||||||
|
descriptions.put(code, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
array.recycle();
|
||||||
|
|
||||||
|
return descriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a character sequence to be read aloud.
|
||||||
|
*
|
||||||
|
* @param description The {@link CharSequence} to be read aloud.
|
||||||
|
*/
|
||||||
|
private void speakDescription(CharSequence description) {
|
||||||
|
// TODO We need to add an AccessibilityEvent type for IMEs.
|
||||||
|
final AccessibilityEvent event = AccessibilityEvent.obtain(
|
||||||
|
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
|
||||||
|
event.setPackageName(mService.getPackageName());
|
||||||
|
event.setClassName(getClass().getName());
|
||||||
|
event.setAddedCount(description.length());
|
||||||
|
event.getText().add(description);
|
||||||
|
|
||||||
|
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -158,6 +158,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
private boolean mIsSettingsSuggestionStripOn;
|
private boolean mIsSettingsSuggestionStripOn;
|
||||||
private boolean mApplicationSpecifiedCompletionOn;
|
private boolean mApplicationSpecifiedCompletionOn;
|
||||||
|
|
||||||
|
private AccessibilityUtils mAccessibilityUtils;
|
||||||
|
|
||||||
private final StringBuilder mComposing = new StringBuilder();
|
private final StringBuilder mComposing = new StringBuilder();
|
||||||
private WordComposer mWord = new WordComposer();
|
private WordComposer mWord = new WordComposer();
|
||||||
private CharSequence mBestWord;
|
private CharSequence mBestWord;
|
||||||
|
@ -371,6 +373,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
LatinImeLogger.init(this, prefs);
|
LatinImeLogger.init(this, prefs);
|
||||||
SubtypeSwitcher.init(this, prefs);
|
SubtypeSwitcher.init(this, prefs);
|
||||||
KeyboardSwitcher.init(this, prefs);
|
KeyboardSwitcher.init(this, prefs);
|
||||||
|
AccessibilityUtils.init(this, prefs);
|
||||||
|
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
@ -378,6 +381,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
|
mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
|
||||||
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
|
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
|
||||||
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
||||||
|
mAccessibilityUtils = AccessibilityUtils.getInstance();
|
||||||
|
|
||||||
final Resources res = getResources();
|
final Resources res = getResources();
|
||||||
mResources = res;
|
mResources = res;
|
||||||
|
@ -560,8 +564,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
|
|
||||||
updateCorrectionMode();
|
updateCorrectionMode();
|
||||||
|
|
||||||
|
final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled();
|
||||||
|
|
||||||
inputView.setPreviewEnabled(mPopupOn);
|
inputView.setPreviewEnabled(mPopupOn);
|
||||||
inputView.setProximityCorrectionEnabled(true);
|
inputView.setProximityCorrectionEnabled(true);
|
||||||
|
inputView.setAccessibilityEnabled(accessibilityEnabled);
|
||||||
// If we just entered a text field, maybe it has some old text that requires correction
|
// If we just entered a text field, maybe it has some old text that requires correction
|
||||||
checkReCorrectionOnStart();
|
checkReCorrectionOnStart();
|
||||||
inputView.setForeground(true);
|
inputView.setForeground(true);
|
||||||
|
@ -1087,6 +1094,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
}
|
}
|
||||||
mLastKeyTime = when;
|
mLastKeyTime = when;
|
||||||
KeyboardSwitcher switcher = mKeyboardSwitcher;
|
KeyboardSwitcher switcher = mKeyboardSwitcher;
|
||||||
|
final boolean accessibilityEnabled = switcher.isAccessibilityEnabled();
|
||||||
final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
|
final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
|
||||||
switch (primaryCode) {
|
switch (primaryCode) {
|
||||||
case Keyboard.CODE_DELETE:
|
case Keyboard.CODE_DELETE:
|
||||||
|
@ -1096,12 +1104,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
break;
|
break;
|
||||||
case Keyboard.CODE_SHIFT:
|
case Keyboard.CODE_SHIFT:
|
||||||
// Shift key is handled in onPress() when device has distinct multi-touch panel.
|
// Shift key is handled in onPress() when device has distinct multi-touch panel.
|
||||||
if (!distinctMultiTouch)
|
if (!distinctMultiTouch || accessibilityEnabled)
|
||||||
switcher.toggleShift();
|
switcher.toggleShift();
|
||||||
break;
|
break;
|
||||||
case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
|
case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
|
||||||
// Symbol key is handled in onPress() when device has distinct multi-touch panel.
|
// Symbol key is handled in onPress() when device has distinct multi-touch panel.
|
||||||
if (!distinctMultiTouch)
|
if (!distinctMultiTouch || accessibilityEnabled)
|
||||||
switcher.changeKeyboardMode();
|
switcher.changeKeyboardMode();
|
||||||
break;
|
break;
|
||||||
case Keyboard.CODE_CANCEL:
|
case Keyboard.CODE_CANCEL:
|
||||||
|
@ -1939,6 +1947,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
} else {
|
} else {
|
||||||
switcher.onOtherKeyPressed();
|
switcher.onOtherKeyPressed();
|
||||||
}
|
}
|
||||||
|
mAccessibilityUtils.onPress(primaryCode, switcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1952,6 +1961,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
} else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
|
} else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
|
||||||
switcher.onReleaseSymbol();
|
switcher.onReleaseSymbol();
|
||||||
}
|
}
|
||||||
|
mAccessibilityUtils.onRelease(primaryCode, switcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue