Let accessibility users know to connect a headset when the IME connects to a password field.

Change-Id: If96cd7626950dd12e88a8a97f5e405d303d41e06
main
Alan Viverette 2011-10-05 16:59:31 -07:00
parent c1368a8efc
commit b0c8db018d
4 changed files with 54 additions and 27 deletions

View File

@ -142,6 +142,9 @@
<!-- 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 description to let the user know that when typing in a password, they can plug in a headset in to hear spoken descriptions of the keys they type. [CHAR LIMIT=NONE] -->
<string name="spoken_use_headphones">Plug in a headset to hear password keys spoken aloud.</string>
<!-- Spoken description for the currently entered text --> <!-- Spoken description for the currently entered text -->
<string name="spoken_current_text_is">Current text is "%s"</string> <string name="spoken_current_text_is">Current text is "%s"</string>
<!-- Spoken description when there is no text entered --> <!-- Spoken description when there is no text entered -->

View File

@ -19,15 +19,19 @@ package com.android.inputmethod.accessibility;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper; import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper;
import com.android.inputmethod.compat.AudioManagerCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.latin.R;
public class AccessibilityUtils { public class AccessibilityUtils {
private static final String TAG = AccessibilityUtils.class.getSimpleName(); private static final String TAG = AccessibilityUtils.class.getSimpleName();
@ -37,8 +41,10 @@ public class AccessibilityUtils {
private static final AccessibilityUtils sInstance = new AccessibilityUtils(); private static final AccessibilityUtils sInstance = new AccessibilityUtils();
private Context mContext;
private AccessibilityManager mAccessibilityManager; private AccessibilityManager mAccessibilityManager;
private AccessibilityManagerCompatWrapper mCompatManager; private AccessibilityManagerCompatWrapper mCompatManager;
private AudioManagerCompatWrapper mAudioManager;
/* /*
* Setting this constant to {@code false} will disable all keyboard * Setting this constant to {@code false} will disable all keyboard
@ -67,9 +73,14 @@ public class AccessibilityUtils {
} }
private void initInternal(Context context, SharedPreferences prefs) { private void initInternal(Context context, SharedPreferences prefs) {
mContext = context;
mAccessibilityManager = (AccessibilityManager) context mAccessibilityManager = (AccessibilityManager) context
.getSystemService(Context.ACCESSIBILITY_SERVICE); .getSystemService(Context.ACCESSIBILITY_SERVICE);
mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager); mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager);
final AudioManager audioManager = (AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
mAudioManager = new AudioManagerCompatWrapper(audioManager);
} }
/** /**
@ -101,6 +112,22 @@ public class AccessibilityUtils {
|| action == MotionEventCompatUtils.ACTION_HOVER_MOVE; || action == MotionEventCompatUtils.ACTION_HOVER_MOVE;
} }
/**
* @return {@code true} if the device should not speak text (eg.
* non-control) characters
*/
public boolean shouldObscureInput(EditorInfo attribute) {
if (attribute == null)
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 InputTypeCompatUtils.isPasswordInputType(attribute.inputType);
}
/** /**
* Sends the specified text to the {@link AccessibilityManager} to be * Sends the specified text to the {@link AccessibilityManager} to be
* spoken. * spoken.
@ -117,7 +144,7 @@ public class AccessibilityUtils {
// class. Instead, we're just forcing a fake AccessibilityEvent into // class. Instead, we're just forcing a fake AccessibilityEvent into
// the screen reader to make it speak. // the screen reader to make it speak.
final AccessibilityEvent event = AccessibilityEvent final AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER); .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
event.setPackageName(PACKAGE); event.setPackageName(PACKAGE);
event.setClassName(CLASS); event.setClassName(CLASS);
@ -127,4 +154,18 @@ public class AccessibilityUtils {
mAccessibilityManager.sendAccessibilityEvent(event); mAccessibilityManager.sendAccessibilityEvent(event);
} }
/**
* Handles speaking the "connect a headset to hear passwords" notification
* when connecting to a password field.
*
* @param attribute The input connection's editor info attribute.
* @param restarting Whether the connection is being restarted.
*/
public void onStartInputViewInternal(EditorInfo attribute, boolean restarting) {
if (shouldObscureInput(attribute)) {
final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
speak(text);
}
}
} }

View File

@ -21,7 +21,6 @@ import android.content.SharedPreferences;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
@ -29,8 +28,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.AccessibilityEventCompatUtils; import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
import com.android.inputmethod.compat.AudioManagerCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyDetector;
@ -48,7 +45,6 @@ public class AccessibleKeyboardViewProxy {
private FlickGestureDetector mGestureDetector; private FlickGestureDetector mGestureDetector;
private LatinKeyboardView mView; private LatinKeyboardView mView;
private AccessibleKeyboardActionListener mListener; private AccessibleKeyboardActionListener mListener;
private AudioManagerCompatWrapper mAudioManager;
private int mScaledEdgeSlop; private int mScaledEdgeSlop;
private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
@ -82,26 +78,6 @@ public class AccessibleKeyboardViewProxy {
mInputMethod = inputMethod; mInputMethod = inputMethod;
mGestureDetector = new KeyboardFlickGestureDetector(inputMethod); mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
mScaledEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop(); mScaledEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop();
final AudioManager audioManager = (AudioManager) inputMethod
.getSystemService(Context.AUDIO_SERVICE);
mAudioManager = new AudioManagerCompatWrapper(audioManager);
}
/**
* @return {@code true} if the device should not speak text (eg. non-control) characters
*/
private boolean shouldObscureInput() {
// Always speak if the user is listening through headphones.
if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn())
return false;
final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
if (info == null)
return false;
// Don't speak if the IME is connected to a password field.
return InputTypeCompatUtils.isPasswordInputType(info.inputType);
} }
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
@ -118,7 +94,8 @@ public class AccessibleKeyboardViewProxy {
if (key == null) if (key == null)
break; break;
final boolean shouldObscure = shouldObscureInput(); final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
final boolean shouldObscure = AccessibilityUtils.getInstance().shouldObscureInput(info);
final CharSequence description = KeyCodeDescriptionMapper.getInstance() final CharSequence description = KeyCodeDescriptionMapper.getInstance()
.getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key, .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
shouldObscure); shouldObscure);

View File

@ -693,6 +693,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
return; return;
} }
// Forward this event to the accessibility utilities, if enabled.
final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
if (accessUtils.isTouchExplorationEnabled()) {
accessUtils.onStartInputViewInternal(attribute, restarting);
}
mSubtypeSwitcher.updateParametersOnStartInputView(); mSubtypeSwitcher.updateParametersOnStartInputView();
TextEntryState.reset(); TextEntryState.reset();