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.Constants; 26 import com.android.inputmethod.latin.R; 27 import com.android.inputmethod.latin.RichInputMethodManager; 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 38 protected ExpectedMoreKey getExpectedNavigateNextMoreKey() { 39 return new ExpectedMoreKey(R.string.label_next_key); 40 } 41 42 protected ExpectedMoreKey getExpectedNavigatePreviousMoreKey() { 43 return new ExpectedMoreKey(R.string.label_previous_key); 44 } 45 46 protected ExpectedMoreKey getExpectedEmojiMoreKey() { 47 return new ExpectedMoreKey(KeyboardIconsSet.NAME_EMOJI_ACTION_KEY); 48 } 49 50 @Override 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 68 public ExpectedMoreKey(final String iconName) { 69 mLabelResId = NO_LABEL; 70 mIconId = KeyboardIconsSet.getIconId(iconName); 71 } 72 73 public ExpectedMoreKey(final int labelResId) { 74 mLabelResId = labelResId; 75 mIconId = KeyboardIconsSet.ICON_UNDEFINED; 76 } 77 } 78 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 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 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 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 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