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.text.InputType;
20 import android.view.inputmethod.EditorInfo;
21 import android.view.inputmethod.InputMethodSubtype;
22 
23 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
24 import com.android.inputmethod.keyboard.internal.MoreKeySpec;
25 import com.android.inputmethod.latin.R;
26 import com.android.inputmethod.latin.RichInputMethodManager;
27 import com.android.inputmethod.latin.common.Constants;
28 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
29 
30 import java.util.Arrays;
31 import java.util.Locale;
32 
33 abstract class KeyboardLayoutSetNavigateMoreKeysBase extends KeyboardLayoutSetTestsBase {
34     private ExpectedMoreKey mExpectedNavigateNextMoreKey;
35     private ExpectedMoreKey mExpectedNavigatePreviousMoreKey;
36     private ExpectedMoreKey mExpectedEmojiMoreKey;
37 
getExpectedNavigateNextMoreKey()38     protected ExpectedMoreKey getExpectedNavigateNextMoreKey() {
39         return new ExpectedMoreKey(R.string.label_next_key);
40     }
41 
getExpectedNavigatePreviousMoreKey()42     protected ExpectedMoreKey getExpectedNavigatePreviousMoreKey() {
43         return new ExpectedMoreKey(R.string.label_previous_key);
44     }
45 
getExpectedEmojiMoreKey()46     protected ExpectedMoreKey getExpectedEmojiMoreKey() {
47         return new ExpectedMoreKey(KeyboardIconsSet.NAME_EMOJI_ACTION_KEY);
48     }
49 
50     @Override
setUp()51     protected void setUp() throws Exception {
52         super.setUp();
53         mExpectedNavigateNextMoreKey = getExpectedNavigateNextMoreKey();
54         mExpectedNavigatePreviousMoreKey =  getExpectedNavigatePreviousMoreKey();
55         mExpectedEmojiMoreKey = getExpectedEmojiMoreKey();
56     }
57 
58     /**
59      * This class represents an expected more key.
60      */
61     protected static class ExpectedMoreKey {
62         public static final int NO_LABEL = 0;
63         public static final ExpectedMoreKey[] EMPTY_MORE_KEYS = new ExpectedMoreKey[0];
64 
65         public final int mLabelResId;
66         public final int mIconId;
67 
ExpectedMoreKey(final String iconName)68         public ExpectedMoreKey(final String iconName) {
69             mLabelResId = NO_LABEL;
70             mIconId = KeyboardIconsSet.getIconId(iconName);
71         }
72 
ExpectedMoreKey(final int labelResId)73         public ExpectedMoreKey(final int labelResId) {
74             mLabelResId = labelResId;
75             mIconId = KeyboardIconsSet.ICON_UNDEFINED;
76         }
77     }
78 
doTestMoreKeysOf(final int code, final InputMethodSubtype subtype, final int elementId, final int inputType, final int imeOptions, final ExpectedMoreKey ... expectedMoreKeys)79     private void doTestMoreKeysOf(final int code, final InputMethodSubtype subtype,
80             final int elementId, final int inputType, final int imeOptions,
81             final ExpectedMoreKey ... expectedMoreKeys) {
82         final EditorInfo editorInfo = new EditorInfo();
83         editorInfo.inputType = inputType;
84         editorInfo.imeOptions = imeOptions;
85         final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
86         final Keyboard keyboard = layoutSet.getKeyboard(elementId);
87 
88         final Key actualKey = keyboard.getKey(code);
89         final MoreKeySpec[] actualMoreKeys = actualKey.getMoreKeys();
90         final String tag = actualKey.toString() + " moreKeys=" + Arrays.toString(actualMoreKeys);
91         if (expectedMoreKeys.length == 0) {
92             assertEquals(tag, null, actualMoreKeys);
93             return;
94         }
95         if (expectedMoreKeys.length == 1) {
96             assertEquals(tag + " fixedOrder", false, actualKey.isMoreKeysFixedOrder());
97             assertEquals(tag + " fixedColumn", false, actualKey.isMoreKeysFixedColumn());
98         } else {
99             assertEquals(tag + " fixedOrder", true, actualKey.isMoreKeysFixedOrder());
100             assertEquals(tag + " fixedColumn", true, actualKey.isMoreKeysFixedColumn());
101             // TODO: Can't handle multiple rows of more keys.
102             assertEquals(tag + " column",
103                     expectedMoreKeys.length, actualKey.getMoreKeysColumnNumber());
104         }
105         assertNotNull(tag + " moreKeys", actualMoreKeys);
106         assertEquals(tag, expectedMoreKeys.length, actualMoreKeys.length);
107         for (int index = 0; index < actualMoreKeys.length; index++) {
108             final int expectedLabelResId = expectedMoreKeys[index].mLabelResId;
109             if (expectedLabelResId == ExpectedMoreKey.NO_LABEL) {
110                 assertEquals(tag + " label " + index, null, actualMoreKeys[index].mLabel);
111             } else {
112                 final CharSequence expectedLabel = getContext().getText(expectedLabelResId);
113                 assertEquals(tag + " label " + index, expectedLabel, actualMoreKeys[index].mLabel);
114             }
115             final int expectedIconId = expectedMoreKeys[index].mIconId;
116             assertEquals(tag + " icon " + index, expectedIconId, actualMoreKeys[index].mIconId);
117         }
118     }
119 
doTestNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype, final int elementId, final int inputType)120     private void doTestNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype,
121             final int elementId, final int inputType) {
122         // No navigate flag.
123         doTestMoreKeysOf(code, subtype, elementId, inputType,
124                 EditorInfo.IME_NULL,
125                 ExpectedMoreKey.EMPTY_MORE_KEYS);
126         // With next navigate flag.
127         doTestMoreKeysOf(code, subtype, elementId, inputType,
128                 EditorInfo.IME_FLAG_NAVIGATE_NEXT,
129                 mExpectedNavigateNextMoreKey);
130         // With previous navigate flag.
131         doTestMoreKeysOf(code, subtype, elementId, inputType,
132                 EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
133                 mExpectedNavigatePreviousMoreKey);
134         // With next and previous naviagte flags.
135         doTestMoreKeysOf(code, subtype, elementId, inputType,
136                 EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
137                 mExpectedNavigatePreviousMoreKey, mExpectedNavigateNextMoreKey);
138         // Action next.
139         doTestMoreKeysOf(code, subtype, elementId, inputType,
140                 EditorInfo.IME_ACTION_NEXT,
141                 ExpectedMoreKey.EMPTY_MORE_KEYS);
142         // Action next with next navigate flag.
143         doTestMoreKeysOf(code, subtype, elementId, inputType,
144                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
145                 ExpectedMoreKey.EMPTY_MORE_KEYS);
146         // Action next with previous navigate flag.
147         doTestMoreKeysOf(code, subtype, elementId, inputType,
148                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
149                 mExpectedNavigatePreviousMoreKey);
150         // Action next with next and previous navigate flags.
151         doTestMoreKeysOf(code, subtype, elementId, inputType,
152                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT
153                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
154                 mExpectedNavigatePreviousMoreKey);
155         // Action previous.
156         doTestMoreKeysOf(code, subtype, elementId, inputType,
157                 EditorInfo.IME_ACTION_PREVIOUS,
158                 ExpectedMoreKey.EMPTY_MORE_KEYS);
159         // Action previous with next navigate flag.
160         doTestMoreKeysOf(code, subtype, elementId, inputType,
161                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
162                 mExpectedNavigateNextMoreKey);
163         // Action previous with previous navigate flag.
164         doTestMoreKeysOf(code, subtype, elementId, inputType,
165                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
166                 ExpectedMoreKey.EMPTY_MORE_KEYS);
167         // Action previous with next and previous navigate flags.
168         doTestMoreKeysOf(code, subtype, elementId, inputType,
169                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT
170                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
171                 mExpectedNavigateNextMoreKey);
172     }
173 
doTestNavigationWithEmojiMoreKeysOf(final int code, final InputMethodSubtype subtype, final int elementId, final int inputType)174     private void doTestNavigationWithEmojiMoreKeysOf(final int code,
175             final InputMethodSubtype subtype, final int elementId, final int inputType) {
176         // No navigate flag.
177         doTestMoreKeysOf(code, subtype, elementId, inputType,
178                 EditorInfo.IME_NULL,
179                 mExpectedEmojiMoreKey);
180         // With next navigate flag.
181         doTestMoreKeysOf(code, subtype, elementId, inputType,
182                 EditorInfo.IME_FLAG_NAVIGATE_NEXT,
183                 mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey);
184         // With previous navigate flag.
185         doTestMoreKeysOf(code, subtype, elementId, inputType,
186                 EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
187                 mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey);
188         // With next and previous naviagte flags.
189         doTestMoreKeysOf(code, subtype, elementId, inputType,
190                 EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
191                 mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey,
192                 mExpectedNavigateNextMoreKey);
193         // Action next.
194         doTestMoreKeysOf(code, subtype, elementId, inputType,
195                 EditorInfo.IME_ACTION_NEXT,
196                 mExpectedEmojiMoreKey);
197         // Action next with next navigate flag.
198         doTestMoreKeysOf(code, subtype, elementId, inputType,
199                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
200                 mExpectedEmojiMoreKey);
201         // Action next with previous navigate flag.
202         doTestMoreKeysOf(code, subtype, elementId, inputType,
203                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
204                 mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey);
205         // Action next with next and previous navigate flags.
206         doTestMoreKeysOf(code, subtype, elementId, inputType,
207                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT
208                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
209                 mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey);
210         // Action previous.
211         doTestMoreKeysOf(code, subtype, elementId, inputType,
212                 EditorInfo.IME_ACTION_PREVIOUS,
213                 mExpectedEmojiMoreKey);
214         // Action previous with next navigate flag.
215         doTestMoreKeysOf(code, subtype, elementId, inputType,
216                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
217                 mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey);
218         // Action previous with previous navigate flag.
219         doTestMoreKeysOf(code, subtype, elementId, inputType,
220                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
221                 mExpectedEmojiMoreKey);
222         // Action previous with next and previous navigate flags.
223         doTestMoreKeysOf(code, subtype, elementId, inputType,
224                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT
225                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
226                 mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey);
227     }
228 
doTestNoNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype, final int elementId, final int inputType)229     private void doTestNoNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype,
230             final int elementId, final int inputType) {
231         // No navigate flag.
232         doTestMoreKeysOf(code, subtype, elementId, inputType,
233                 EditorInfo.IME_NULL,
234                 ExpectedMoreKey.EMPTY_MORE_KEYS);
235         // With next navigate flag.
236         doTestMoreKeysOf(code, subtype, elementId, inputType,
237                 EditorInfo.IME_FLAG_NAVIGATE_NEXT,
238                 ExpectedMoreKey.EMPTY_MORE_KEYS);
239         // With previous navigate flag.
240         doTestMoreKeysOf(code, subtype, elementId, inputType,
241                 EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
242                 ExpectedMoreKey.EMPTY_MORE_KEYS);
243         // With next and previous naviagte flags.
244         doTestMoreKeysOf(code, subtype, elementId, inputType,
245                 EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
246                 ExpectedMoreKey.EMPTY_MORE_KEYS);
247         // Action next.
248         doTestMoreKeysOf(code, subtype, elementId, inputType,
249                 EditorInfo.IME_ACTION_NEXT,
250                 ExpectedMoreKey.EMPTY_MORE_KEYS);
251         // Action next with next navigate flag.
252         doTestMoreKeysOf(code, subtype, elementId, inputType,
253                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
254                 ExpectedMoreKey.EMPTY_MORE_KEYS);
255         // Action next with previous navigate flag.
256         doTestMoreKeysOf(code, subtype, elementId, inputType,
257                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
258                 ExpectedMoreKey.EMPTY_MORE_KEYS);
259         // Action next with next and previous navigate flags.
260         doTestMoreKeysOf(code, subtype, elementId, inputType,
261                 EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT
262                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
263                 ExpectedMoreKey.EMPTY_MORE_KEYS);
264         // Action previous.
265         doTestMoreKeysOf(code, subtype, elementId, inputType,
266                 EditorInfo.IME_ACTION_PREVIOUS,
267                 ExpectedMoreKey.EMPTY_MORE_KEYS);
268         // Action previous with next navigate flag.
269         doTestMoreKeysOf(code, subtype, elementId, inputType,
270                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT,
271                 ExpectedMoreKey.EMPTY_MORE_KEYS);
272         // Action previous with previous navigate flag.
273         doTestMoreKeysOf(code, subtype, elementId, inputType,
274                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
275                 ExpectedMoreKey.EMPTY_MORE_KEYS);
276         // Action previous with next and previous navigate flags.
277         doTestMoreKeysOf(code, subtype, elementId, inputType,
278                 EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT
279                         | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS,
280                 ExpectedMoreKey.EMPTY_MORE_KEYS);
281     }
282 
testMoreKeysOfEnterKey()283     public void testMoreKeysOfEnterKey() {
284         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
285         final InputMethodSubtype subtype = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
286                 Locale.US.toString(), SubtypeLocaleUtils.QWERTY);
287 
288         // Password field.
289         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET,
290                 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
291         // Email field.
292         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET,
293                 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
294         // Url field.
295         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET,
296                 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
297         // Phone number field.
298         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_PHONE,
299                 InputType.TYPE_CLASS_PHONE);
300         // Number field.
301         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER,
302                 InputType.TYPE_CLASS_NUMBER);
303         // Date-time field.
304         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER,
305                 InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL);
306         // Date field.
307         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER,
308                 InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE);
309         // Time field.
310         doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER,
311                 InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME);
312         // Text field.
313         if (isPhone()) {
314             // The enter key has an Emoji key as one of more keys.
315             doTestNavigationWithEmojiMoreKeysOf(Constants.CODE_ENTER, subtype,
316                     KeyboardId.ELEMENT_ALPHABET,
317                     InputType.TYPE_CLASS_TEXT);
318         } else {
319             // Tablet has a dedicated Emoji key, so the Enter key has no Emoji more key.
320             doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype,
321                     KeyboardId.ELEMENT_ALPHABET,
322                     InputType.TYPE_CLASS_TEXT);
323         }
324         // Short message field.
325         if (isPhone()) {
326             // Enter key is switched to Emoji key on a short message field.
327             // Emoji key has no navigation more keys.
328             doTestNoNavigationMoreKeysOf(Constants.CODE_EMOJI, subtype,
329                     KeyboardId.ELEMENT_ALPHABET,
330                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
331         } else {
332             doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype,
333                     KeyboardId.ELEMENT_ALPHABET,
334                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
335         }
336     }
337 }
338