Merge "Use dynamic operations to construct all ver4 dicts."
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index 96acb15..ed2db07 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -90,7 +90,7 @@
         /*   8:20 */ "more_keys_for_s",
         /*   9:20 */ "more_keys_for_n",
         /*  10:20 */ "label_to_alpha_key",
-        /*  11:15 */ "more_keys_for_y",
+        /*  11:14 */ "more_keys_for_y",
         /*  12:13 */ "more_keys_for_d",
         /*  13:12 */ "more_keys_for_z",
         /*  14:10 */ "more_keys_for_t",
@@ -3585,7 +3585,7 @@
     private static final Object[] LANGUAGES_AND_TEXTS = {
     // "locale", TEXT_ARRAY,  /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */
         "DEFAULT", LANGUAGE_DEFAULT, /* 171/171 default */
-        "af", LANGUAGE_af,    /*   8/ 12 Afrikaans */
+        "af", LANGUAGE_af,    /*   7/ 12 Afrikaans */
         "ar", LANGUAGE_ar,    /*  58/110 Arabic */
         "az", LANGUAGE_az_AZ, /*   8/ 17 Azerbaijani (Azerbaijan) */
         "be", LANGUAGE_be_BY, /*  10/ 33 Belarusian (Belarus) */
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 8cdddeb..851ecc0 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.makedict.WordProperty;
+import com.android.inputmethod.latin.personalization.PersonalizationHelper;
 import com.android.inputmethod.latin.settings.NativeSuggestOptions;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
@@ -507,7 +508,9 @@
      */
     @UsedForTesting
     public static int setCurrentTimeForTest(final int currentTime) {
-        return setCurrentTimeForTestNative(currentTime);
+        final int currentNativeTimestamp = setCurrentTimeForTestNative(currentTime);
+        PersonalizationHelper.currentTimeChangedForTesting(currentNativeTimestamp);
+        return currentNativeTimestamp;
     }
 
     @UsedForTesting
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8ab1bb6..bfc5780 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -172,7 +172,11 @@
         }
 
         public void onCreate() {
-            final Resources res = getOwnerInstance().getResources();
+            final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
+            final Resources res = latinIme.getResources();
             mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions);
             mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state);
             mDoubleSpacePeriodTimeout =
@@ -182,6 +186,9 @@
         @Override
         public void handleMessage(final Message msg) {
             final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
             final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
             switch (msg.what) {
             case MSG_UPDATE_SUGGESTION_STRIP:
@@ -239,7 +246,11 @@
         }
 
         public void postResumeSuggestions() {
-            if (!getOwnerInstance().mSettings.getCurrent().isSuggestionStripVisible()) {
+            final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
+            if (!latinIme.mSettings.getCurrent().isSuggestionStripVisible()) {
                 return;
             }
             removeMessages(MSG_RESUME_SUGGESTIONS);
@@ -326,6 +337,9 @@
             resetPendingImsCallback();
             mIsOrientationChanging = true;
             final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
             if (latinIme.isInputViewShown()) {
                 latinIme.mKeyboardSwitcher.saveKeyboardState();
             }
@@ -362,8 +376,10 @@
                     mPendingSuccessiveImsCallback = true;
                 }
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, editorInfo, restarting);
-                latinIme.onStartInputInternal(editorInfo, restarting);
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, editorInfo, restarting);
+                    latinIme.onStartInputInternal(editorInfo, restarting);
+                }
             }
         }
 
@@ -381,9 +397,11 @@
                             PENDING_IMS_CALLBACK_DURATION);
                 }
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, editorInfo, restarting);
-                latinIme.onStartInputViewInternal(editorInfo, restarting);
-                mAppliedEditorInfo = editorInfo;
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, editorInfo, restarting);
+                    latinIme.onStartInputViewInternal(editorInfo, restarting);
+                    mAppliedEditorInfo = editorInfo;
+                }
             }
         }
 
@@ -393,8 +411,10 @@
                 mHasPendingFinishInputView = true;
             } else {
                 final LatinIME latinIme = getOwnerInstance();
-                latinIme.onFinishInputViewInternal(finishingInput);
-                mAppliedEditorInfo = null;
+                if (latinIme != null) {
+                    latinIme.onFinishInputViewInternal(finishingInput);
+                    mAppliedEditorInfo = null;
+                }
             }
         }
 
@@ -404,8 +424,10 @@
                 mHasPendingFinishInput = true;
             } else {
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, null, false);
-                latinIme.onFinishInputInternal();
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, null, false);
+                    latinIme.onFinishInputInternal();
+                }
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 68b4731..074ec40 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -155,7 +155,7 @@
         clearAndFlushDictionary();
     }
 
-    /* package */ void decayIfNeeded() {
+    /* package */ void runGCIfRequired() {
         runGCIfRequired(false /* mindsBlockByGC */);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
index e9ca662..de2744f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
@@ -43,7 +43,7 @@
     /**
      * Interval to update for decaying dictionaries.
      */
-    private static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60);
+    /* package */ static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60);
 
     public static void setUpIntervalAlarmForDictionaryDecaying(Context context) {
         AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
@@ -60,7 +60,7 @@
     public void onReceive(final Context context, final Intent intent) {
         final String action = intent.getAction();
         if (action.equals(DICTIONARY_DECAY_INTENT_ACTION)) {
-            PersonalizationHelper.tryDecayingAllOpeningUserHistoryDictionary();
+            PersonalizationHelper.runGCOnAllOpenedUserHistoryDictionaries();
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 5ae2fb6..385b525 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.personalization;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.FileUtils;
 
@@ -27,6 +28,7 @@
 import java.lang.ref.SoftReference;
 import java.util.Locale;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 public class PersonalizationHelper {
     private static final String TAG = PersonalizationHelper.class.getSimpleName();
@@ -59,14 +61,35 @@
         }
     }
 
-    public static void tryDecayingAllOpeningUserHistoryDictionary() {
-        for (final ConcurrentHashMap.Entry<String, SoftReference<UserHistoryDictionary>> entry
-                : sLangUserHistoryDictCache.entrySet()) {
-            if (entry.getValue() != null) {
-                final UserHistoryDictionary dict = entry.getValue().get();
-                if (dict != null) {
-                    dict.decayIfNeeded();
-                }
+    private static int sCurrentTimestampForTesting = 0;
+    public static void currentTimeChangedForTesting(final int currentTimestamp) {
+        if (TimeUnit.MILLISECONDS.toSeconds(
+                DictionaryDecayBroadcastReciever.DICTIONARY_DECAY_INTERVAL)
+                        < currentTimestamp - sCurrentTimestampForTesting) {
+            // TODO: Run GC for both PersonalizationDictionary and UserHistoryDictionary.
+            runGCOnAllOpenedUserHistoryDictionaries();
+        }
+    }
+
+    public static void runGCOnAllOpenedUserHistoryDictionaries() {
+        runGCOnAllDictionariesIfRequired(sLangUserHistoryDictCache);
+    }
+
+    @UsedForTesting
+    public static void runGCOnAllOpenedPersonalizationDictionaries() {
+        runGCOnAllDictionariesIfRequired(sLangPersonalizationDictCache);
+    }
+
+    private static <T extends DecayingExpandableBinaryDictionaryBase>
+            void runGCOnAllDictionariesIfRequired(
+                    final ConcurrentHashMap<String, SoftReference<T>> dictionaryMap) {
+        for (final ConcurrentHashMap.Entry<String, SoftReference<T>> entry
+                : dictionaryMap.entrySet()) {
+            final DecayingExpandableBinaryDictionaryBase dict = entry.getValue().get();
+            if (dict != null) {
+                dict.runGCIfRequired();
+            } else {
+                dictionaryMap.remove(entry.getKey());
             }
         }
     }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index 6dcf513..9939a43 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -80,12 +80,25 @@
                 || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
     }
 
+    private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
+        final StringBuilder sb = new StringBuilder();
+        for (int index = 0; index < subtypeList.size(); index++) {
+            final InputMethodSubtype subtype = subtypeList.get(index);
+            sb.append((index + 1) + ": ");
+            sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
     public final void testAllSubtypesCount() {
-        assertEquals(NUMBER_OF_SUBTYPES, mAllSubtypesList.size());
+        assertEquals(toString(mAllSubtypesList),
+                NUMBER_OF_SUBTYPES, mAllSubtypesList.size());
     }
 
     public final void testAsciiCapableSubtypesCount() {
-        assertEquals(NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size());
+        assertEquals(toString(mAsciiCapableSubtypesList),
+                NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size());
     }
 
     protected final InputMethodSubtype getSubtype(final Locale locale,
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index b1239f0..6ace2de 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -267,13 +267,13 @@
             assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
         }
         forcePassingShortTime();
-        dict.decayIfNeeded();
+        dict.runGCIfRequired();
         dict.waitAllTasksForTests();
         for (final String word : words) {
             assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
         }
         forcePassingLongTime();
-        dict.decayIfNeeded();
+        dict.runGCIfRequired();
         dict.waitAllTasksForTests();
         for (final String word : words) {
             assertFalse(dict.isInUnderlyingBinaryDictionaryForTests(word));
diff --git a/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
index ee96f44..5a90e62 100644
--- a/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
@@ -62,7 +62,6 @@
     <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
          U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE -->
     <string name="more_keys_for_n">&#x00F1;,&#x0144;</string>
-    <string name="more_keys_for_y">&#x00FD;,&#x0177;,&#x00FF;,&#x0133;</string>
     <!-- U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
          U+0133: "ij" LATIN SMALL LIGATURE IJ -->
     <string name="more_keys_for_y">&#x00FD;,&#x0133;</string>