Let accessibility users know to connect a headset when the IME connects to a password field.
Change-Id: If96cd7626950dd12e88a8a97f5e405d303d41e06main
parent
c1368a8efc
commit
b0c8db018d
|
@ -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 -->
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue