1 /*
2  * Copyright (C) 2010 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 static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
20 
21 import android.text.InputType;
22 import android.text.TextUtils;
23 import android.view.inputmethod.EditorInfo;
24 import android.view.inputmethod.InputMethodSubtype;
25 
26 import com.android.inputmethod.compat.EditorInfoCompatUtils;
27 import com.android.inputmethod.latin.utils.InputTypeUtils;
28 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
29 
30 import java.util.Arrays;
31 import java.util.Locale;
32 
33 /**
34  * Unique identifier for each keyboard type.
35  */
36 public final class KeyboardId {
37     public static final int MODE_TEXT = 0;
38     public static final int MODE_URL = 1;
39     public static final int MODE_EMAIL = 2;
40     public static final int MODE_IM = 3;
41     public static final int MODE_PHONE = 4;
42     public static final int MODE_NUMBER = 5;
43     public static final int MODE_DATE = 6;
44     public static final int MODE_TIME = 7;
45     public static final int MODE_DATETIME = 8;
46 
47     public static final int ELEMENT_ALPHABET = 0;
48     public static final int ELEMENT_ALPHABET_MANUAL_SHIFTED = 1;
49     public static final int ELEMENT_ALPHABET_AUTOMATIC_SHIFTED = 2;
50     public static final int ELEMENT_ALPHABET_SHIFT_LOCKED = 3;
51     public static final int ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED = 4;
52     public static final int ELEMENT_SYMBOLS = 5;
53     public static final int ELEMENT_SYMBOLS_SHIFTED = 6;
54     public static final int ELEMENT_PHONE = 7;
55     public static final int ELEMENT_PHONE_SYMBOLS = 8;
56     public static final int ELEMENT_NUMBER = 9;
57     public static final int ELEMENT_EMOJI_RECENTS = 10;
58     public static final int ELEMENT_EMOJI_CATEGORY1 = 11;
59     public static final int ELEMENT_EMOJI_CATEGORY2 = 12;
60     public static final int ELEMENT_EMOJI_CATEGORY3 = 13;
61     public static final int ELEMENT_EMOJI_CATEGORY4 = 14;
62     public static final int ELEMENT_EMOJI_CATEGORY5 = 15;
63     public static final int ELEMENT_EMOJI_CATEGORY6 = 16;
64 
65     public final InputMethodSubtype mSubtype;
66     public final Locale mLocale;
67     public final int mWidth;
68     public final int mHeight;
69     public final int mMode;
70     public final int mElementId;
71     public final EditorInfo mEditorInfo;
72     public final boolean mClobberSettingsKey;
73     public final boolean mLanguageSwitchKeyEnabled;
74     public final String mCustomActionLabel;
75     public final boolean mHasShortcutKey;
76 
77     private final int mHashCode;
78 
KeyboardId(final int elementId, final KeyboardLayoutSet.Params params)79     public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
80         mSubtype = params.mSubtype;
81         mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype);
82         mWidth = params.mKeyboardWidth;
83         mHeight = params.mKeyboardHeight;
84         mMode = params.mMode;
85         mElementId = elementId;
86         mEditorInfo = params.mEditorInfo;
87         mClobberSettingsKey = params.mNoSettingsKey;
88         mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled;
89         mCustomActionLabel = (mEditorInfo.actionLabel != null)
90                 ? mEditorInfo.actionLabel.toString() : null;
91         mHasShortcutKey = params.mVoiceInputKeyEnabled;
92 
93         mHashCode = computeHashCode(this);
94     }
95 
computeHashCode(final KeyboardId id)96     private static int computeHashCode(final KeyboardId id) {
97         return Arrays.hashCode(new Object[] {
98                 id.mElementId,
99                 id.mMode,
100                 id.mWidth,
101                 id.mHeight,
102                 id.passwordInput(),
103                 id.mClobberSettingsKey,
104                 id.mHasShortcutKey,
105                 id.mLanguageSwitchKeyEnabled,
106                 id.isMultiLine(),
107                 id.imeAction(),
108                 id.mCustomActionLabel,
109                 id.navigateNext(),
110                 id.navigatePrevious(),
111                 id.mSubtype
112         });
113     }
114 
equals(final KeyboardId other)115     private boolean equals(final KeyboardId other) {
116         if (other == this)
117             return true;
118         return other.mElementId == mElementId
119                 && other.mMode == mMode
120                 && other.mWidth == mWidth
121                 && other.mHeight == mHeight
122                 && other.passwordInput() == passwordInput()
123                 && other.mClobberSettingsKey == mClobberSettingsKey
124                 && other.mHasShortcutKey == mHasShortcutKey
125                 && other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled
126                 && other.isMultiLine() == isMultiLine()
127                 && other.imeAction() == imeAction()
128                 && TextUtils.equals(other.mCustomActionLabel, mCustomActionLabel)
129                 && other.navigateNext() == navigateNext()
130                 && other.navigatePrevious() == navigatePrevious()
131                 && other.mSubtype.equals(mSubtype);
132     }
133 
isAlphabetKeyboard(final int elementId)134     private static boolean isAlphabetKeyboard(final int elementId) {
135         return elementId < ELEMENT_SYMBOLS;
136     }
137 
isAlphabetKeyboard()138     public boolean isAlphabetKeyboard() {
139         return isAlphabetKeyboard(mElementId);
140     }
141 
navigateNext()142     public boolean navigateNext() {
143         return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0
144                 || imeAction() == EditorInfo.IME_ACTION_NEXT;
145     }
146 
navigatePrevious()147     public boolean navigatePrevious() {
148         return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0
149                 || imeAction() == EditorInfo.IME_ACTION_PREVIOUS;
150     }
151 
passwordInput()152     public boolean passwordInput() {
153         final int inputType = mEditorInfo.inputType;
154         return InputTypeUtils.isPasswordInputType(inputType)
155                 || InputTypeUtils.isVisiblePasswordInputType(inputType);
156     }
157 
isMultiLine()158     public boolean isMultiLine() {
159         return (mEditorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
160     }
161 
imeAction()162     public int imeAction() {
163         return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo);
164     }
165 
166     @Override
equals(final Object other)167     public boolean equals(final Object other) {
168         return other instanceof KeyboardId && equals((KeyboardId) other);
169     }
170 
171     @Override
hashCode()172     public int hashCode() {
173         return mHashCode;
174     }
175 
176     @Override
toString()177     public String toString() {
178         return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s]",
179                 elementIdToName(mElementId),
180                 mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
181                 mWidth, mHeight,
182                 modeName(mMode),
183                 actionName(imeAction()),
184                 (navigateNext() ? " navigateNext" : ""),
185                 (navigatePrevious() ? " navigatePrevious" : ""),
186                 (mClobberSettingsKey ? " clobberSettingsKey" : ""),
187                 (passwordInput() ? " passwordInput" : ""),
188                 (mHasShortcutKey ? " hasShortcutKey" : ""),
189                 (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""),
190                 (isMultiLine() ? " isMultiLine" : "")
191         );
192     }
193 
equivalentEditorInfoForKeyboard(final EditorInfo a, final EditorInfo b)194     public static boolean equivalentEditorInfoForKeyboard(final EditorInfo a, final EditorInfo b) {
195         if (a == null && b == null) return true;
196         if (a == null || b == null) return false;
197         return a.inputType == b.inputType
198                 && a.imeOptions == b.imeOptions
199                 && TextUtils.equals(a.privateImeOptions, b.privateImeOptions);
200     }
201 
elementIdToName(final int elementId)202     public static String elementIdToName(final int elementId) {
203         switch (elementId) {
204         case ELEMENT_ALPHABET: return "alphabet";
205         case ELEMENT_ALPHABET_MANUAL_SHIFTED: return "alphabetManualShifted";
206         case ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: return "alphabetAutomaticShifted";
207         case ELEMENT_ALPHABET_SHIFT_LOCKED: return "alphabetShiftLocked";
208         case ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: return "alphabetShiftLockShifted";
209         case ELEMENT_SYMBOLS: return "symbols";
210         case ELEMENT_SYMBOLS_SHIFTED: return "symbolsShifted";
211         case ELEMENT_PHONE: return "phone";
212         case ELEMENT_PHONE_SYMBOLS: return "phoneSymbols";
213         case ELEMENT_NUMBER: return "number";
214         case ELEMENT_EMOJI_RECENTS: return "emojiRecents";
215         case ELEMENT_EMOJI_CATEGORY1: return "emojiCategory1";
216         case ELEMENT_EMOJI_CATEGORY2: return "emojiCategory2";
217         case ELEMENT_EMOJI_CATEGORY3: return "emojiCategory3";
218         case ELEMENT_EMOJI_CATEGORY4: return "emojiCategory4";
219         case ELEMENT_EMOJI_CATEGORY5: return "emojiCategory5";
220         case ELEMENT_EMOJI_CATEGORY6: return "emojiCategory6";
221         default: return null;
222         }
223     }
224 
modeName(final int mode)225     public static String modeName(final int mode) {
226         switch (mode) {
227         case MODE_TEXT: return "text";
228         case MODE_URL: return "url";
229         case MODE_EMAIL: return "email";
230         case MODE_IM: return "im";
231         case MODE_PHONE: return "phone";
232         case MODE_NUMBER: return "number";
233         case MODE_DATE: return "date";
234         case MODE_TIME: return "time";
235         case MODE_DATETIME: return "datetime";
236         default: return null;
237         }
238     }
239 
actionName(final int actionId)240     public static String actionName(final int actionId) {
241         return (actionId == InputTypeUtils.IME_ACTION_CUSTOM_LABEL) ? "actionCustomLabel"
242                 : EditorInfoCompatUtils.imeActionName(actionId);
243     }
244 }
245