diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index a1f34887b..8bc97f66e 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -142,6 +142,9 @@
Wait
+
+ Plug in a headset to hear password keys spoken aloud.
+
Current text is "%s"
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 7e71b5f36..4a2542dec 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -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);
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index e1b778126..4c109c708 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -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);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 36e97af11..1278c5ec8 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -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();