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

This commit is contained in:
Svetoslav Ganov 2011-10-07 15:36:04 -07:00 committed by Android (Google) Code Review
commit 7c2d7ad634
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]-->
<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 -->
<string name="spoken_current_text_is">Current text is "%s"</string>
<!-- 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.SharedPreferences;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
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.AudioManagerCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.latin.R;
public class AccessibilityUtils {
private static final String TAG = AccessibilityUtils.class.getSimpleName();
@ -37,8 +41,10 @@ public class AccessibilityUtils {
private static final AccessibilityUtils sInstance = new AccessibilityUtils();
private Context mContext;
private AccessibilityManager mAccessibilityManager;
private AccessibilityManagerCompatWrapper mCompatManager;
private AudioManagerCompatWrapper mAudioManager;
/*
* Setting this constant to {@code false} will disable all keyboard
@ -67,9 +73,14 @@ public class AccessibilityUtils {
}
private void initInternal(Context context, SharedPreferences prefs) {
mContext = context;
mAccessibilityManager = (AccessibilityManager) context
.getSystemService(Context.ACCESSIBILITY_SERVICE);
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;
}
/**
* @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
* spoken.
@ -117,7 +144,7 @@ public class AccessibilityUtils {
// class. Instead, we're just forcing a fake AccessibilityEvent into
// the screen reader to make it speak.
final AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
event.setPackageName(PACKAGE);
event.setClassName(CLASS);
@ -127,4 +154,18 @@ public class AccessibilityUtils {
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.Paint;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@ -29,8 +28,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.EditorInfo;
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.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
@ -48,7 +45,6 @@ public class AccessibleKeyboardViewProxy {
private FlickGestureDetector mGestureDetector;
private LatinKeyboardView mView;
private AccessibleKeyboardActionListener mListener;
private AudioManagerCompatWrapper mAudioManager;
private int mScaledEdgeSlop;
private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
@ -82,26 +78,6 @@ public class AccessibleKeyboardViewProxy {
mInputMethod = inputMethod;
mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
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,
@ -118,7 +94,8 @@ public class AccessibleKeyboardViewProxy {
if (key == null)
break;
final boolean shouldObscure = shouldObscureInput();
final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
final boolean shouldObscure = AccessibilityUtils.getInstance().shouldObscureInput(info);
final CharSequence description = KeyCodeDescriptionMapper.getInstance()
.getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
shouldObscure);

View file

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