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