1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.keyboard; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.os.Build; 22 import android.os.Build.VERSION_CODES; 23 import android.preference.PreferenceManager; 24 import android.util.Log; 25 26 import com.android.inputmethod.compat.BuildCompatUtils; 27 import com.android.inputmethod.latin.R; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 32 public final class KeyboardTheme implements Comparable<KeyboardTheme> { 33 private static final String TAG = KeyboardTheme.class.getSimpleName(); 34 35 static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916"; 36 static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509"; 37 38 // These should be aligned with Keyboard.themeId and Keyboard.Case.keyboardTheme 39 // attributes' values in attrs.xml. 40 public static final int THEME_ID_ICS = 0; 41 public static final int THEME_ID_KLP = 2; 42 public static final int THEME_ID_LXX_LIGHT = 3; 43 public static final int THEME_ID_LXX_DARK = 4; 44 public static final int DEFAULT_THEME_ID = THEME_ID_KLP; 45 46 private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES; 47 48 /* package private for testing */ 49 static final KeyboardTheme[] KEYBOARD_THEMES = { 50 new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS, 51 // This has never been selected because we support ICS or later. 52 VERSION_CODES.BASE), 53 new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP, 54 // Default theme for ICS, JB, and KLP. 55 VERSION_CODES.ICE_CREAM_SANDWICH), 56 new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light, 57 // Default theme for LXX. 58 Build.VERSION_CODES.LOLLIPOP), 59 new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark, 60 // This has never been selected as default theme. 61 VERSION_CODES.BASE), 62 }; 63 64 static { 65 // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}. 66 Arrays.sort(KEYBOARD_THEMES); 67 } 68 69 public final int mThemeId; 70 public final int mStyleId; 71 public final String mThemeName; 72 public final int mMinApiVersion; 73 74 // Note: The themeId should be aligned with "themeId" attribute of Keyboard style 75 // in values/themes-<style>.xml. 76 private KeyboardTheme(final int themeId, final String themeName, final int styleId, 77 final int minApiVersion) { 78 mThemeId = themeId; 79 mThemeName = themeName; 80 mStyleId = styleId; 81 mMinApiVersion = minApiVersion; 82 } 83 84 @Override 85 public int compareTo(final KeyboardTheme rhs) { 86 if (mMinApiVersion > rhs.mMinApiVersion) return -1; 87 if (mMinApiVersion < rhs.mMinApiVersion) return 1; 88 return 0; 89 } 90 91 @Override 92 public boolean equals(final Object o) { 93 if (o == this) return true; 94 return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId; 95 } 96 97 @Override 98 public int hashCode() { 99 return mThemeId; 100 } 101 102 /* package private for testing */ 103 static KeyboardTheme searchKeyboardThemeById(final int themeId, 104 final KeyboardTheme[] availableThemeIds) { 105 // TODO: This search algorithm isn't optimal if there are many themes. 106 for (final KeyboardTheme theme : availableThemeIds) { 107 if (theme.mThemeId == themeId) { 108 return theme; 109 } 110 } 111 return null; 112 } 113 114 /* package private for testing */ 115 static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, 116 final int sdkVersion, final KeyboardTheme[] availableThemeArray) { 117 final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); 118 if (klpThemeIdString != null) { 119 if (sdkVersion <= VERSION_CODES.KITKAT) { 120 try { 121 final int themeId = Integer.parseInt(klpThemeIdString); 122 final KeyboardTheme theme = searchKeyboardThemeById(themeId, 123 availableThemeArray); 124 if (theme != null) { 125 return theme; 126 } 127 Log.w(TAG, "Unknown keyboard theme in KLP preference: " + klpThemeIdString); 128 } catch (final NumberFormatException e) { 129 Log.w(TAG, "Illegal keyboard theme in KLP preference: " + klpThemeIdString, e); 130 } 131 } 132 // Remove old preference. 133 Log.i(TAG, "Remove KLP keyboard theme preference: " + klpThemeIdString); 134 prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply(); 135 } 136 // TODO: This search algorithm isn't optimal if there are many themes. 137 for (final KeyboardTheme theme : availableThemeArray) { 138 if (sdkVersion >= theme.mMinApiVersion) { 139 return theme; 140 } 141 } 142 return searchKeyboardThemeById(DEFAULT_THEME_ID, availableThemeArray); 143 } 144 145 public static String getKeyboardThemeName(final int themeId) { 146 final KeyboardTheme theme = searchKeyboardThemeById(themeId, KEYBOARD_THEMES); 147 return theme.mThemeName; 148 } 149 150 public static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs) { 151 saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); 152 } 153 154 /* package private for testing */ 155 static String getPreferenceKey(final int sdkVersion) { 156 if (sdkVersion <= VERSION_CODES.KITKAT) { 157 return KLP_KEYBOARD_THEME_KEY; 158 } 159 return LXX_KEYBOARD_THEME_KEY; 160 } 161 162 /* package private for testing */ 163 static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs, 164 final int sdkVersion) { 165 final String prefKey = getPreferenceKey(sdkVersion); 166 prefs.edit().putString(prefKey, Integer.toString(themeId)).apply(); 167 } 168 169 public static KeyboardTheme getKeyboardTheme(final Context context) { 170 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 171 final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context); 172 return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray); 173 } 174 175 /* package private for testing */ 176 static KeyboardTheme[] getAvailableThemeArray(final Context context) { 177 if (AVAILABLE_KEYBOARD_THEMES == null) { 178 final int[] availableThemeIdStringArray = context.getResources().getIntArray( 179 R.array.keyboard_theme_ids); 180 final ArrayList<KeyboardTheme> availableThemeList = new ArrayList<>(); 181 for (final int id : availableThemeIdStringArray) { 182 final KeyboardTheme theme = searchKeyboardThemeById(id, KEYBOARD_THEMES); 183 if (theme != null) { 184 availableThemeList.add(theme); 185 } 186 } 187 AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray( 188 new KeyboardTheme[availableThemeList.size()]); 189 Arrays.sort(AVAILABLE_KEYBOARD_THEMES); 190 } 191 return AVAILABLE_KEYBOARD_THEMES; 192 } 193 194 /* package private for testing */ 195 static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion, 196 final KeyboardTheme[] availableThemeArray) { 197 final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); 198 if (lxxThemeIdString == null) { 199 return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); 200 } 201 try { 202 final int themeId = Integer.parseInt(lxxThemeIdString); 203 final KeyboardTheme theme = searchKeyboardThemeById(themeId, availableThemeArray); 204 if (theme != null) { 205 return theme; 206 } 207 Log.w(TAG, "Unknown keyboard theme in LXX preference: " + lxxThemeIdString); 208 } catch (final NumberFormatException e) { 209 Log.w(TAG, "Illegal keyboard theme in LXX preference: " + lxxThemeIdString, e); 210 } 211 // Remove preference that contains unknown or illegal theme id. 212 prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply(); 213 return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); 214 } 215 } 216