Extract a new class for app-specific workarounds.

Bug: 8944307
Change-Id: Iaff9dd03d3d856a6bdaa2b16c4e71e95cd2b6349
diff --git a/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java b/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
new file mode 100644
index 0000000..5594ccb
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.compat;
+
+import android.content.pm.PackageInfo;
+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
+
+    public void setPackageInfo(final PackageInfo packageInfo) {
+        mPackageInfo = packageInfo;
+    }
+
+    public boolean isBeforeJellyBean() {
+        if (null == mPackageInfo || null == mPackageInfo.applicationInfo) {
+            return false;
+        }
+        return mPackageInfo.applicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN;
+    }
+
+    @Override
+    public String toString() {
+        if (null == mPackageInfo || null == mPackageInfo.applicationInfo) {
+            return "";
+        }
+        final StringBuilder s = new StringBuilder();
+        s.append("Target application : ")
+                .append(mPackageInfo.applicationInfo.name)
+                .append("\nPackage : ")
+                .append(mPackageInfo.applicationInfo.packageName)
+                .append("\nTarget app sdk version : ")
+                .append(mPackageInfo.applicationInfo.targetSdkVersion);
+        return s.toString();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
index 41fcb83..5609612 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -75,7 +75,7 @@
             final String packageName = packageUri.getSchemeSpecificPart();
             if (null == packageName) return;
             // TODO: do this in a more appropriate place
-            TargetApplicationGetter.removeApplicationInfoCache(packageName);
+            TargetPackageInfoGetterTask.removeCachedPackageInfo(packageName);
             final PackageInfo packageInfo;
             try {
                 packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 347a4c6..9349595 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,14 +28,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
-import android.os.Build.VERSION_CODES;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -64,6 +63,7 @@
 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.compat.SuggestionSpanUtils;
 import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
@@ -91,7 +91,7 @@
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService implements KeyboardActionListener,
-        SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
+        SuggestionStripView.Listener, TargetPackageInfoGetterTask.OnTargetPackageInfoKnownListener,
         Suggest.SuggestInitializationListener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean TRACE = false;
@@ -141,7 +141,7 @@
     private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
     @UsedForTesting Suggest mSuggest;
     private CompletionInfo[] mApplicationSpecifiedCompletions;
-    private ApplicationInfo mTargetApplicationInfo;
+    private AppWorkaroundsUtils mAppWorkAroundsUtils = new AppWorkaroundsUtils();
 
     private RichInputMethodManager mRichImm;
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@@ -711,10 +711,11 @@
             Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
         }
 
-        mTargetApplicationInfo =
-                TargetApplicationGetter.getCachedApplicationInfo(editorInfo.packageName);
-        if (null == mTargetApplicationInfo) {
-            new TargetApplicationGetter(this /* context */, this /* listener */)
+        final PackageInfo packageInfo =
+                TargetPackageInfoGetterTask.getCachedPackageInfo(editorInfo.packageName);
+        mAppWorkAroundsUtils.setPackageInfo(packageInfo);
+        if (null == packageInfo) {
+            new TargetPackageInfoGetterTask(this /* context */, this /* listener */)
                     .execute(editorInfo.packageName);
         }
 
@@ -819,10 +820,10 @@
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
 
-    // Callback for the TargetApplicationGetter
+    // Callback for the TargetPackageInfoGetterTask
     @Override
-    public void onTargetApplicationKnown(final ApplicationInfo info) {
-        mTargetApplicationInfo = info;
+    public void onTargetPackageInfoKnown(final PackageInfo info) {
+        mAppWorkAroundsUtils.setPackageInfo(info);
     }
 
     @Override
@@ -1369,8 +1370,7 @@
             return;
         }
 
-        if (Constants.CODE_ENTER == code && mTargetApplicationInfo != null
-                && mTargetApplicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
+        if (Constants.CODE_ENTER == code && mAppWorkAroundsUtils.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
@@ -1864,8 +1864,7 @@
                     // This should never happen.
                     Log.e(TAG, "Backspace when we don't know the selection position");
                 }
-                if (mTargetApplicationInfo != null
-                        && mTargetApplicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
+                if (mAppWorkAroundsUtils.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
@@ -2785,12 +2784,8 @@
     }
 
     public void debugDumpStateAndCrashWithException(final String context) {
-        final StringBuilder s = new StringBuilder();
-        s.append("Target application : ").append(mTargetApplicationInfo.name)
-                .append("\nPackage : ").append(mTargetApplicationInfo.packageName)
-                .append("\nTarget app sdk version : ")
-                .append(mTargetApplicationInfo.targetSdkVersion)
-                .append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
+        final StringBuilder s = new StringBuilder(mAppWorkAroundsUtils.toString());
+        s.append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
                 .append("\nContext : ").append(context);
         throw new RuntimeException(s.toString());
     }
diff --git a/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java b/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java
deleted file mode 100644
index 1ea4ac3..0000000
--- a/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.util.LruCache;
-
-public final class TargetApplicationGetter extends AsyncTask<String, Void, ApplicationInfo> {
-    private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
-    private static LruCache<String, ApplicationInfo> sCache =
-            new LruCache<String, ApplicationInfo>(MAX_CACHE_ENTRIES);
-
-    public static ApplicationInfo getCachedApplicationInfo(final String packageName) {
-        if (null == packageName) return null;
-        return sCache.get(packageName);
-    }
-
-    public static void removeApplicationInfoCache(final String packageName) {
-        sCache.remove(packageName);
-    }
-
-    public interface OnTargetApplicationKnownListener {
-        public void onTargetApplicationKnown(final ApplicationInfo info);
-    }
-
-    private Context mContext;
-    private final OnTargetApplicationKnownListener mListener;
-
-    public TargetApplicationGetter(final Context context,
-            final OnTargetApplicationKnownListener listener) {
-        mContext = context;
-        mListener = listener;
-    }
-
-    @Override
-    protected ApplicationInfo doInBackground(final String... packageName) {
-        final PackageManager pm = mContext.getPackageManager();
-        mContext = null; // Bazooka-powered anti-leak device
-        try {
-            final ApplicationInfo targetAppInfo =
-                    pm.getApplicationInfo(packageName[0], 0 /* flags */);
-            sCache.put(packageName[0], targetAppInfo);
-            return targetAppInfo;
-        } catch (android.content.pm.PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    @Override
-    protected void onPostExecute(final ApplicationInfo info) {
-        mListener.onTargetApplicationKnown(info);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/TargetPackageInfoGetterTask.java
new file mode 100644
index 0000000..947b0c5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/TargetPackageInfoGetterTask.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 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 android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.util.LruCache;
+
+public final class TargetPackageInfoGetterTask extends
+        AsyncTask<String, Void, PackageInfo> {
+    private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
+    private static final LruCache<String, PackageInfo> sCache =
+            new LruCache<String, PackageInfo>(MAX_CACHE_ENTRIES);
+
+    public static PackageInfo getCachedPackageInfo(final String packageName) {
+        if (null == packageName) return null;
+        return sCache.get(packageName);
+    }
+
+    public static void removeCachedPackageInfo(final String packageName) {
+        sCache.remove(packageName);
+    }
+
+    public interface OnTargetPackageInfoKnownListener {
+        public void onTargetPackageInfoKnown(final PackageInfo info);
+    }
+
+    private Context mContext;
+    private final OnTargetPackageInfoKnownListener mListener;
+
+    public TargetPackageInfoGetterTask(final Context context,
+            final OnTargetPackageInfoKnownListener listener) {
+        mContext = context;
+        mListener = listener;
+    }
+
+    @Override
+    protected PackageInfo doInBackground(final String... packageName) {
+        final PackageManager pm = mContext.getPackageManager();
+        mContext = null; // Bazooka-powered anti-leak device
+        try {
+            final PackageInfo packageInfo = pm.getPackageInfo(packageName[0], 0 /* flags */);
+            sCache.put(packageName[0], packageInfo);
+            return packageInfo;
+        } catch (android.content.pm.PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected void onPostExecute(final PackageInfo info) {
+        mListener.onTargetPackageInfoKnown(info);
+    }
+}