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

Change-Id: If96cd7626950dd12e88a8a97f5e405d303d41e06
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index a1f3488..8bc97f6 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -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 -->
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 7e71b5f..4a2542d 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -19,15 +19,19 @@
 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 @@
 
     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 @@
     }
 
     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);
     }
 
     /**
@@ -102,6 +113,22 @@
     }
 
     /**
+     * @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 @@
         // 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 @@
 
         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 e1b7781..4c109c7 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.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.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 @@
     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 @@
         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 @@
             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 36e97af..1278c5e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -693,6 +693,12 @@
             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();