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.SharedPreferences;
20 import android.os.Build.VERSION_CODES;
21 import android.util.Log;
22 
23 import com.android.inputmethod.annotations.UsedForTesting;
24 import com.android.inputmethod.compat.BuildCompatUtils;
25 import com.android.inputmethod.latin.R;
26 
27 import java.util.Arrays;
28 
29 public final class KeyboardTheme implements Comparable<KeyboardTheme> {
30     private static final String TAG = KeyboardTheme.class.getSimpleName();
31 
32     static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916";
33     static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509";
34 
35     // These should be aligned with Keyboard.themeId and Keyboard.Case.keyboardTheme
36     // attributes' values in attrs.xml.
37     public static final int THEME_ID_ICS = 0;
38     public static final int THEME_ID_KLP = 2;
39     public static final int THEME_ID_LXX_LIGHT = 3;
40     public static final int THEME_ID_LXX_DARK = 4;
41     public static final int DEFAULT_THEME_ID = THEME_ID_KLP;
42 
43     private static final KeyboardTheme[] KEYBOARD_THEMES = {
44         new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS,
45                 // This has never been selected because we support ICS or later.
46                 VERSION_CODES.BASE),
47         new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP,
48                 // Default theme for ICS, JB, and KLP.
49                 VERSION_CODES.ICE_CREAM_SANDWICH),
50         new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light,
51                 // Default theme for LXX.
52                 BuildCompatUtils.VERSION_CODES_LXX),
53         new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark,
54                 VERSION_CODES.BASE),
55     };
56 
57     static {
58         // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}.
59         Arrays.sort(KEYBOARD_THEMES);
60     }
61 
62     public final int mThemeId;
63     public final int mStyleId;
64     public final String mThemeName;
65     private final int mMinApiVersion;
66 
67     // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
68     // in values/themes-<style>.xml.
KeyboardTheme(final int themeId, final String themeName, final int styleId, final int minApiVersion)69     private KeyboardTheme(final int themeId, final String themeName, final int styleId,
70             final int minApiVersion) {
71         mThemeId = themeId;
72         mThemeName = themeName;
73         mStyleId = styleId;
74         mMinApiVersion = minApiVersion;
75     }
76 
77     @Override
compareTo(final KeyboardTheme rhs)78     public int compareTo(final KeyboardTheme rhs) {
79         if (mMinApiVersion > rhs.mMinApiVersion) return -1;
80         if (mMinApiVersion < rhs.mMinApiVersion) return 1;
81         return 0;
82     }
83 
84     @Override
equals(final Object o)85     public boolean equals(final Object o) {
86         if (o == this) return true;
87         return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId;
88     }
89 
90     @Override
hashCode()91     public int hashCode() {
92         return mThemeId;
93     }
94 
95     @UsedForTesting
searchKeyboardThemeById(final int themeId)96     static KeyboardTheme searchKeyboardThemeById(final int themeId) {
97         // TODO: This search algorithm isn't optimal if there are many themes.
98         for (final KeyboardTheme theme : KEYBOARD_THEMES) {
99             if (theme.mThemeId == themeId) {
100                 return theme;
101             }
102         }
103         return null;
104     }
105 
106     @UsedForTesting
getDefaultKeyboardTheme(final SharedPreferences prefs, final int sdkVersion)107     static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs,
108             final int sdkVersion) {
109         final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null);
110         if (klpThemeIdString != null) {
111             if (sdkVersion <= VERSION_CODES.KITKAT) {
112                 try {
113                     final int themeId = Integer.parseInt(klpThemeIdString);
114                     final KeyboardTheme theme = searchKeyboardThemeById(themeId);
115                     if (theme != null) {
116                         return theme;
117                     }
118                     Log.w(TAG, "Unknown keyboard theme in KLP preference: " + klpThemeIdString);
119                 } catch (final NumberFormatException e) {
120                     Log.w(TAG, "Illegal keyboard theme in KLP preference: " + klpThemeIdString, e);
121                 }
122             }
123             // Remove old preference.
124             Log.i(TAG, "Remove KLP keyboard theme preference: " + klpThemeIdString);
125             prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply();
126         }
127         // TODO: This search algorithm isn't optimal if there are many themes.
128         for (final KeyboardTheme theme : KEYBOARD_THEMES) {
129             if (sdkVersion >= theme.mMinApiVersion) {
130                 return theme;
131             }
132         }
133         return searchKeyboardThemeById(DEFAULT_THEME_ID);
134     }
135 
getKeyboardThemeName(final int themeId)136     public static String getKeyboardThemeName(final int themeId) {
137         final KeyboardTheme theme = searchKeyboardThemeById(themeId);
138         return theme.mThemeName;
139     }
140 
saveKeyboardThemeId(final String themeIdString, final SharedPreferences prefs)141     public static void saveKeyboardThemeId(final String themeIdString,
142             final SharedPreferences prefs) {
143         saveKeyboardThemeId(themeIdString, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT);
144     }
145 
146     @UsedForTesting
getPreferenceKey(final int sdkVersion)147     static String getPreferenceKey(final int sdkVersion) {
148         if (sdkVersion <= VERSION_CODES.KITKAT) {
149             return KLP_KEYBOARD_THEME_KEY;
150         }
151         return LXX_KEYBOARD_THEME_KEY;
152     }
153 
154     @UsedForTesting
saveKeyboardThemeId(final String themeIdString, final SharedPreferences prefs, final int sdkVersion)155     static void saveKeyboardThemeId(final String themeIdString,
156             final SharedPreferences prefs, final int sdkVersion) {
157         final String prefKey = getPreferenceKey(sdkVersion);
158         prefs.edit().putString(prefKey, themeIdString).apply();
159     }
160 
getKeyboardTheme(final SharedPreferences prefs)161     public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) {
162         return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT);
163     }
164 
165     @UsedForTesting
getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion)166     static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) {
167         final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null);
168         if (lxxThemeIdString == null) {
169             return getDefaultKeyboardTheme(prefs, sdkVersion);
170         }
171         try {
172             final int themeId = Integer.parseInt(lxxThemeIdString);
173             final KeyboardTheme theme = searchKeyboardThemeById(themeId);
174             if (theme != null) {
175                 return theme;
176             }
177             Log.w(TAG, "Unknown keyboard theme in LXX preference: " + lxxThemeIdString);
178         } catch (final NumberFormatException e) {
179             Log.w(TAG, "Illegal keyboard theme in LXX preference: " + lxxThemeIdString, e);
180         }
181         // Remove preference that contains unknown or illegal theme id.
182         prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply();
183         return getDefaultKeyboardTheme(prefs, sdkVersion);
184     }
185 }
186