From 1404c1af850236da4fe4ed3825b70cb228a1da5b Mon Sep 17 00:00:00 2001
From: Jean Chalard <jchalard@google.com>
Date: Fri, 20 Dec 2013 15:00:44 +0900
Subject: [PATCH] [IL21] Move the workaround utils to a better place.

Bug: 8636060
Change-Id: I566f9e109ebcabe3a6a778118434ce79d02d1113
---
 .../compat/AppWorkaroundsUtils.java           |  6 +-
 .../inputmethod/latin/InputAttributes.java    |  2 +
 .../android/inputmethod/latin/LatinIME.java   | 38 +++-------
 .../latin/inputlogic/InputLogic.java          | 14 ++--
 .../inputmethod/latin/settings/Settings.java  | 10 ++-
 .../latin/settings/SettingsValues.java        | 30 +++++++-
 .../utils/TargetPackageInfoGetterTask.java    | 14 ++--
 .../latin/AppWorkaroundsTests.java            | 74 +++++++++++++++++++
 .../inputmethod/latin/InputTestsBase.java     | 10 ++-
 9 files changed, 147 insertions(+), 51 deletions(-)
 create mode 100644 tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java

diff --git a/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java b/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
index 7e9e2e37b3..6e43cc9a7f 100644
--- a/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
+++ b/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
@@ -23,10 +23,10 @@ import android.os.Build.VERSION_CODES;
  * A class to encapsulate work-arounds specific to particular apps.
  */
 public class AppWorkaroundsUtils {
-    private PackageInfo mPackageInfo; // May be null
-    private boolean mIsBrokenByRecorrection = false;
+    private final PackageInfo mPackageInfo; // May be null
+    private final boolean mIsBrokenByRecorrection;
 
-    public void setPackageInfo(final PackageInfo packageInfo) {
+    public AppWorkaroundsUtils(final PackageInfo packageInfo) {
         mPackageInfo = packageInfo;
         mIsBrokenByRecorrection = AppWorkaroundsHelper.evaluateIsBrokenByRecorrection(
                 packageInfo);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index fcf0430319..b01bc4ba5f 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -29,6 +29,7 @@ import com.android.inputmethod.latin.utils.StringUtils;
 public final class InputAttributes {
     private final String TAG = InputAttributes.class.getSimpleName();
 
+    final public String mTargetApplicationPackageName;
     final public boolean mInputTypeNoAutoCorrect;
     final public boolean mIsSettingsSuggestionStripOn;
     final public boolean mApplicationSpecifiedCompletionOn;
@@ -36,6 +37,7 @@ public final class InputAttributes {
     final private int mInputType;
 
     public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
+        mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null;
         final int inputType = null != editorInfo ? editorInfo.inputType : 0;
         final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
         mInputType = inputType;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a941dc3f44..6f5cc9fee0 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,7 +28,6 @@ import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -61,7 +60,6 @@ import android.view.inputmethod.InputMethodSubtype;
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
 import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.AppWorkaroundsUtils;
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
 import com.android.inputmethod.keyboard.Keyboard;
@@ -89,7 +87,6 @@ import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
 import com.android.inputmethod.latin.utils.TextRange;
 import com.android.inputmethod.research.ResearchLogger;
 
@@ -102,8 +99,7 @@ import java.util.Locale;
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService implements KeyboardActionListener,
-        SuggestionStripView.Listener, TargetPackageInfoGetterTask.OnTargetPackageInfoKnownListener,
-        Suggest.SuggestInitializationListener {
+        SuggestionStripView.Listener, Suggest.SuggestInitializationListener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean TRACE = false;
     private static boolean DEBUG = false;
@@ -128,8 +124,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     private SuggestionStripView mSuggestionStripView;
 
     private CompletionInfo[] mApplicationSpecifiedCompletions;
-    // TODO[IL]: Make this an AsyncResultHolder or a Future in SettingsValues
-    public AppWorkaroundsUtils mAppWorkAroundsUtils = new AppWorkaroundsUtils();
 
     private RichInputMethodManager mRichImm;
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@@ -519,9 +513,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     @UsedForTesting
     void loadSettings() {
         final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-        final InputAttributes inputAttributes =
-                new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
-        mSettings.loadSettings(locale, inputAttributes);
+        final EditorInfo editorInfo = getCurrentInputEditorInfo();
+        final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
+        mSettings.loadSettings(this, locale, inputAttributes);
         AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(mSettings.getCurrent());
         // To load the keyboard we need to load all the settings once, but resetting the
         // contacts dictionary should be deferred until after the new layout has been displayed
@@ -798,13 +792,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         if (isDifferentTextField) {
             mainKeyboardView.closing();
             loadSettings();
-            final PackageInfo packageInfo =
-                    TargetPackageInfoGetterTask.getCachedPackageInfo(editorInfo.packageName);
-            mAppWorkAroundsUtils.setPackageInfo(packageInfo);
-            if (null == packageInfo) {
-                new TargetPackageInfoGetterTask(this /* context */, this /* listener */)
-                        .execute(editorInfo.packageName);
-            }
             currentSettingsValues = mSettings.getCurrent();
 
             if (suggest != null && currentSettingsValues.mCorrectionEnabled) {
@@ -912,12 +899,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         }
     }
 
-    // Callback for the TargetPackageInfoGetterTask
-    @Override
-    public void onTargetPackageInfoKnown(final PackageInfo info) {
-        mAppWorkAroundsUtils.setPackageInfo(info);
-    }
-
     @Override
     public void onWindowHidden() {
         super.onWindowHidden();
@@ -1991,12 +1972,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // HACK: We may want to special-case some apps that exhibit bad behavior in case of
         // recorrection. This is a temporary, stopgap measure that will be removed later.
         // TODO: remove this.
-        if (mAppWorkAroundsUtils.isBrokenByRecorrection()) return;
+        final SettingsValues settingsValues = mSettings.getCurrent();
+        if (settingsValues.isBrokenByRecorrection()) return;
         // A simple way to test for support from the TextView.
         if (!isSuggestionsStripVisible()) return;
         // Recorrection is not supported in languages without spaces because we don't know
         // how to segment them yet.
-        if (!mSettings.getCurrent().mCurrentLanguageHasSpaces) return;
+        if (!settingsValues.mCurrentLanguageHasSpaces) return;
         // If the cursor is not touching a word, or if there is a selection, return right away.
         if (mInputLogic.mLastSelectionStart != mInputLogic.mLastSelectionEnd) return;
         // If we don't know the cursor location, return.
@@ -2322,8 +2304,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     }
 
     public void debugDumpStateAndCrashWithException(final String context) {
-        final StringBuilder s = new StringBuilder(mAppWorkAroundsUtils.toString());
-        s.append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
+        final SettingsValues settingsValues = mSettings.getCurrent();
+        final StringBuilder s = new StringBuilder(settingsValues.toString());
+        s.append("\nAttributes : ").append(settingsValues.mInputAttributes)
                 .append("\nContext : ").append(context);
         throw new RuntimeException(s.toString());
     }
@@ -2348,5 +2331,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         p.println("  mVibrateOn=" + settingsValues.mVibrateOn);
         p.println("  mKeyPreviewPopupOn=" + settingsValues.mKeyPreviewPopupOn);
         p.println("  inputAttributes=" + settingsValues.mInputAttributes);
+        // TODO: Dump all settings values
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index db27d22b74..db9bf348f3 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -387,7 +387,7 @@ public final class InputLogic {
             final boolean swapWeakSpace = maybeStripSpace(settingsValues,
                     codePoint, spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
 
-            sendKeyCodePoint(codePoint);
+            sendKeyCodePoint(settingsValues, codePoint);
 
             if (swapWeakSpace) {
                 swapSwapperAndSpace(keyboardSwitcher);
@@ -449,7 +449,7 @@ public final class InputLogic {
         }
 
         if (!shouldAvoidSendingCode) {
-            sendKeyCodePoint(codePoint);
+            sendKeyCodePoint(settingsValues, codePoint);
         }
 
         if (Constants.CODE_SPACE == codePoint) {
@@ -593,7 +593,7 @@ public final class InputLogic {
                     // This should never happen.
                     Log.e(TAG, "Backspace when we don't know the selection position");
                 }
-                if (mLatinIME.mAppWorkAroundsUtils.isBeforeJellyBean() ||
+                if (settingsValues.isBeforeJellyBean() ||
                         settingsValues.mInputAttributes.isTypeNull()) {
                     // There are two possible reasons to send a key event: either the field has
                     // type TYPE_NULL, in which case the keyboard should send events, or we are
@@ -1167,9 +1167,10 @@ public final class InputLogic {
      * Normally we send code points with commitText, but there are some cases (where backward
      * compatibility is a concern for example) where we want to use deprecated methods.
      *
+     * @param settingsValues the current values of the settings.
      * @param codePoint the code point to send.
      */
-    private void sendKeyCodePoint(final int codePoint) {
+    private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) {
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_sendKeyCodePoint(codePoint);
         }
@@ -1181,8 +1182,7 @@ public final class InputLogic {
         }
 
         // TODO: we should do this also when the editor has TYPE_NULL
-        if (Constants.CODE_ENTER == codePoint
-                && mLatinIME.mAppWorkAroundsUtils.isBeforeJellyBean()) {
+        if (Constants.CODE_ENTER == codePoint && settingsValues.isBeforeJellyBean()) {
             // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
             // a hardware keyboard event on pressing enter or delete. This is bad for many
             // reasons (there are race conditions with commits) but some applications are
@@ -1209,7 +1209,7 @@ public final class InputLogic {
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_promotePhantomSpace();
             }
-            sendKeyCodePoint(Constants.CODE_SPACE);
+            sendKeyCodePoint(settingsValues, Constants.CODE_SPACE);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 94f1458133..9bd2b93894 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -101,6 +101,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
     public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
     public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
 
+    private Context mContext;
     private Resources mRes;
     private SharedPreferences mPrefs;
     private SettingsValues mSettingsValues;
@@ -121,6 +122,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
     }
 
     private void onCreate(final Context context) {
+        mContext = context;
         mRes = context.getResources();
         mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
         mPrefs.registerOnSharedPreferenceChangeListener(this);
@@ -140,20 +142,22 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
                 Log.w(TAG, "onSharedPreferenceChanged called before loadSettings.");
                 return;
             }
-            loadSettings(mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
+            loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
         } finally {
             mSettingsValuesLock.unlock();
         }
     }
 
-    public void loadSettings(final Locale locale, final InputAttributes inputAttributes) {
+    public void loadSettings(final Context context, final Locale locale,
+            final InputAttributes inputAttributes) {
         mSettingsValuesLock.lock();
+        mContext = context;
         try {
             final SharedPreferences prefs = mPrefs;
             final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
                 @Override
                 protected SettingsValues job(final Resources res) {
-                    return new SettingsValues(prefs, locale, res, inputAttributes);
+                    return new SettingsValues(context, prefs, locale, res, inputAttributes);
                 }
             };
             mSettingsValues = job.runInLocale(mRes, locale);
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index d98e547fc1..f97e9e1fc8 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -16,13 +16,16 @@
 
 package com.android.inputmethod.latin.settings;
 
+import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.AppWorkaroundsUtils;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.Dictionary;
@@ -31,8 +34,10 @@ import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
+import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -48,6 +53,7 @@ public final class SettingsValues {
     // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings.
     private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
     private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
+    private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds
 
     // From resources:
     public final int mDelayUpdateOldSuggestions;
@@ -95,6 +101,7 @@ public final class SettingsValues {
     public final int mSuggestionVisibility;
     public final boolean mBoostPersonalizationDictionaryForDebug;
     public final boolean mUseOnlyPersonalizationDictionaryForDebug;
+    private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
 
     // Setting values for additional features
     public final int[] mAdditionalFeaturesSettingValues =
@@ -103,8 +110,8 @@ public final class SettingsValues {
     // Debug settings
     public final boolean mIsInternal;
 
-    public SettingsValues(final SharedPreferences prefs, final Locale locale, final Resources res,
-            final InputAttributes inputAttributes) {
+    public SettingsValues(final Context context, final SharedPreferences prefs, final Locale locale,
+            final Resources res, final InputAttributes inputAttributes) {
         mLocale = locale;
         // Get the resources
         mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions);
@@ -177,6 +184,15 @@ public final class SettingsValues {
                 Settings.readBoostPersonalizationDictionaryForDebug(prefs);
         mUseOnlyPersonalizationDictionaryForDebug =
                 Settings.readUseOnlyPersonalizationDictionaryForDebug(prefs);
+        mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>();
+        final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
+                mInputAttributes.mTargetApplicationPackageName);
+        if (null != packageInfo) {
+            mAppWorkarounds.set(new AppWorkaroundsUtils(packageInfo));
+        } else {
+            new TargetPackageInfoGetterTask(context, mAppWorkarounds)
+                    .execute(mInputAttributes.mTargetApplicationPackageName);
+        }
     }
 
     // Only for tests
@@ -225,6 +241,8 @@ public final class SettingsValues {
         mIsInternal = false;
         mBoostPersonalizationDictionaryForDebug = false;
         mUseOnlyPersonalizationDictionaryForDebug = false;
+        mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>();
+        mAppWorkarounds.set(null);
     }
 
     @UsedForTesting
@@ -288,6 +306,14 @@ public final class SettingsValues {
         return mInputAttributes.isSameInputType(editorInfo);
     }
 
+    public boolean isBeforeJellyBean() {
+        return mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE).isBeforeJellyBean();
+    }
+
+    public boolean isBrokenByRecorrection() {
+        return mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE).isBrokenByRecorrection();
+    }
+
     // Helper functions to create member values.
     private static SuggestedWords createSuggestPuncList(final String[] puncs) {
         final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList();
diff --git a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
index afbe2ecad9..42ea3c9594 100644
--- a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
+++ b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
@@ -22,6 +22,8 @@ import android.content.pm.PackageManager;
 import android.os.AsyncTask;
 import android.util.LruCache;
 
+import com.android.inputmethod.compat.AppWorkaroundsUtils;
+
 public final class TargetPackageInfoGetterTask extends
         AsyncTask<String, Void, PackageInfo> {
     private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
@@ -37,17 +39,13 @@ public final class TargetPackageInfoGetterTask extends
         sCache.remove(packageName);
     }
 
-    public interface OnTargetPackageInfoKnownListener {
-        public void onTargetPackageInfoKnown(final PackageInfo info);
-    }
-
     private Context mContext;
-    private final OnTargetPackageInfoKnownListener mListener;
+    private final AsyncResultHolder<AppWorkaroundsUtils> mResult;
 
     public TargetPackageInfoGetterTask(final Context context,
-            final OnTargetPackageInfoKnownListener listener) {
+            final AsyncResultHolder<AppWorkaroundsUtils> result) {
         mContext = context;
-        mListener = listener;
+        mResult = result;
     }
 
     @Override
@@ -65,6 +63,6 @@ public final class TargetPackageInfoGetterTask extends
 
     @Override
     protected void onPostExecute(final PackageInfo info) {
-        mListener.onTargetPackageInfoKnown(info);
+        mResult.set(new AppWorkaroundsUtils(info));
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java b/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java
new file mode 100644
index 0000000000..c29257d34c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.settings.Settings;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION_CODES;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.inputmethod.EditorInfo;
+
+@LargeTest
+public class AppWorkaroundsTests extends InputTestsBase {
+    String packageNameOfAppBeforeJellyBean;
+    String packageNameOfAppAfterJellyBean;
+
+    @Override
+    protected void setUp() throws Exception {
+        // NOTE: this will fail if there is no app installed that targets an SDK
+        // before Jelly Bean. For the moment, it's fine.
+        final PackageManager pm = getContext().getPackageManager();
+        for (ApplicationInfo ai : pm.getInstalledApplications(0 /* flags */)) {
+            if (ai.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
+                packageNameOfAppBeforeJellyBean = ai.packageName;
+            } else {
+                packageNameOfAppAfterJellyBean = ai.packageName;
+            }
+        }
+        super.setUp();
+    }
+
+    // We want to test if the app package info is correctly retrieved by LatinIME. Since it
+    // asks this information to the package manager from the package name, and that it takes
+    // the package name from the EditorInfo, all we have to do it put the correct package
+    // name in the editor info.
+    // To this end, our base class InputTestsBase offers a hook for us to touch the EditorInfo.
+    // We override this hook to write the package name that we need.
+    @Override
+    protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
+        if ("testBeforeJellyBeanTrue".equals(getName())) {
+            ei.packageName = packageNameOfAppBeforeJellyBean;
+        } else if ("testBeforeJellyBeanFalse".equals(getName())) {
+            ei.packageName = packageNameOfAppAfterJellyBean;
+        }
+        return ei;
+    }
+
+    public void testBeforeJellyBeanTrue() {
+        assertTrue("Couldn't successfully detect this app targets < Jelly Bean (package is "
+                + packageNameOfAppBeforeJellyBean + ")",
+                Settings.getInstance().getCurrent().isBeforeJellyBean());
+    }
+
+    public void testBeforeJellyBeanFalse() {
+        assertFalse("Couldn't successfully detect this app targets >= Jelly Bean (package is "
+                + packageNameOfAppAfterJellyBean + ")",
+                Settings.getInstance().getCurrent().isBeforeJellyBean());
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index aaad740e40..cee73c9170 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -162,6 +162,13 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
         return setBooleanPreference(PREF_DEBUG_MODE, value, false);
     }
 
+    protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
+        // Some tests that inherit from us need to add some data in the EditorInfo (see
+        // AppWorkaroundsTests#enrichEditorInfo() for a concrete example of this). Since we
+        // control the EditorInfo, we supply a hook here for children to override.
+        return ei;
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -176,12 +183,13 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
         mPreviousAutoCorrectSetting = setStringPreference(PREF_AUTO_CORRECTION_THRESHOLD,
                 DEFAULT_AUTO_CORRECTION_THRESHOLD, DEFAULT_AUTO_CORRECTION_THRESHOLD);
         mLatinIME.onCreate();
-        final EditorInfo ei = new EditorInfo();
+        EditorInfo ei = new EditorInfo();
         final InputConnection ic = mEditText.onCreateInputConnection(ei);
         final LayoutInflater inflater =
                 (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         final ViewGroup vg = new FrameLayout(getContext());
         mInputView = inflater.inflate(R.layout.input_view, vg);
+        ei = enrichEditorInfo(ei);
         mLatinIME.onCreateInputMethodInterface().startInput(ic, ei);
         mLatinIME.setInputView(mInputView);
         mLatinIME.onBindInput();
-- 
GitLab