Use translation of fallback umlauts digraphs for German.

For German : handle "ae", "oe" and "ue" to be alternate forms for
umlaut-bearing versions of "a", "o" and "u".

Issue: 3275926

Change-Id: I056c707cdacc464ceab63be56c016c7f8439196c
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index b1f7379..8dec7ab 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -65,6 +65,7 @@
             android:label="@string/subtype_mode_de_keyboard"
             android:imeSubtypeLocale="de"
             android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="requiresGermanUmlautProcessing"
     />
     <subtype android:icon="@drawable/ic_subtype_mic"
             android:label="@string/subtype_mode_de_voice"
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index d866ec1..08ddd25 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -58,6 +58,25 @@
     private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
 
     private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+    private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+
+    private static class Flags {
+        private static class FlagEntry {
+            public final String mName;
+            public final int mValue;
+            public FlagEntry(String name, int value) {
+                mName = name;
+                mValue = value;
+            }
+        }
+        public static final FlagEntry[] ALL_FLAGS = {
+            // Here should reside all flags that trigger some special processing
+            // These *must* match the definition in UnigramDictionary enum in
+            // unigram_dictionary.h so please update both at the same time.
+            new FlagEntry("requiresGermanUmlautProcessing", 0x1)
+        };
+    }
+    private int mFlags = 0;
 
     private BinaryDictionary() {
     }
@@ -91,6 +110,7 @@
                 return null;
             }
         }
+        sInstance.initFlags();
         return sInstance;
     }
 
@@ -109,16 +129,26 @@
         return sInstance;
     }
 
+    private void initFlags() {
+        int flags = 0;
+        for (Flags.FlagEntry entry : Flags.ALL_FLAGS) {
+            if (mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(entry.mName))
+                flags |= entry.mValue;
+        }
+        mFlags = flags;
+    }
+
     static {
         Utils.loadNativeLibrary();
     }
+
     private native int openNative(String sourceDir, long dictOffset, long dictSize,
             int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength,
             int maxWords, int maxAlternatives);
     private native void closeNative(int dict);
     private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
     private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates,
-            int[] yCoordinates, int[] inputCodes, int codesSize, char[] outputChars,
+            int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars,
             int[] frequencies);
     private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
             int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
@@ -207,7 +237,7 @@
         return getSuggestionsNative(
                 mNativeDict, keyboard.getProximityInfo(),
                 codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
-                outputChars, frequencies);
+                mFlags, outputChars, frequencies);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index c43f5b5..50827c6 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -74,10 +74,10 @@
     private InputMethodInfo mShortcutInputMethodInfo;
     private InputMethodSubtype mShortcutSubtype;
     private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
+    private InputMethodSubtype mCurrentSubtype;
     private Locale mSystemLocale;
     private Locale mInputLocale;
     private String mInputLocaleStr;
-    private String mMode;
     private VoiceInput mVoiceInput;
     /*-----------------------------------------------------------*/
 
@@ -110,8 +110,7 @@
         mSystemLocale = null;
         mInputLocale = null;
         mInputLocaleStr = null;
-        // Mode is initialized to KEYBOARD_MODE, in case that LatinIME can't obtain currentSubtype
-        mMode = KEYBOARD_MODE;
+        mCurrentSubtype = null;
         mAllEnabledSubtypesOfCurrentInputMethod = null;
         // TODO: Voice input should be created here
         mVoiceInput = null;
@@ -145,6 +144,7 @@
 
     // Reload enabledSubtypes from the framework.
     private void updateEnabledSubtypes() {
+        final String currentMode = getCurrentSubtypeMode();
         boolean foundCurrentSubtypeBecameDisabled = true;
         mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
                 null, true);
@@ -157,7 +157,7 @@
             if (mLocaleSplitter.hasNext()) {
                 mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
             }
-            if (locale.equals(mInputLocaleStr) && mode.equals(mMode)) {
+            if (locale.equals(mInputLocaleStr) && mode.equals(currentMode)) {
                 foundCurrentSubtypeBecameDisabled = false;
             }
             if (KEYBOARD_MODE.equals(ims.getMode())) {
@@ -168,7 +168,7 @@
                 && mIsSystemLanguageSameAsInputLanguage);
         if (foundCurrentSubtypeBecameDisabled) {
             if (DBG) {
-                Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + mMode);
+                Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + currentMode);
                 Log.w(TAG, "Last subtype was disabled. Update to the current one.");
             }
             updateSubtype(mImm.getCurrentInputMethodSubtype());
@@ -209,9 +209,10 @@
     public void updateSubtype(InputMethodSubtype newSubtype) {
         final String newLocale;
         final String newMode;
+        final String oldMode = getCurrentSubtypeMode();
         if (newSubtype == null) {
             // Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
-            // fallback to the default locale and mode.
+            // fallback to the default locale.
             Log.w(TAG, "Couldn't get the current subtype.");
             newLocale = "en_US";
             newMode = KEYBOARD_MODE;
@@ -220,8 +221,8 @@
             newMode = newSubtype.getMode();
         }
         if (DBG) {
-            Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
-                    + ", from: " + mInputLocaleStr + ", " + mMode);
+            Log.w(TAG, "Update subtype to:" + newLocale + "," + newSubtype.getMode()
+                    + ", from: " + mInputLocaleStr + ", " + oldMode);
         }
         boolean languageChanged = false;
         if (!newLocale.equals(mInputLocaleStr)) {
@@ -231,13 +232,12 @@
             updateInputLocale(newLocale);
         }
         boolean modeChanged = false;
-        String oldMode = mMode;
-        if (!newMode.equals(mMode)) {
-            if (mMode != null) {
+        if (!newMode.equals(oldMode)) {
+            if (oldMode != null) {
                 modeChanged = true;
             }
-            mMode = newMode;
         }
+        mCurrentSubtype = newSubtype;
 
         // If the old mode is voice input, we need to reset or cancel its status.
         // We cancel its status when we change mode, while we reset otherwise.
@@ -262,7 +262,7 @@
                 triggerVoiceIME();
             }
         } else {
-            Log.w(TAG, "Unknown subtype mode: " + mMode);
+            Log.w(TAG, "Unknown subtype mode: " + newMode);
             if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
                 // We need to reset the voice input to release the resources and to reset its status
                 // as it is not the current input mode.
@@ -483,7 +483,7 @@
     }
 
     public boolean isKeyboardMode() {
-        return KEYBOARD_MODE.equals(mMode);
+        return KEYBOARD_MODE.equals(getCurrentSubtypeMode());
     }
 
 
@@ -506,7 +506,7 @@
     }
 
     public boolean isVoiceMode() {
-        return VOICE_MODE.equals(mMode);
+        return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
     }
 
     private void triggerVoiceIME() {
@@ -572,6 +572,30 @@
         }
     }
 
+    /////////////////////////////
+    // Other utility functions //
+    /////////////////////////////
+
+    public String getCurrentSubtypeExtraValue() {
+        // If null, return what an empty ExtraValue would return : the empty string.
+        return null != mCurrentSubtype ? mCurrentSubtype.getExtraValue() : "";
+    }
+
+    public boolean currentSubtypeContainsExtraValueKey(String key) {
+        // If null, return what an empty ExtraValue would return : false.
+        return null != mCurrentSubtype ? mCurrentSubtype.containsExtraValueKey(key) : false;
+    }
+
+    public String getCurrentSubtypeExtraValueOf(String key) {
+        // If null, return what an empty ExtraValue would return : null.
+        return null != mCurrentSubtype ? mCurrentSubtype.getExtraValueOf(key) : null;
+    }
+
+    public String getCurrentSubtypeMode() {
+        return null != mCurrentSubtype ? mCurrentSubtype.getMode() : KEYBOARD_MODE;
+    }
+
+
     // A list of locales which are supported by default for voice input, unless we get a
     // different list from Gservices.
     private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =