Refactor KeyboardTheme

This CL introduces two preference keys in addition to the existing one.

- pref_keyboard_theme: The new preference key that is used only in the
  preference settings.

- pref_keyboard_layout_20110916: The previous preference key that
  holds keyboard color scheme settings. This is superseded by the next
  key.

- pref_keyboard_theme_20140509: The new preference key that holds
  keyboard theme settings. This value is forcibly reset to LMP theme
  on a LMP device. On a KitKat and previous device, the settings of
  the pref_keyboard_layout_20110916 is carried on into this
  preference.

This CL must be checked in together with Id7b3ddd8aa.

Change-Id: I2d55602d026e30b38d8f822d2124f5c5e4a9a812
diff --git a/java/res/values/config-common.xml b/java/res/values/config-common.xml
index 3fe4b94..1962c0d 100644
--- a/java/res/values/config-common.xml
+++ b/java/res/values/config-common.xml
@@ -24,9 +24,6 @@
          at input history to suggest a hopefully helpful suggestions for the next word? -->
     <bool name="config_default_next_word_prediction">true</bool>
 
-    <!-- This configuration must be aligned with {@link KeyboardTheme#DEFAULT_THEME_ID}. -->
-    <string name="config_default_keyboard_theme_id" translatable="false">2</string>
-
     <integer name="config_delay_update_shift_state">100</integer>
     <integer name="config_double_space_period_timeout">1100</integer>
 
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 9a610a0..415dd0b 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -47,17 +47,6 @@
     <string name="prefs_debug_mode">Debug Mode</string>
     <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string>
 
-    <!-- For keyboard color scheme option dialog. -->
-    <string-array name="keyboard_theme_names">
-        <item>@string/keyboard_color_scheme_white</item>
-        <item>@string/keyboard_color_scheme_blue</item>
-    </string-array>
-    <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. -->
-    <string-array name="keyboard_theme_ids">
-        <item>2</item>
-        <item>0</item>
-    </string-array>
-
     <!-- Subtype locale display name exceptions.
          For each exception, there should be related string resources for display name that may have
          explicit keyboard layout. The string resource name must be "subtype_<locale>" or
diff --git a/java/res/values/keyboard-themes.xml b/java/res/values/keyboard-themes.xml
new file mode 100644
index 0000000..0325f5b
--- /dev/null
+++ b/java/res/values/keyboard-themes.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- For keyboard color scheme option dialog. -->
+    <string-array name="keyboard_theme_names" translatable="false">
+        <item>@string/keyboard_color_scheme_white</item>
+        <item>@string/keyboard_color_scheme_blue</item>
+        <!-- TODO: Make this item as translatable string resource. -->
+        <item>Quantum</item>
+    </string-array>
+    <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. -->
+    <string-array name="keyboard_theme_ids" translatable="false">
+        <item>2</item>
+        <item>0</item>
+        <item>3</item>
+    </string-array>
+</resources>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 7d86dbd..61ebb69 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -158,12 +158,11 @@
                 android:persistent="true"
                 android:defaultValue="false" />
             <ListPreference
-                android:key="pref_keyboard_layout_20110916"
+                android:key="pref_keyboard_theme"
                 android:title="@string/keyboard_color_scheme"
                 android:persistent="true"
                 android:entryValues="@array/keyboard_theme_ids"
-                android:entries="@array/keyboard_theme_names"
-                android:defaultValue="@string/config_default_keyboard_theme_id" />
+                android:entries="@array/keyboard_theme_names" />
             <PreferenceScreen
                 android:fragment="com.android.inputmethod.latin.settings.AdditionalSubtypeSettings"
                 android:key="custom_input_styles"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 1cd6ef2..4a46a4a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -64,7 +64,7 @@
      * what user actually typed. */
     private boolean mIsAutoCorrectionActive;
 
-    private KeyboardTheme mKeyboardTheme = KeyboardTheme.getDefaultKeyboardTheme();
+    private KeyboardTheme mKeyboardTheme;
     private Context mThemeContext;
 
     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
@@ -101,7 +101,7 @@
 
     private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
             final KeyboardTheme keyboardTheme) {
-        if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
+        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
             mKeyboardTheme = keyboardTheme;
             mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
             KeyboardLayoutSet.clearKeyboardCache();
@@ -342,7 +342,8 @@
             mKeyboardView.closing();
         }
 
-        updateKeyboardThemeAndContextThemeWrapper(mLatinIME, mKeyboardTheme);
+        updateKeyboardThemeAndContextThemeWrapper(
+                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
         mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                 R.layout.input_view, null);
         mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
index 4db72ad..5034540 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
@@ -17,34 +17,86 @@
 package com.android.inputmethod.keyboard;
 
 import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.util.Log;
 
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.settings.Settings;
+
+import java.util.Arrays;
+import java.util.Comparator;
 
 public final class KeyboardTheme {
     private static final String TAG = KeyboardTheme.class.getSimpleName();
 
-    public static final int THEME_ID_ICS = 0;
-    public static final int THEME_ID_KLP = 2;
-    private static final int DEFAULT_THEME_ID = THEME_ID_KLP;
+    static final String KITKAT_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916";
+    static final String KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509";
+
+    static final int THEME_ID_ICS = 0;
+    static final int THEME_ID_KLP = 2;
+    static final int THEME_ID_LMP = 3;
+    static final int DEFAULT_THEME_ID = THEME_ID_KLP;
 
     private static final KeyboardTheme[] KEYBOARD_THEMES = {
-        new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS),
-        new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP),
+        new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS,
+                VERSION_CODES.ICE_CREAM_SANDWICH),
+        new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP,
+                VERSION_CODES.KITKAT),
+        // TODO: Update to LMP style.
+        new KeyboardTheme(THEME_ID_LMP, R.style.KeyboardTheme_KLP,
+                // TODO: Update this constant once the *next* version becomes available.
+                VERSION_CODES.CUR_DEVELOPMENT),
     };
+    static {
+        // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}.
+        Arrays.sort(KEYBOARD_THEMES, new Comparator<KeyboardTheme>() {
+            @Override
+            public int compare(final KeyboardTheme lhs, final KeyboardTheme rhs) {
+                if (lhs.mMinApiVersion > rhs.mMinApiVersion) return -1;
+                if (lhs.mMinApiVersion < rhs.mMinApiVersion) return 1;
+                return 0;
+            }
+        });
+    }
 
     public final int mThemeId;
     public final int mStyleId;
+    final int mMinApiVersion;
 
     // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
-    // in values/style.xml.
-    public KeyboardTheme(final int themeId, final int styleId) {
+    // in values/themes-<style>.xml.
+    private KeyboardTheme(final int themeId, final int styleId, final int minApiVersion) {
         mThemeId = themeId;
         mStyleId = styleId;
+        mMinApiVersion = minApiVersion;
     }
 
-    private static KeyboardTheme searchKeyboardTheme(final int themeId) {
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) return true;
+        return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId;
+    }
+
+    @Override
+    public int hashCode() {
+        return mThemeId;
+    }
+
+    // TODO: This method should be removed when {@link LatinImeLogger} is removed.
+    public int getCompatibleThemeIdForLogging() {
+        switch (mThemeId) {
+        case THEME_ID_ICS:
+            return 5;
+        case THEME_ID_KLP:
+            return 9;
+        case THEME_ID_LMP:
+            return 10;
+        default: // Invalid theme
+            return -1;
+        }
+    }
+
+    private static KeyboardTheme searchKeyboardThemeById(final int themeId) {
         // TODO: This search algorithm isn't optimal if there are many themes.
         for (final KeyboardTheme theme : KEYBOARD_THEMES) {
             if (theme.mThemeId == themeId) {
@@ -54,18 +106,57 @@
         return null;
     }
 
-    public static KeyboardTheme getDefaultKeyboardTheme() {
-        return searchKeyboardTheme(DEFAULT_THEME_ID);
+    private static int getSdkVersion() {
+        final int sdkVersion = Build.VERSION.SDK_INT;
+        // TODO: Consider to remove this check once the *next* version becomes available.
+        if (sdkVersion == VERSION_CODES.KITKAT && Build.VERSION.CODENAME.startsWith("L")) {
+            return VERSION_CODES.CUR_DEVELOPMENT;
+        }
+        return sdkVersion;
+    }
+
+    static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs,
+            final int sdkVersion) {
+        final String obsoleteIdString = prefs.getString(KITKAT_KEYBOARD_THEME_KEY, null);
+        if (obsoleteIdString != null) {
+            // Remove old preference.
+            prefs.edit().remove(KITKAT_KEYBOARD_THEME_KEY).apply();
+            if (sdkVersion <= VERSION_CODES.KITKAT) {
+                try {
+                    final int themeId = Integer.parseInt(obsoleteIdString);
+                    final KeyboardTheme theme = searchKeyboardThemeById(themeId);
+                    if (theme != null) {
+                        return theme;
+                    }
+                    Log.w(TAG, "Unknown keyboard theme in preference: " + obsoleteIdString);
+                } catch (final NumberFormatException e) {
+                    Log.w(TAG, "Illegal keyboard theme in preference: " + obsoleteIdString);
+                }
+            }
+        }
+        // TODO: This search algorithm isn't optimal if there are many themes.
+        for (final KeyboardTheme theme : KEYBOARD_THEMES) {
+            if (sdkVersion >= theme.mMinApiVersion) {
+                return theme;
+            }
+        }
+        return searchKeyboardThemeById(DEFAULT_THEME_ID);
+    }
+
+    public static void saveKeyboardThemeId(final String themeIdString,
+            final SharedPreferences prefs) {
+        prefs.edit().putString(KEYBOARD_THEME_KEY, themeIdString).apply();
     }
 
     public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) {
-        final String themeIdString = prefs.getString(Settings.PREF_KEYBOARD_LAYOUT, null);
+        final int sdkVersion = getSdkVersion();
+        final String themeIdString = prefs.getString(KEYBOARD_THEME_KEY, null);
         if (themeIdString == null) {
-            return getDefaultKeyboardTheme();
+            return getDefaultKeyboardTheme(prefs, sdkVersion);
         }
         try {
             final int themeId = Integer.parseInt(themeIdString);
-            final KeyboardTheme theme = searchKeyboardTheme(themeId);
+            final KeyboardTheme theme = searchKeyboardThemeById(themeId);
             if (theme != null) {
                 return theme;
             }
@@ -73,9 +164,8 @@
         } catch (final NumberFormatException e) {
             Log.w(TAG, "Illegal keyboard theme in preference: " + themeIdString);
         }
-        // Reset preference to default value.
-        final String defaultThemeIdString = Integer.toString(DEFAULT_THEME_ID);
-        prefs.edit().putString(Settings.PREF_KEYBOARD_LAYOUT, defaultThemeIdString).apply();
-        return getDefaultKeyboardTheme();
+        // Remove preference.
+        prefs.edit().remove(KEYBOARD_THEME_KEY).apply();
+        return getDefaultKeyboardTheme(prefs, sdkVersion);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index a3aae8c..4e4c888 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -64,7 +64,7 @@
             "pref_show_language_switch_key";
     public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
             "pref_include_other_imes_in_language_switch_list";
-    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
+    public static final String PREF_KEYBOARD_THEME = "pref_keyboard_theme";
     public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
     // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
     public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 22cbd20..e1d38e7 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -37,6 +37,7 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.dictionarypack.DictionarySettingsActivity;
+import com.android.inputmethod.keyboard.KeyboardTheme;
 import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -253,11 +254,31 @@
         }
         updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
         updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
-        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT);
+        final ListPreference keyboardThemePref = (ListPreference)findPreference(
+                Settings.PREF_KEYBOARD_THEME);
+        if (keyboardThemePref != null) {
+            final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs);
+            final String value = Integer.toString(keyboardTheme.mThemeId);
+            final CharSequence entries[] = keyboardThemePref.getEntries();
+            final int entryIndex = keyboardThemePref.findIndexOfValue(value);
+            keyboardThemePref.setSummary(entryIndex < 0 ? null : entries[entryIndex]);
+            keyboardThemePref.setValue(value);
+        }
         updateCustomInputStylesSummary(prefs, res);
     }
 
     @Override
+    public void onPause() {
+        super.onPause();
+        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+        final ListPreference keyboardThemePref = (ListPreference)findPreference(
+                Settings.PREF_KEYBOARD_THEME);
+        if (keyboardThemePref != null) {
+            KeyboardTheme.saveKeyboardThemeId(keyboardThemePref.getValue(), prefs);
+        }
+    }
+
+    @Override
     public void onDestroy() {
         getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
                 this);
@@ -287,7 +308,7 @@
         ensureConsistencyOfAutoCorrectionSettings();
         updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
         updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
-        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT);
+        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME);
         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
     }