Merge "Import revised translations."
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index a3e8d4a..e2422d2 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -38,7 +38,7 @@
     <bool name="config_default_bigram_prediction">false</bool>
     <bool name="config_default_sound_enabled">false</bool>
     <bool name="config_default_vibration_enabled">true</bool>
-    <bool name="config_auto_correction_spacebar_led_enabled">true</bool>
+    <bool name="config_auto_correction_spacebar_led_enabled">false</bool>
     <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
     <bool name="config_show_mini_keyboard_at_touched_point">false</bool>
     <!-- The language is never displayed if == 0, always displayed if < 0 -->
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/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 5c54fa5..876bd65 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -24,18 +24,22 @@
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Locale;
 
 public class SuggestionSpanUtils {
+    private static final String TAG = SuggestionSpanUtils.class.getSimpleName();
     // TODO: Use reflection to get field values
     public static final String ACTION_SUGGESTION_PICKED =
             "android.text.style.SUGGESTION_PICKED";
     public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
     public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
     public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
+    // TODO: Use the API constant after it gets public.
+    public static final int FLAG_AUTO_CORRECTION = 0x0004;
     public static final int SUGGESTION_MAX_SIZE = 5;
     public static final boolean SUGGESTION_SPAN_IS_SUPPORTED;
 
@@ -50,6 +54,25 @@
                 CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null;
     }
 
+    public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
+            Context context, CharSequence text) {
+        if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null) {
+            return text;
+        }
+        final Spannable spannable = text instanceof Spannable
+                ? (Spannable) text : new SpannableString(text);
+        final Object[] args =
+                { context, null, new String[] {}, FLAG_AUTO_CORRECTION,
+                        (Class<?>) SuggestionSpanPickedNotificationReceiver.class };
+        final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args);
+        if (ss == null) {
+            Log.w(TAG, "Suggestion span was not created.");
+            return text;
+        }
+        spannable.setSpan(ss, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        return spannable;
+    }
+
     public static CharSequence getTextWithSuggestionSpan(Context context,
             CharSequence pickedWord, SuggestedWords suggestedWords) {
         if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 3b31137..958092b 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();
@@ -1602,6 +1608,17 @@
             mKeyboardSwitcher.onAutoCorrectionStateChanged(
                     words.hasWordAboveAutoCorrectionScoreThreshold());
         }
+
+        // Put a blue underline to a word in TextView which will be auto-corrected.
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic != null && Utils.willAutoCorrect(words)) {
+            final CharSequence textWithUnderline =
+                    SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
+                            this, mComposingStringBuilder);
+            if (!TextUtils.isEmpty(textWithUnderline)) {
+                ic.setComposingText(textWithUnderline, 1);
+            }
+        }
     }
 
     public void updateSuggestions() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java
index fe54f4a..3271b82 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java
@@ -260,7 +260,7 @@
 
         private CharSequence getStyledSuggestionWord(SuggestedWords suggestions, int pos) {
             final CharSequence word = suggestions.getWord(pos);
-            final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions);
+            final boolean isAutoCorrect = pos == 1 && Utils.willAutoCorrect(suggestions);
             final boolean isTypedWordValid = pos == 0 && suggestions.mTypedWordValid;
             if (!isAutoCorrect && !isTypedWordValid)
                 return word;
@@ -278,14 +278,10 @@
             return spannedWord;
         }
 
-        private static boolean willAutoCorrect(SuggestedWords suggestions) {
-            return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion;
-        }
-
         private int getWordPosition(int index, SuggestedWords suggestions) {
             // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
             // suggestions.
-            final int centerPos = willAutoCorrect(suggestions) ? 1 : 0;
+            final int centerPos = Utils.willAutoCorrect(suggestions) ? 1 : 0;
             if (index == mCenterSuggestionIndex) {
                 return centerPos;
             } else if (index == centerPos) {
@@ -300,7 +296,7 @@
             final boolean isSuggested = (pos != 0);
 
             final int color;
-            if (index == mCenterSuggestionIndex && willAutoCorrect(suggestions)) {
+            if (index == mCenterSuggestionIndex && Utils.willAutoCorrect(suggestions)) {
                 color = mColorAutoCorrect;
             } else if (isSuggested) {
                 color = mColorSuggested;
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 7712765..de29304 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -790,4 +790,8 @@
         }
         return -1;
     }
+
+    public static boolean willAutoCorrect(SuggestedWords suggestions) {
+        return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion;
+    }
 }