aboutsummaryrefslogtreecommitdiffstats
path: root/tests/src
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src')
-rw-r--r--tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java212
-rw-r--r--tests/src/com/android/inputmethod/compat/TextInfoCompatUtilsTests.java86
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java191
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java97
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java65
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java135
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java346
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java644
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java340
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java96
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java167
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java21
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java (renamed from tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java)41
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java374
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java16
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Arabic.java352
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java229
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Azerty.java77
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Bengali.java192
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java105
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java97
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Colemak.java76
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/DevanagariLetterConstants.java75
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java122
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java110
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Farsi.java364
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Georgian.java163
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Greek.java139
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java187
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Hindi.java378
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java201
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Kannada.java197
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Khmer.java281
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Lao.java237
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java371
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java188
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Marathi.java179
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java114
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java290
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java187
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java269
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Nordic.java58
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java227
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java53
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java50
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java177
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java110
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Spanish.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Swiss.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Symbols.java205
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java161
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Tamil.java150
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Telugu.java189
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Thai.java266
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java143
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java136
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java184
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java363
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java138
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java135
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java345
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java76
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java89
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java89
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java77
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java191
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java159
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java80
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java104
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java99
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java38
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java88
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java73
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java50
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java122
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java78
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java133
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java109
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java106
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java57
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java181
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java157
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java81
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java56
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java62
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java56
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java75
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java107
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java106
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java56
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java82
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java70
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java148
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java149
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java69
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java38
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java38
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java52
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java94
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java104
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java81
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java69
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java75
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java155
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java70
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java55
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java93
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java124
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java50
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java86
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java83
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java148
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java36
-rw-r--r--tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java74
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java586
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java1212
-rw-r--r--tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java13
-rw-r--r--tests/src/com/android/inputmethod/latin/DistracterFilterTest.java151
-rw-r--r--tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java58
-rw-r--r--tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java12
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTests.java337
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java5
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java26
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java234
-rw-r--r--tests/src/com/android/inputmethod/latin/InputPointersTests.java209
-rw-r--r--tests/src/com/android/inputmethod/latin/InputTestsBase.java155
-rw-r--r--tests/src/com/android/inputmethod/latin/LatinImeStressTests.java9
-rw-r--r--tests/src/com/android/inputmethod/latin/PunctuationTests.java63
-rw-r--r--tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java242
-rw-r--r--tests/src/com/android/inputmethod/latin/ShiftModeTests.java126
-rw-r--r--tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java17
-rw-r--r--tests/src/com/android/inputmethod/latin/WordComposerTests.java57
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java104
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java438
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java361
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java881
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java306
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java389
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java78
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java222
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java38
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java716
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java44
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java32
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java51
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java195
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java321
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java (renamed from tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java)14
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java240
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java112
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java150
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java75
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java117
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java222
-rw-r--r--tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java477
-rw-r--r--tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java19
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java175
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java8
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java225
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java92
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java81
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java124
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java424
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java47
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java (renamed from tests/src/com/android/inputmethod/latin/EditDistanceTests.java)30
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java57
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java58
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java105
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java34
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java158
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java43
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java251
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java499
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java283
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java280
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java239
-rw-r--r--tests/src/com/android/inputmethod/research/MotionEventReaderTests.java171
218 files changed, 26148 insertions, 4029 deletions
diff --git a/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java
new file mode 100644
index 000000000..319302c71
--- /dev/null
+++ b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.graphics.Typeface;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+
+import java.util.Locale;
+
+@SmallTest
+public class LocaleSpanCompatUtilsTests extends AndroidTestCase {
+ public void testInstantiatable() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ // LocaleSpan isn't yet available.
+ return;
+ }
+ assertTrue(LocaleSpanCompatUtils.isLocaleSpanAvailable());
+ final Object japaneseLocaleSpan = LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE);
+ assertNotNull(japaneseLocaleSpan);
+ assertEquals(Locale.JAPANESE,
+ LocaleSpanCompatUtils.getLocaleFromLocaleSpan(japaneseLocaleSpan));
+ }
+
+ private static void assertLocaleSpan(final Spanned spanned, final int index,
+ final int expectedStart, final int expectedEnd,
+ final Locale expectedLocale, final int expectedSpanFlags) {
+ final Object span = spanned.getSpans(0, spanned.length(), Object.class)[index];
+ assertEquals(expectedLocale, LocaleSpanCompatUtils.getLocaleFromLocaleSpan(span));
+ assertEquals(expectedStart, spanned.getSpanStart(span));
+ assertEquals(expectedEnd, spanned.getSpanEnd(span));
+ assertEquals(expectedSpanFlags, spanned.getSpanFlags(span));
+ }
+
+ private static void assertSpanEquals(final Object expectedSpan, final Spanned spanned,
+ final int index) {
+ final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
+ assertEquals(expectedSpan, spans[index]);
+ }
+
+ private static void assertSpanCount(final int expectedCount, final Spanned spanned) {
+ final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
+ assertEquals(expectedCount, spans.length);
+ }
+
+ public void testUpdateLocaleSpan() {
+ if (!LocaleSpanCompatUtils.isLocaleSpanAvailable()) {
+ return;
+ }
+
+ // Test if the simplest case works.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if only LocaleSpans are updated.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
+ text.setSpan(styleSpan, 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
+ assertSpanCount(2, text);
+ assertSpanEquals(styleSpan, text, 0);
+ assertLocaleSpan(text, 1, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if two jointed spans are merged into one span.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if two overlapped spans are merged into one span.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if three overlapped spans are merged into one span.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 8, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 8, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if disjoint spans remain disjoint.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 8, 9, Locale.JAPANESE);
+ assertSpanCount(3, text);
+ assertLocaleSpan(text, 0, 1, 3, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 1, 5, 6, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 2, 8, 9, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if existing span flags are preserved during merge.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 4, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ }
+
+ // Test if existing span flags are preserved even when partially overlapped (leading edge).
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 7, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
+ Spannable.SPAN_INCLUSIVE_EXCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ }
+
+ // Test if existing span flags are preserved even when partially overlapped (trailing edge).
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 3, 7,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
+ Spannable.SPAN_EXCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE);
+ }
+
+ // Test if existing locale span will be removed when the locale doesn't match.
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 5,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 7, Locale.JAPANESE);
+ assertSpanCount(1, text);
+ assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if existing locale span will be removed when the locale doesn't match. (case 2)
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 6, Locale.JAPANESE);
+ assertSpanCount(3, text);
+ assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 1, 6, 7, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 2, 5, 6, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if existing locale span will be removed when the locale doesn't match. (case 3)
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 5, Locale.JAPANESE);
+ assertSpanCount(2, text);
+ assertLocaleSpan(text, 0, 5, 7, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 1, 2, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // Test if existing locale span will be removed when the locale doesn't match. (case 3)
+ {
+ final SpannableString text = new SpannableString("0123456789");
+ text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 8, Locale.JAPANESE);
+ assertSpanCount(2, text);
+ assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertLocaleSpan(text, 1, 5, 8, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/compat/TextInfoCompatUtilsTests.java b/tests/src/com/android/inputmethod/compat/TextInfoCompatUtilsTests.java
new file mode 100644
index 000000000..c399cced6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/compat/TextInfoCompatUtilsTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.text.style.URLSpan;
+import android.view.textservice.TextInfo;
+
+import java.util.Arrays;
+
+@SmallTest
+public class TextInfoCompatUtilsTests extends AndroidTestCase {
+ final private static String TEST_TEXT = "0123456789";
+ final private static int TEST_COOKIE = 0x1234;
+ final private static int TEST_SEQUENCE_NUMBER = 0x4321;
+ final private static int TEST_CHAR_SEQUENCE_START = 1;
+ final private static int TEST_CHAR_SEQUENCE_END = 6;
+ final private static StyleSpan TEST_STYLE_SPAN = new StyleSpan(Typeface.BOLD);
+ final private static int TEST_STYLE_SPAN_START = 4;
+ final private static int TEST_STYLE_SPAN_END = 5;
+ final private static int TEST_STYLE_SPAN_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
+ final private static URLSpan TEST_URL_SPAN_URL = new URLSpan("http://example.com");
+ final private static int TEST_URL_SPAN_START = 3;
+ final private static int TEST_URL_SPAN_END = 7;
+ final private static int TEST_URL_SPAN_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+
+ public void testGetCharSequence() {
+ final SpannableString text = new SpannableString(TEST_TEXT);
+ text.setSpan(TEST_STYLE_SPAN, TEST_STYLE_SPAN_START, TEST_STYLE_SPAN_END,
+ TEST_STYLE_SPAN_FLAGS);
+ text.setSpan(TEST_URL_SPAN_URL, TEST_URL_SPAN_START, TEST_URL_SPAN_END,
+ TEST_URL_SPAN_FLAGS);
+
+ final TextInfo textInfo = TextInfoCompatUtils.newInstance(text,
+ TEST_CHAR_SEQUENCE_START, TEST_CHAR_SEQUENCE_END, TEST_COOKIE,
+ TEST_SEQUENCE_NUMBER);
+ final Spanned expectedSpanned = (Spanned) text.subSequence(TEST_CHAR_SEQUENCE_START,
+ TEST_CHAR_SEQUENCE_END);
+ final CharSequence actualCharSequence =
+ TextInfoCompatUtils.getCharSequenceOrString(textInfo);
+
+ // This should be valid even if TextInfo#getCharSequence is not supported.
+ assertTrue(TextUtils.equals(expectedSpanned, actualCharSequence));
+
+ if (TextInfoCompatUtils.isCharSequenceSupported()) {
+ // This is valid only if TextInfo#getCharSequence is supported.
+ assertTrue("should be Spanned", actualCharSequence instanceof Spanned);
+ assertTrue(Arrays.equals(marshall(expectedSpanned), marshall(actualCharSequence)));
+ }
+ }
+
+ private static byte[] marshall(final CharSequence cahrSequence) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ TextUtils.writeToParcel(cahrSequence, parcel, 0);
+ return parcel.marshall();
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ parcel = null;
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java
new file mode 100644
index 000000000..96f925554
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.res.Resources;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.InputType;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+@MediumTest
+public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetTestsBase {
+ @Override
+ protected int getKeyboardThemeForTests() {
+ return KeyboardTheme.THEME_ID_KLP;
+ }
+
+ private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet,
+ final int elementId, final CharSequence label, final int iconId) {
+ final Keyboard keyboard = layoutSet.getKeyboard(elementId);
+ final Key enterKey = keyboard.getKey(Constants.CODE_ENTER);
+ assertNotNull(tag + " enter key on " + keyboard.mId, enterKey);
+ assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel());
+ assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId());
+ }
+
+ protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype,
+ final int actionId, final int labelResId) {
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.imeOptions = actionId;
+ final RunInLocale<String> job = new RunInLocale<String>() {
+ @Override
+ protected String job(final Resources res) {
+ return res.getString(labelResId);
+ }
+ };
+ final Resources res = getContext().getResources();
+ final String label;
+ if (subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
+ // Using system locale.
+ label = res.getString(labelResId);
+ } else {
+ label = job.runInLocale(res, SubtypeLocaleUtils.getSubtypeLocale(subtype));
+ }
+ doTestActionLabel(tag, subtype, editorInfo, label);
+ }
+
+ protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype,
+ final EditorInfo editorInfo, final CharSequence label) {
+ // Test text layouts.
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test phone number layouts.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test normal number layout.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test number password layouts.
+ editorInfo.inputType =
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+ final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ }
+
+ protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype,
+ final int actionId, final String iconName) {
+ final int iconId = KeyboardIconsSet.getIconId(iconName);
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.imeOptions = actionId;
+ // Test text layouts.
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, null /* label */, iconId);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, null /* label */, iconId);
+ doTestActionKey(
+ tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, null /* label */, iconId);
+ // Test phone number layouts.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, null /* label */, iconId);
+ doTestActionKey(
+ tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, null /* label */, iconId);
+ // Test normal number layout.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+ // Test number password layout.
+ editorInfo.inputType =
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+ final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+ }
+
+ public void testActionUnspecified() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "unspecifiled "
+ + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED,
+ KeyboardIconsSet.NAME_ENTER_KEY);
+ }
+ }
+
+ public void testActionNone() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE,
+ KeyboardIconsSet.NAME_ENTER_KEY);
+ }
+ }
+
+ public void testActionGo() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_GO, R.string.label_go_key);
+ }
+ }
+
+ public void testActionSearch() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH,
+ KeyboardIconsSet.NAME_SEARCH_KEY);
+ }
+ }
+
+ public void testActionSend() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_SEND, R.string.label_send_key);
+ }
+ }
+
+ public void testActionNext() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_NEXT, R.string.label_next_key);
+ }
+ }
+
+ public void testActionDone() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_DONE, R.string.label_done_key);
+ }
+ }
+
+ public void testActionPrevious() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(
+ tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, R.string.label_previous_key);
+ }
+ }
+
+ public void testActionCustom() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ final CharSequence customLabel = "customLabel";
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED;
+ editorInfo.actionLabel = customLabel;
+ doTestActionLabel(tag, subtype, editorInfo, customLabel);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java
new file mode 100644
index 000000000..7747ac5f9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+@MediumTest
+public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActionLabelKlpTests {
+ @Override
+ protected int getKeyboardThemeForTests() {
+ return KeyboardTheme.THEME_ID_LXX_DARK;
+ }
+
+ @Override
+ public void testActionUnspecified() {
+ super.testActionUnspecified();
+ }
+
+ @Override
+ public void testActionNone() {
+ super.testActionNone();
+ }
+
+ @Override
+ public void testActionGo() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_GO,
+ KeyboardIconsSet.NAME_GO_KEY);
+ }
+ }
+
+ @Override
+ public void testActionSearch() {
+ super.testActionSearch();
+ }
+
+ @Override
+ public void testActionSend() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEND,
+ KeyboardIconsSet.NAME_SEND_KEY);
+ }
+ }
+
+ @Override
+ public void testActionNext() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NEXT,
+ KeyboardIconsSet.NAME_NEXT_KEY);
+ }
+ }
+
+ @Override
+ public void testActionDone() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_DONE,
+ KeyboardIconsSet.NAME_DONE_KEY);
+ }
+ }
+
+ @Override
+ public void testActionPrevious() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_PREVIOUS,
+ KeyboardIconsSet.NAME_PREVIOUS_KEY);
+ }
+ }
+
+ @Override
+ public void testActionCustom() {
+ super.testActionCustom();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
new file mode 100644
index 000000000..eb67bc134
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+
+@SmallTest
+public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase {
+ private static final int NUMBER_OF_SUBTYPES = 77;
+ private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 45;
+ private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
+
+ @Override
+ protected int getKeyboardThemeForTests() {
+ return KeyboardTheme.THEME_ID_KLP;
+ }
+
+ private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
+ final StringBuilder sb = new StringBuilder();
+ for (int index = 0; index < subtypeList.size(); index++) {
+ final InputMethodSubtype subtype = subtypeList.get(index);
+ sb.append(index + ": ");
+ sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ public final void testAllSubtypesCount() {
+ final ArrayList<InputMethodSubtype> allSubtypesList = getAllSubtypesList();
+ assertEquals(toString(allSubtypesList), NUMBER_OF_SUBTYPES, allSubtypesList.size());
+ }
+
+ public final void testAsciiCapableSubtypesCount() {
+ final ArrayList<InputMethodSubtype> asciiCapableSubtypesList =
+ getAsciiCapableSubtypesList();
+ assertEquals(toString(asciiCapableSubtypesList),
+ NUMBER_OF_ASCII_CAPABLE_SUBTYPES, asciiCapableSubtypesList.size());
+ }
+
+ public final void testAdditionalSubtypesCount() {
+ final ArrayList<InputMethodSubtype> additionalSubtypesList = getAdditionalSubtypesList();
+ assertEquals(toString(additionalSubtypesList),
+ NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES, additionalSubtypesList.size());
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
new file mode 100644
index 000000000..cf884bfea
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.view.ContextThemeWrapper;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet.Builder;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.ResourceUtils;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase {
+ // All input method subtypes of LatinIME.
+ private final ArrayList<InputMethodSubtype> mAllSubtypesList = new ArrayList<>();
+ private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = new ArrayList<>();
+ private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = new ArrayList<>();
+
+ private Context mThemeContext;
+ private int mScreenMetrics;
+
+ protected abstract int getKeyboardThemeForTests();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics);
+
+ final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById(
+ getKeyboardThemeForTests());
+ mThemeContext = new ContextThemeWrapper(mContext, keyboardTheme.mStyleId);
+ RichInputMethodManager.init(mThemeContext);
+ final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+
+ final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme();
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int index = 0; index < subtypeCount; index++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ if (AdditionalSubtypeUtils.isAdditionalSubtype(subtype)) {
+ mAdditionalSubtypesList.add(subtype);
+ continue;
+ }
+ mAllSubtypesList.add(subtype);
+ if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
+ mAsciiCapableSubtypesList.add(subtype);
+ }
+ }
+ }
+
+ protected final ArrayList<InputMethodSubtype> getAllSubtypesList() {
+ return mAllSubtypesList;
+ }
+
+ protected final ArrayList<InputMethodSubtype> getAsciiCapableSubtypesList() {
+ return mAsciiCapableSubtypesList;
+ }
+
+ protected final ArrayList<InputMethodSubtype> getAdditionalSubtypesList() {
+ return mAdditionalSubtypesList;
+ }
+
+ protected final boolean isPhone() {
+ return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
+ || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+ }
+
+ protected final InputMethodSubtype getSubtype(final Locale locale,
+ final String keyboardLayout) {
+ for (final InputMethodSubtype subtype : mAllSubtypesList) {
+ final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ final String subtypeLayout = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+ if (locale.equals(subtypeLocale) && keyboardLayout.equals(subtypeLayout)) {
+ // Found subtype that matches locale and keyboard layout.
+ return subtype;
+ }
+ }
+ for (final InputMethodSubtype subtype : mAsciiCapableSubtypesList) {
+ final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ if (locale.equals(subtypeLocale)) {
+ // Create additional subtype.
+ return AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ locale.toString(), keyboardLayout);
+ }
+ }
+ throw new RuntimeException(
+ "Unknown subtype: locale=" + locale + " keyboardLayout=" + keyboardLayout);
+ }
+
+ protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype,
+ final EditorInfo editorInfo) {
+ return createKeyboardLayoutSet(subtype, editorInfo, false /* voiceInputKeyEnabled */,
+ false /* languageSwitchKeyEnabled */);
+ }
+
+ protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype,
+ final EditorInfo editorInfo, final boolean voiceInputKeyEnabled,
+ final boolean languageSwitchKeyEnabled) {
+ final Context context = mThemeContext;
+ final Resources res = context.getResources();
+ final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
+ final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
+ final Builder builder = new Builder(context, editorInfo);
+ builder.setKeyboardGeometry(keyboardWidth, keyboardHeight)
+ .setSubtype(subtype)
+ .setVoiceInputKeyEnabled(voiceInputKeyEnabled)
+ .setLanguageSwitchKeyEnabled(languageSwitchKeyEnabled);
+ return builder.build();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java
new file mode 100644
index 000000000..0c7e4000e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_ICS;
+import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_KLP;
+import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_DARK;
+import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_LIGHT;
+
+import android.content.SharedPreferences;
+import android.os.Build.VERSION_CODES;
+import android.preference.PreferenceManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class KeyboardThemeTests extends AndroidTestCase {
+ private SharedPreferences mPrefs;
+
+ // TODO: Remove this constant once the *next* version becomes available.
+ private static final int VERSION_CODES_LXX = VERSION_CODES.CUR_DEVELOPMENT;
+
+ private static final int THEME_ID_NULL = -1;
+ private static final int THEME_ID_UNKNOWN = -2;
+ private static final int THEME_ID_ILLEGAL = -3;
+ private static final String ILLEGAL_THEME_ID_STRING = "ThisCausesNumberFormatExecption";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ }
+
+ /*
+ * Helper functions.
+ */
+
+ private static boolean isValidKeyboardThemeId(final int themeId) {
+ switch (themeId) {
+ case THEME_ID_ICS:
+ case THEME_ID_KLP:
+ case THEME_ID_LXX_LIGHT:
+ case THEME_ID_LXX_DARK:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setKeyboardThemePreference(final String prefKey, final int themeId) {
+ final String themeIdString = Integer.toString(themeId);
+ if (isValidKeyboardThemeId(themeId) || themeId == THEME_ID_UNKNOWN) {
+ // Set valid theme id to preference.
+ mPrefs.edit().putString(prefKey, themeIdString).apply();
+ return;
+ }
+ if (themeId == THEME_ID_NULL) {
+ // Simulate undefined preference.
+ mPrefs.edit().remove(prefKey).apply();
+ return;
+ }
+ // themeId == THEME_ID_ILLEGAL
+ // Simulate illegal format theme id in preference.
+ mPrefs.edit().putString(prefKey, ILLEGAL_THEME_ID_STRING).apply();
+ }
+
+ private void assertKeyboardTheme(final int sdkVersion, final int expectedThemeId) {
+ assertEquals(expectedThemeId, KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion).mThemeId);
+ }
+
+ /*
+ * Test keyboard theme preference on the same platform version and the same keyboard version.
+ */
+
+ private void assertKeyboardThemePreference(final int sdkVersion, final int previousThemeId,
+ final int expectedThemeId) {
+ // Clear preferences before testing.
+ setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL);
+ setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL);
+ // Set the preference of the sdkVersion to previousThemeId.
+ final String prefKey = KeyboardTheme.getPreferenceKey(sdkVersion);
+ setKeyboardThemePreference(prefKey, previousThemeId);
+ assertKeyboardTheme(sdkVersion, expectedThemeId);
+ }
+
+ private void assertKeyboardThemePreferenceOnKlp(final int sdkVersion) {
+ final int defaultThemeId = THEME_ID_KLP;
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_NULL, defaultThemeId);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_LIGHT, THEME_ID_LXX_LIGHT);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_DARK, THEME_ID_LXX_DARK);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_UNKNOWN, defaultThemeId);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_ILLEGAL, defaultThemeId);
+ }
+
+ public void testKeyboardThemePreferenceOnKlp() {
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN);
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN_MR1);
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN_MR2);
+ assertKeyboardThemePreferenceOnKlp(VERSION_CODES.KITKAT);
+ }
+
+ private void assertKeyboardThemePreferenceOnLxx(final int sdkVersion) {
+ final int defaultThemeId = THEME_ID_LXX_LIGHT;
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_NULL, defaultThemeId);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_LIGHT, THEME_ID_LXX_LIGHT);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_DARK, THEME_ID_LXX_DARK);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_UNKNOWN, defaultThemeId);
+ assertKeyboardThemePreference(sdkVersion, THEME_ID_ILLEGAL, defaultThemeId);
+ }
+
+ public void testKeyboardThemePreferenceOnLxx() {
+ assertKeyboardThemePreferenceOnLxx(VERSION_CODES_LXX);
+ }
+
+ /*
+ * Test default keyboard theme based on the platform version.
+ */
+
+ private void assertDefaultKeyboardTheme(final int sdkVersion, final int previousThemeId,
+ final int expectedThemeId) {
+ final String oldPrefKey = KeyboardTheme.KLP_KEYBOARD_THEME_KEY;
+ setKeyboardThemePreference(oldPrefKey, previousThemeId);
+
+ final KeyboardTheme defaultTheme =
+ KeyboardTheme.getDefaultKeyboardTheme(mPrefs, sdkVersion);
+
+ assertNotNull(defaultTheme);
+ assertEquals(expectedThemeId, defaultTheme.mThemeId);
+ if (sdkVersion <= VERSION_CODES.KITKAT) {
+ // Old preference must be retained if it is valid. Otherwise it must be pruned.
+ assertEquals(isValidKeyboardThemeId(previousThemeId), mPrefs.contains(oldPrefKey));
+ return;
+ }
+ // Old preference must be removed.
+ assertFalse(mPrefs.contains(oldPrefKey));
+ }
+
+ private void assertDefaultKeyboardThemeOnKlp(final int sdkVersion) {
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_NULL, THEME_ID_KLP);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP);
+ }
+
+ public void testDefaultKeyboardThemeOnKlp() {
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN);
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN_MR1);
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN_MR2);
+ assertDefaultKeyboardThemeOnKlp(VERSION_CODES.KITKAT);
+ }
+
+ private void assertDefaultKeyboardThemeOnLxx(final int sdkVersion) {
+ // Forced to switch to LXX theme.
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ICS, THEME_ID_LXX_LIGHT);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_KLP, THEME_ID_LXX_LIGHT);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_LIGHT);
+ assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_LIGHT);
+ }
+
+ public void testDefaultKeyboardThemeOnLxx() {
+ assertDefaultKeyboardThemeOnLxx(VERSION_CODES_LXX);
+ }
+
+ /*
+ * Test keyboard theme preference while upgrading the keyboard that doesn't support LXX theme
+ * to the keyboard that supports LXX theme.
+ */
+
+ private void assertUpgradeKeyboardToLxxOn(final int sdkVersion, final int previousThemeId,
+ final int expectedThemeId) {
+ setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, previousThemeId);
+ // Clean up new keyboard theme preference to simulate "upgrade to LXX keyboard".
+ setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL);
+
+ final KeyboardTheme theme = KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion);
+
+ assertNotNull(theme);
+ assertEquals(expectedThemeId, theme.mThemeId);
+ if (sdkVersion <= VERSION_CODES.KITKAT) {
+ // New preference must not exist.
+ assertFalse(mPrefs.contains(KeyboardTheme.LXX_KEYBOARD_THEME_KEY));
+ // Old preference must be retained if it is valid. Otherwise it must be pruned.
+ assertEquals(isValidKeyboardThemeId(previousThemeId),
+ mPrefs.contains(KeyboardTheme.KLP_KEYBOARD_THEME_KEY));
+ if (isValidKeyboardThemeId(previousThemeId)) {
+ // Old preference must have an expected value.
+ assertEquals(mPrefs.getString(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, null),
+ Integer.toString(expectedThemeId));
+ }
+ return;
+ }
+ // Old preference must be removed.
+ assertFalse(mPrefs.contains(KeyboardTheme.KLP_KEYBOARD_THEME_KEY));
+ // New preference must not exist.
+ assertFalse(mPrefs.contains(KeyboardTheme.LXX_KEYBOARD_THEME_KEY));
+ }
+
+ private void assertUpgradeKeyboardToLxxOnKlp(final int sdkVersion) {
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_NULL, THEME_ID_KLP);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP);
+ }
+
+ // Upgrading keyboard on I,J and K.
+ public void testUpgradeKeyboardToLxxOnKlp() {
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN);
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN_MR1);
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN_MR2);
+ assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.KITKAT);
+ }
+
+ private void assertUpgradeKeyboardToLxxOnLxx(final int sdkVersion) {
+ // Forced to switch to LXX theme.
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ICS, THEME_ID_LXX_LIGHT);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_KLP, THEME_ID_LXX_LIGHT);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_LIGHT);
+ assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_LIGHT);
+ }
+
+ // Upgrading keyboard on L.
+ public void testUpgradeKeyboardToLxxOnLxx() {
+ assertUpgradeKeyboardToLxxOnLxx(VERSION_CODES_LXX);
+ }
+
+ /*
+ * Test keyboard theme preference while upgrading platform version.
+ */
+
+ private void assertUpgradePlatformFromTo(final int oldSdkVersion, final int newSdkVersion,
+ final int previousThemeId, final int expectedThemeId) {
+ if (newSdkVersion < oldSdkVersion) {
+ // No need to test.
+ return;
+ }
+ // Clean up preferences.
+ setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL);
+ setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL);
+
+ final String oldPrefKey = KeyboardTheme.getPreferenceKey(oldSdkVersion);
+ setKeyboardThemePreference(oldPrefKey, previousThemeId);
+
+ assertKeyboardTheme(newSdkVersion, expectedThemeId);
+ }
+
+ private void assertUpgradePlatformFromKlpToKlp(final int oldSdkVersion,
+ final int newSdkVersion) {
+ assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_KLP);
+ assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP);
+ assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP);
+ }
+
+ private void assertUpgradePlatformToKlpFrom(final int oldSdkVersion) {
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN);
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN_MR1);
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN_MR2);
+ assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.KITKAT);
+ }
+
+ // Update platform from I,J, and K to I,J, and K
+ public void testUpgradePlatformToKlpFromKlp() {
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN);
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN_MR1);
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN_MR2);
+ assertUpgradePlatformToKlpFrom(VERSION_CODES.KITKAT);
+ }
+
+ private void assertUpgradePlatformToLxxFrom(final int oldSdkVersion) {
+ // Forced to switch to LXX theme.
+ final int newSdkVersion = VERSION_CODES_LXX;
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_LIGHT);
+ }
+
+ // Update platform from I,J, and K to L
+ public void testUpgradePlatformToLxx() {
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.ICE_CREAM_SANDWICH);
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN);
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN_MR1);
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN_MR2);
+ assertUpgradePlatformToLxxFrom(VERSION_CODES.KITKAT);
+ }
+
+ // Update platform from L to L.
+ public void testUpgradePlatformToLxxFromLxx() {
+ final int oldSdkVersion = VERSION_CODES_LXX;
+ final int newSdkVersion = VERSION_CODES_LXX;
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_ICS);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_KLP);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_LXX_LIGHT, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_LXX_DARK, THEME_ID_LXX_DARK);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_LIGHT);
+ assertUpgradePlatformFromTo(
+ oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_LIGHT);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
index afb2b0343..8e26e7fc7 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,643 +17,39 @@
package com.android.inputmethod.keyboard.internal;
import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
-import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
-import android.content.Context;
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.utils.RunInLocale;
-
-import java.util.Arrays;
-import java.util.Locale;
@SmallTest
-public class KeySpecParserTests extends AndroidTestCase {
- private final static Locale TEST_LOCALE = Locale.ENGLISH;
- final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
- final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
-
- private static final String CODE_SETTINGS = "!code/key_settings";
- private static final String ICON_SETTINGS = "!icon/settings_key";
- private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase(Locale.ROOT);
- private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase(Locale.ROOT);
- private static final String CODE_NON_EXISTING = "!code/non_existing";
- private static final String ICON_NON_EXISTING = "!icon/non_existing";
-
- private int mCodeSettings;
- private int mCodeActionNext;
- private int mSettingsIconId;
-
+public final class KeySpecParserTests extends KeySpecParserTestsBase {
@Override
- protected void setUp() throws Exception {
- super.setUp();
-
- final String language = TEST_LOCALE.getLanguage();
- mCodesSet.setLanguage(language);
- mTextsSet.setLanguage(language);
- final Context context = getContext();
- new RunInLocale<Void>() {
- @Override
- protected Void job(final Resources res) {
- mTextsSet.loadStringResources(context);
- return null;
- }
- }.runInLocale(context.getResources(), TEST_LOCALE);
-
- mCodeSettings = KeySpecParser.parseCode(
- CODE_SETTINGS, mCodesSet, CODE_UNSPECIFIED);
- mCodeActionNext = KeySpecParser.parseCode(
- "!code/key_action_next", mCodesSet, CODE_UNSPECIFIED);
- mSettingsIconId = KeySpecParser.getIconId(ICON_SETTINGS);
- }
-
- private void assertParser(String message, String moreKeySpec, String expectedLabel,
- String expectedOutputText, int expectedIcon, int expectedCode) {
- final String labelResolved = KeySpecParser.resolveTextReference(moreKeySpec, mTextsSet);
- final MoreKeySpec spec = new MoreKeySpec(labelResolved, false /* needsToUpperCase */,
- Locale.US, mCodesSet);
- assertEquals(message + " [label]", expectedLabel, spec.mLabel);
- assertEquals(message + " [ouptputText]", expectedOutputText, spec.mOutputText);
+ protected void assertParser(final String message, final String keySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIcon,
+ final int expectedCode) {
+ final String keySpecResolved = mTextsSet.resolveTextReference(keySpec);
+ final String actualLabel = KeySpecParser.getLabel(keySpecResolved);
+ final String actualOutputText = KeySpecParser.getOutputText(keySpecResolved);
+ final int actualIcon = KeySpecParser.getIconId(keySpecResolved);
+ final int actualCode = KeySpecParser.getCode(keySpecResolved);
+ assertEquals(message + " [label]", expectedLabel, actualLabel);
+ assertEquals(message + " [ouptputText]", expectedOutputText, actualOutputText);
assertEquals(message + " [icon]",
KeyboardIconsSet.getIconName(expectedIcon),
- KeyboardIconsSet.getIconName(spec.mIconId));
+ KeyboardIconsSet.getIconName(actualIcon));
assertEquals(message + " [code]",
Constants.printableCode(expectedCode),
- Constants.printableCode(spec.mCode));
- }
-
- private void assertParserError(String message, String moreKeySpec, String expectedLabel,
- String expectedOutputText, int expectedIcon, int expectedCode) {
- try {
- assertParser(message, moreKeySpec, expectedLabel, expectedOutputText, expectedIcon,
- expectedCode);
- fail(message);
- } catch (Exception pcpe) {
- // success.
- }
- }
-
- // \U001d11e: MUSICAL SYMBOL G CLEF
- private static final String PAIR1 = "\ud834\udd1e";
- private static final int CODE1 = PAIR1.codePointAt(0);
- // \U001d122: MUSICAL SYMBOL F CLEF
- private static final String PAIR2 = "\ud834\udd22";
- private static final int CODE2 = PAIR2.codePointAt(0);
- // \U002f8a6: CJK COMPATIBILITY IDEOGRAPH-2F8A6; variant character of \u6148.
- private static final String PAIR3 = "\ud87e\udca6";
- private static final String SURROGATE1 = PAIR1 + PAIR2;
- private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
-
- public void testSingleLetter() {
- assertParser("Single letter", "a",
- "a", null, ICON_UNDEFINED, 'a');
- assertParser("Single surrogate", PAIR1,
- PAIR1, null, ICON_UNDEFINED, CODE1);
- assertParser("Single escaped bar", "\\|",
- "|", null, ICON_UNDEFINED, '|');
- assertParser("Single escaped escape", "\\\\",
- "\\", null, ICON_UNDEFINED, '\\');
- assertParser("Single comma", ",",
- ",", null, ICON_UNDEFINED, ',');
- assertParser("Single escaped comma", "\\,",
- ",", null, ICON_UNDEFINED, ',');
- assertParser("Single escaped letter", "\\a",
- "a", null, ICON_UNDEFINED, 'a');
- assertParser("Single escaped surrogate", "\\" + PAIR2,
- PAIR2, null, ICON_UNDEFINED, CODE2);
- assertParser("Single bang", "!",
- "!", null, ICON_UNDEFINED, '!');
- assertParser("Single escaped bang", "\\!",
- "!", null, ICON_UNDEFINED, '!');
- assertParser("Single output text letter", "a|a",
- "a", null, ICON_UNDEFINED, 'a');
- assertParser("Single surrogate pair outputText", "G Clef|" + PAIR1,
- "G Clef", null, ICON_UNDEFINED, CODE1);
- assertParser("Single letter with outputText", "a|abc",
- "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with surrogate outputText", "a|" + SURROGATE1,
- "a", SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single surrogate with outputText", PAIR3 + "|abc",
- PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with escaped outputText", "a|a\\|c",
- "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with escaped surrogate outputText",
- "a|" + PAIR1 + "\\|" + PAIR2,
- "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with comma outputText", "a|a,b",
- "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with escaped comma outputText", "a|a\\,b",
- "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with outputText starts with bang", "a|!bc",
- "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with surrogate outputText starts with bang", "a|!" + SURROGATE2,
- "a", "!" + SURROGATE2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with outputText contains bang", "a|a!c",
- "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single letter with escaped bang outputText", "a|\\!bc",
- "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Single escaped escape with single outputText", "\\\\|\\\\",
- "\\", null, ICON_UNDEFINED, '\\');
- assertParser("Single escaped bar with single outputText", "\\||\\|",
- "|", null, ICON_UNDEFINED, '|');
- assertParser("Single letter with code", "a|" + CODE_SETTINGS,
- "a", null, ICON_UNDEFINED, mCodeSettings);
- }
-
- public void testLabel() {
- assertParser("Simple label", "abc",
- "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Simple surrogate label", SURROGATE1,
- SURROGATE1, SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped bar", "a\\|c",
- "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Surrogate label with escaped bar", PAIR1 + "\\|" + PAIR2,
- PAIR1 + "|" + PAIR2, PAIR1 + "|" + PAIR2,
- ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped escape", "a\\\\c",
- "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with comma", "a,c",
- "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped comma", "a\\,c",
- "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label starts with bang", "!bc",
- "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Surrogate label starts with bang", "!" + SURROGATE1,
- "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label contains bang", "a!c",
- "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped bang", "\\!bc",
- "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped letter", "\\abc",
- "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with outputText", "abc|def",
- "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with comma and outputText", "a,c|def",
- "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped comma label with outputText", "a\\,c|def",
- "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped label with outputText", "a\\|c|def",
- "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped bar outputText", "abc|d\\|f",
- "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped escape label with outputText", "a\\\\|def",
- "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label starts with bang and outputText", "!bc|def",
- "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label contains bang label and outputText", "a!c|def",
- "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped bang label with outputText", "\\!bc|def",
- "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with comma outputText", "abc|a,b",
- "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped comma outputText", "abc|a\\,b",
- "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with outputText starts with bang", "abc|!bc",
- "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with outputText contains bang", "abc|a!c",
- "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped bang outputText", "abc|\\!bc",
- "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with escaped bar outputText", "abc|d\\|f",
- "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f",
- "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with code", "abc|" + CODE_SETTINGS,
- "abc", null, ICON_UNDEFINED, mCodeSettings);
- assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS,
- "a|c", null, ICON_UNDEFINED, mCodeSettings);
- }
-
- public void testIconAndCode() {
- assertParser("Icon with outputText", ICON_SETTINGS + "|abc",
- null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT);
- assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc",
- null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
- assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c",
- null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT);
- assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc",
- null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
- assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS,
- "!bc", null, ICON_UNDEFINED, mCodeSettings);
- assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS,
- "a!c", null, ICON_UNDEFINED, mCodeSettings);
- assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS,
- "!bc", null, ICON_UNDEFINED, mCodeSettings);
- assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS,
- null, null, mSettingsIconId, mCodeSettings);
- }
-
- public void testResourceReference() {
- assertParser("Settings as more key", "!text/settings_as_more_key",
- null, null, mSettingsIconId, mCodeSettings);
-
- assertParser("Action next as more key", "!text/label_next_key|!code/key_action_next",
- "Next", null, ICON_UNDEFINED, mCodeActionNext);
-
- assertParser("Popular domain",
- "!text/keylabel_for_popular_domain|!text/keylabel_for_popular_domain ",
- ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ Constants.printableCode(actualCode));
}
- public void testFormatError() {
- assertParserError("Empty spec", "", null,
- null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Empty label with outputText", "|a",
- null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Empty label with code", "|" + CODE_SETTINGS,
- null, null, ICON_UNDEFINED, mCodeSettings);
- assertParserError("Empty outputText with label", "a|",
- "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Empty outputText with icon", ICON_SETTINGS + "|",
- null, null, mSettingsIconId, CODE_UNSPECIFIED);
- assertParserError("Empty icon and code", "|",
+ // TODO: Remove this method.
+ // These should throw {@link KeySpecParserError} when Key.keyLabel attribute become mandatory.
+ public void testEmptySpec() {
+ assertParser("Null spec", null,
+ null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParser("Empty spec", "",
null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Icon without code", ICON_SETTINGS,
- null, null, mSettingsIconId, CODE_UNSPECIFIED);
- assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc",
- null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING,
- "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Third bar at end", "a|b|",
- "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Multiple bar", "a|b|c",
- "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
- assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c",
- "a", null, ICON_UNDEFINED, mCodeSettings);
- assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c",
- null, null, mSettingsIconId, CODE_UNSPECIFIED);
- assertParserError("Multiple bar with icon and code",
- ICON_SETTINGS + "|" + CODE_SETTINGS + "|c",
- null, null, mSettingsIconId, mCodeSettings);
- }
-
- public void testUselessUpperCaseSpecifier() {
- assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE,
- "a", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE,
- "abc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE,
- "a|c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc",
- "!ICON/SETTINGS_KEY", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc",
- "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c",
- "!ICON/SETTINGS_KEY", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc",
- "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE,
- "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE,
- "a!c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE,
- "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE,
- "!ICON/SETTINGS_KEY", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParser("SETTINGS AS MORE KEY", "!TEXT/SETTINGS_AS_MORE_KEY",
- "!TEXT/SETTINGS_AS_MORE_KEY", "!TEXT/SETTINGS_AS_MORE_KEY", ICON_UNDEFINED,
- CODE_OUTPUT_TEXT);
- assertParser("ACTION NEXT AS MORE KEY", "!TEXT/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT",
- "!TEXT/LABEL_NEXT_KEY", "!CODE/KEY_ACTION_NEXT", ICON_UNDEFINED,
- CODE_OUTPUT_TEXT);
- assertParser("POPULAR DOMAIN",
- "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN|!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
- "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN", "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
- ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE,
- null, null, ICON_UNDEFINED, mCodeSettings);
- assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|",
- null, null, mSettingsIconId, CODE_UNSPECIFIED);
- assertParser("ICON without code", ICON_SETTINGS_UPPERCASE,
- "!ICON/SETTINGS_KEY", "!ICON/SETTINGS_KEY", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
- assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c",
- "a", null, ICON_UNDEFINED, mCodeSettings);
- assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c",
- null, null, mSettingsIconId, CODE_UNSPECIFIED);
- assertParserError("Multiple bar with ICON and CODE",
- ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c",
- null, null, mSettingsIconId, mCodeSettings);
- }
-
- private static void assertArrayEquals(String message, Object[] expected, Object[] actual) {
- if (expected == actual) {
- return;
- }
- if (expected == null || actual == null) {
- assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
- return;
- }
- if (expected.length != actual.length) {
- assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual));
- return;
- }
- for (int i = 0; i < expected.length; i++) {
- assertEquals(message + " [" + i + "]",
- Arrays.toString(expected), Arrays.toString(actual));
- }
- }
-
- private static void assertInsertAdditionalMoreKeys(String message, String[] moreKeys,
- String[] additionalMoreKeys, String[] expected) {
- final String[] actual =
- KeySpecParser.insertAdditionalMoreKeys( moreKeys, additionalMoreKeys);
- assertArrayEquals(message, expected, actual);
- }
-
- public void testEmptyEntry() {
- assertInsertAdditionalMoreKeys("null more keys and null additons",
- null,
- null,
- null);
- assertInsertAdditionalMoreKeys("null more keys and empty additons",
- null,
- new String[0],
- null);
- assertInsertAdditionalMoreKeys("empty more keys and null additons",
- new String[0],
- null,
- null);
- assertInsertAdditionalMoreKeys("empty more keys and empty additons",
- new String[0],
- new String[0],
- null);
-
- assertInsertAdditionalMoreKeys("filter out empty more keys",
- new String[] { null, "a", "", "b", null },
- null,
- new String[] { "a", "b" });
- assertInsertAdditionalMoreKeys("filter out empty additons",
- new String[] { "a", "%", "b", "%", "c", "%", "d" },
- new String[] { null, "A", "", "B", null },
- new String[] { "a", "A", "b", "B", "c", "d" });
- }
-
- public void testInsertAdditionalMoreKeys() {
- // Escaped marker.
- assertInsertAdditionalMoreKeys("escaped marker",
- new String[] { "\\%", "%-)" },
- new String[] { "1", "2" },
- new String[] { "1", "2", "\\%", "%-)" });
-
- // 0 more key.
- assertInsertAdditionalMoreKeys("null & null", null, null, null);
- assertInsertAdditionalMoreKeys("null & 1 additon",
- null,
- new String[] { "1" },
- new String[] { "1" });
- assertInsertAdditionalMoreKeys("null & 2 additons",
- null,
- new String[] { "1", "2" },
- new String[] { "1", "2" });
-
- // 0 additional more key.
- assertInsertAdditionalMoreKeys("1 more key & null",
- new String[] { "A" },
- null,
- new String[] { "A" });
- assertInsertAdditionalMoreKeys("2 more keys & null",
- new String[] { "A", "B" },
- null,
- new String[] { "A", "B" });
-
- // No marker.
- assertInsertAdditionalMoreKeys("1 more key & 1 addtional & no marker",
- new String[] { "A" },
- new String[] { "1" },
- new String[] { "1", "A" });
- assertInsertAdditionalMoreKeys("1 more key & 2 addtionals & no marker",
- new String[] { "A" },
- new String[] { "1", "2" },
- new String[] { "1", "2", "A" });
- assertInsertAdditionalMoreKeys("2 more keys & 1 addtional & no marker",
- new String[] { "A", "B" },
- new String[] { "1" },
- new String[] { "1", "A", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 addtionals & no marker",
- new String[] { "A", "B" },
- new String[] { "1", "2" },
- new String[] { "1", "2", "A", "B" });
-
- // 1 marker.
- assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at head",
- new String[] { "%", "A" },
- new String[] { "1" },
- new String[] { "1", "A" });
- assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at tail",
- new String[] { "A", "%" },
- new String[] { "1" },
- new String[] { "A", "1" });
- assertInsertAdditionalMoreKeys("2 more keys & 1 additon & marker at middle",
- new String[] { "A", "%", "B" },
- new String[] { "1" },
- new String[] { "A", "1", "B" });
-
- // 1 marker & excess additional more keys.
- assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at head",
- new String[] { "%", "A", "B" },
- new String[] { "1", "2" },
- new String[] { "1", "A", "B", "2" });
- assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at tail",
- new String[] { "A", "B", "%" },
- new String[] { "1", "2" },
- new String[] { "A", "B", "1", "2" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & marker at middle",
- new String[] { "A", "%", "B" },
- new String[] { "1", "2" },
- new String[] { "A", "1", "B", "2" });
-
- // 2 markers.
- assertInsertAdditionalMoreKeys("0 more key & 2 addtional & 2 markers",
- new String[] { "%", "%" },
- new String[] { "1", "2" },
- new String[] { "1", "2" });
- assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at head",
- new String[] { "%", "%", "A" },
- new String[] { "1", "2" },
- new String[] { "1", "2", "A" });
- assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at tail",
- new String[] { "A", "%", "%" },
- new String[] { "1", "2" },
- new String[] { "A", "1", "2" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle",
- new String[] { "A", "%", "%", "B" },
- new String[] { "1", "2" },
- new String[] { "A", "1", "2", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle",
- new String[] { "%", "A", "%", "B" },
- new String[] { "1", "2" },
- new String[] { "1", "A", "2", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail",
- new String[] { "%", "A", "B", "%" },
- new String[] { "1", "2" },
- new String[] { "1", "A", "B", "2" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail",
- new String[] { "A", "%", "B", "%" },
- new String[] { "1", "2" },
- new String[] { "A", "1", "B", "2" });
-
- // 2 markers & excess additional more keys.
- assertInsertAdditionalMoreKeys("0 more key & 2 additons & 2 markers",
- new String[] { "%", "%" },
- new String[] { "1", "2", "3" },
- new String[] { "1", "2", "3" });
- assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at head",
- new String[] { "%", "%", "A" },
- new String[] { "1", "2", "3" },
- new String[] { "1", "2", "A", "3" });
- assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at tail",
- new String[] { "A", "%", "%" },
- new String[] { "1", "2", "3" },
- new String[] { "A", "1", "2", "3" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle",
- new String[] { "A", "%", "%", "B" },
- new String[] { "1", "2", "3" },
- new String[] { "A", "1", "2", "B", "3" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & middle",
- new String[] { "%", "A", "%", "B" },
- new String[] { "1", "2", "3" },
- new String[] { "1", "A", "2", "B", "3" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & tail",
- new String[] { "%", "A", "B", "%" },
- new String[] { "1", "2", "3" },
- new String[] { "1", "A", "B", "2", "3" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail",
- new String[] { "A", "%", "B", "%" },
- new String[] { "1", "2", "3" },
- new String[] { "A", "1", "B", "2", "3" });
-
- // 0 addtional more key and excess markers.
- assertInsertAdditionalMoreKeys("0 more key & null & excess marker",
- new String[] { "%" },
- null,
- null);
- assertInsertAdditionalMoreKeys("1 more key & null & excess marker at head",
- new String[] { "%", "A" },
- null,
- new String[] { "A" });
- assertInsertAdditionalMoreKeys("1 more key & null & excess marker at tail",
- new String[] { "A", "%" },
- null,
- new String[] { "A" });
- assertInsertAdditionalMoreKeys("2 more keys & null & excess marker at middle",
- new String[] { "A", "%", "B" },
- null,
- new String[] { "A", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & null & excess markers",
- new String[] { "%", "A", "%", "B", "%" },
- null,
- new String[] { "A", "B" });
-
- // Excess markers.
- assertInsertAdditionalMoreKeys("0 more key & 1 additon & excess marker",
- new String[] { "%", "%" },
- new String[] { "1" },
- new String[] { "1" });
- assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at head",
- new String[] { "%", "%", "A" },
- new String[] { "1" },
- new String[] { "1", "A" });
- assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at tail",
- new String[] { "A", "%", "%" },
- new String[] { "1" },
- new String[] { "A", "1" });
- assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess marker at middle",
- new String[] { "A", "%", "%", "B" },
- new String[] { "1" },
- new String[] { "A", "1", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess markers",
- new String[] { "%", "A", "%", "B", "%" },
- new String[] { "1" },
- new String[] { "1", "A", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 2 additons & excess markers",
- new String[] { "%", "A", "%", "B", "%" },
- new String[] { "1", "2" },
- new String[] { "1", "A", "2", "B" });
- assertInsertAdditionalMoreKeys("2 more keys & 3 additons & excess markers",
- new String[] { "%", "A", "%", "%", "B", "%" },
- new String[] { "1", "2", "3" },
- new String[] { "1", "A", "2", "3", "B" });
- }
-
- private static final String HAS_LABEL = "!hasLabel!";
- private static final String NEEDS_DIVIDER = "!needsDividers!";
- private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!";
- private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
-
- private static void assertGetBooleanValue(String message, String key, String[] moreKeys,
- String[] expected, boolean expectedValue) {
- final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
- final boolean actualValue = KeySpecParser.getBooleanValue(actual, key);
- assertEquals(message + " [value]", expectedValue, actualValue);
- assertArrayEquals(message, expected, actual);
- }
-
- public void testGetBooleanValue() {
- assertGetBooleanValue("Has label", HAS_LABEL,
- new String[] { HAS_LABEL, "a", "b", "c" },
- new String[] { null, "a", "b", "c" }, true);
- // Upper case specification will not work.
- assertGetBooleanValue("HAS LABEL", HAS_LABEL,
- new String[] { HAS_LABEL.toUpperCase(Locale.ROOT), "a", "b", "c" },
- new String[] { "!HASLABEL!", "a", "b", "c" }, false);
-
- assertGetBooleanValue("No has label", HAS_LABEL,
- new String[] { "a", "b", "c" },
- new String[] { "a", "b", "c" }, false);
- assertGetBooleanValue("No has label with fixed clumn order", HAS_LABEL,
- new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
- new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, false);
-
- // Upper case specification will not work.
- assertGetBooleanValue("Multiple has label", HAS_LABEL,
- new String[] {
- "a", HAS_LABEL.toUpperCase(Locale.ROOT), "b", "c", HAS_LABEL, "d" },
- new String[] {
- "a", "!HASLABEL!", "b", "c", null, "d" }, true);
- // Upper case specification will not work.
- assertGetBooleanValue("Multiple has label with needs dividers", HAS_LABEL,
- new String[] {
- "a", HAS_LABEL, "b", NEEDS_DIVIDER, HAS_LABEL.toUpperCase(Locale.ROOT), "d" },
- new String[] {
- "a", null, "b", NEEDS_DIVIDER, "!HASLABEL!", "d" }, true);
- }
-
- private static void assertGetIntValue(String message, String key, int defaultValue,
- String[] moreKeys, String[] expected, int expectedValue) {
- final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
- final int actualValue = KeySpecParser.getIntValue(actual, key, defaultValue);
- assertEquals(message + " [value]", expectedValue, actualValue);
- assertArrayEquals(message, expected, actual);
- }
-
- public void testGetIntValue() {
- assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1,
- new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
- new String[] { null, "a", "b", "c" }, 3);
- // Upper case specification will not work.
- assertGetIntValue("FIXED COLUMN ORDER 3", FIXED_COLUMN_ORDER, -1,
- new String[] { FIXED_COLUMN_ORDER.toUpperCase(Locale.ROOT) + "3", "a", "b", "c" },
- new String[] { "!FIXEDCOLUMNORDER!3", "a", "b", "c" }, -1);
-
- assertGetIntValue("No fixed column order", FIXED_COLUMN_ORDER, -1,
- new String[] { "a", "b", "c" },
- new String[] { "a", "b", "c" }, -1);
- assertGetIntValue("No fixed column order with auto column order", FIXED_COLUMN_ORDER, -1,
- new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" },
- new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, -1);
-
- assertGetIntValue("Multiple fixed column order 3,5", FIXED_COLUMN_ORDER, -1,
- new String[] { FIXED_COLUMN_ORDER + "3", "a", FIXED_COLUMN_ORDER + "5", "b" },
- new String[] { null, "a", null, "b" }, 3);
- // Upper case specification will not work.
- assertGetIntValue("Multiple fixed column order 5,3 with has label", FIXED_COLUMN_ORDER, -1,
- new String[] {
- FIXED_COLUMN_ORDER.toUpperCase(Locale.ROOT) + "5", HAS_LABEL, "a",
- FIXED_COLUMN_ORDER + "3", "b" },
- new String[] { "!FIXEDCOLUMNORDER!5", HAS_LABEL, "a", null, "b" }, 3);
}
}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
new file mode 100644
index 000000000..b8cb11b6b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import static com.android.inputmethod.keyboard.internal.KeyboardCodesSet.PREFIX_CODE;
+import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
+import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.PREFIX_ICON;
+import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
+import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
+
+import android.test.AndroidTestCase;
+
+import java.util.Locale;
+
+abstract class KeySpecParserTestsBase extends AndroidTestCase {
+ private final static Locale TEST_LOCALE = Locale.ENGLISH;
+ protected final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
+
+ private static final String CODE_SETTINGS_NAME = "key_settings";
+ private static final String CODE_SETTINGS = PREFIX_CODE + CODE_SETTINGS_NAME;
+ private static final String ICON_SETTINGS_NAME = "settings_key";
+ private static final String ICON_SETTINGS = PREFIX_ICON + ICON_SETTINGS_NAME;
+ private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase(Locale.ROOT);
+ private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase(Locale.ROOT);
+ private static final String CODE_NON_EXISTING = PREFIX_CODE + "non_existing";
+ private static final String ICON_NON_EXISTING = PREFIX_ICON + "non_existing";
+
+ private int mCodeSettings;
+ private int mCodeActionNext;
+ private int mSettingsIconId;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mTextsSet.setLocale(TEST_LOCALE, getContext());
+ mCodeSettings = KeyboardCodesSet.getCode(CODE_SETTINGS_NAME);
+ mCodeActionNext = KeyboardCodesSet.getCode("key_action_next");
+ mSettingsIconId = KeyboardIconsSet.getIconId(ICON_SETTINGS_NAME);
+ }
+
+ abstract protected void assertParser(final String message, final String keySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIcon,
+ final int expectedCode);
+
+ protected void assertParserError(final String message, final String keySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIconId,
+ final int expectedCode) {
+ try {
+ assertParser(message, keySpec, expectedLabel, expectedOutputText, expectedIconId,
+ expectedCode);
+ fail(message);
+ } catch (Exception pcpe) {
+ // success.
+ }
+ }
+
+ // \U001d11e: MUSICAL SYMBOL G CLEF
+ private static final String SURROGATE_PAIR1 = "\ud834\udd1e";
+ private static final int SURROGATE_CODE1 = SURROGATE_PAIR1.codePointAt(0);
+ // \U001d122: MUSICAL SYMBOL F CLEF
+ private static final String SURROGATE_PAIR2 = "\ud834\udd22";
+ private static final int SURROGATE_CODE2 = SURROGATE_PAIR2.codePointAt(0);
+ // \U002f8a6: CJK COMPATIBILITY IDEOGRAPH-2F8A6; variant character of \u6148.
+ private static final String SURROGATE_PAIR3 = "\ud87e\udca6";
+ private static final String SURROGATE_PAIRS4 = SURROGATE_PAIR1 + SURROGATE_PAIR2;
+ private static final String SURROGATE_PAIRS5 = SURROGATE_PAIRS4 + SURROGATE_PAIR3;
+
+ public void testSingleLetter() {
+ assertParser("Single letter", "a",
+ "a", null, ICON_UNDEFINED, 'a');
+ assertParser("Single surrogate", SURROGATE_PAIR1,
+ SURROGATE_PAIR1, null, ICON_UNDEFINED, SURROGATE_CODE1);
+ assertParser("Sole vertical bar", "|",
+ "|", null, ICON_UNDEFINED, '|');
+ assertParser("Single escaped vertical bar", "\\|",
+ "|", null, ICON_UNDEFINED, '|');
+ assertParser("Single escaped escape", "\\\\",
+ "\\", null, ICON_UNDEFINED, '\\');
+ assertParser("Single comma", ",",
+ ",", null, ICON_UNDEFINED, ',');
+ assertParser("Single escaped comma", "\\,",
+ ",", null, ICON_UNDEFINED, ',');
+ assertParser("Single escaped letter", "\\a",
+ "a", null, ICON_UNDEFINED, 'a');
+ assertParser("Single escaped surrogate", "\\" + SURROGATE_PAIR2,
+ SURROGATE_PAIR2, null, ICON_UNDEFINED, SURROGATE_CODE2);
+ assertParser("Single bang", "!",
+ "!", null, ICON_UNDEFINED, '!');
+ assertParser("Single escaped bang", "\\!",
+ "!", null, ICON_UNDEFINED, '!');
+ assertParser("Single output text letter", "a|a",
+ "a", null, ICON_UNDEFINED, 'a');
+ assertParser("Single surrogate pair outputText", "G Clef|" + SURROGATE_PAIR1,
+ "G Clef", null, ICON_UNDEFINED, SURROGATE_CODE1);
+ assertParser("Single letter with outputText", "a|abc",
+ "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with surrogate outputText", "a|" + SURROGATE_PAIRS4,
+ "a", SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single surrogate with outputText", SURROGATE_PAIR3 + "|abc",
+ SURROGATE_PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with escaped outputText", "a|a\\|c",
+ "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with escaped surrogate outputText",
+ "a|" + SURROGATE_PAIR1 + "\\|" + SURROGATE_PAIR2,
+ "a", SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with comma outputText", "a|a,b",
+ "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with escaped comma outputText", "a|a\\,b",
+ "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with outputText starts with bang", "a|!bc",
+ "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with surrogate outputText starts with bang",
+ "a|!" + SURROGATE_PAIRS5,
+ "a", "!" + SURROGATE_PAIRS5, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with outputText contains bang", "a|a!c",
+ "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single letter with escaped bang outputText", "a|\\!bc",
+ "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Single escaped escape with single outputText", "\\\\|\\\\",
+ "\\", null, ICON_UNDEFINED, '\\');
+ assertParser("Single escaped bar with single outputText", "\\||\\|",
+ "|", null, ICON_UNDEFINED, '|');
+ assertParser("Single letter with code", "a|" + CODE_SETTINGS,
+ "a", null, ICON_UNDEFINED, mCodeSettings);
+ }
+
+ public void testLabel() {
+ assertParser("Simple label", "abc",
+ "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Simple surrogate label", SURROGATE_PAIRS4,
+ SURROGATE_PAIRS4, SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped bar", "a\\|c",
+ "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Surrogate label with escaped bar", SURROGATE_PAIR1 + "\\|" + SURROGATE_PAIR2,
+ SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2, SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2,
+ ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped escape", "a\\\\c",
+ "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with comma", "a,c",
+ "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped comma", "a\\,c",
+ "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label starts with bang", "!bc",
+ "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Surrogate label starts with bang", "!" + SURROGATE_PAIRS4,
+ "!" + SURROGATE_PAIRS4, "!" + SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label contains bang", "a!c",
+ "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped bang", "\\!bc",
+ "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped letter", "\\abc",
+ "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with outputText", "abc|def",
+ "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with comma and outputText", "a,c|def",
+ "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped comma label with outputText", "a\\,c|def",
+ "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped label with outputText", "a\\|c|def",
+ "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped bar outputText", "abc|d\\|f",
+ "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped escape label with outputText", "a\\\\|def",
+ "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label starts with bang and outputText", "!bc|def",
+ "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label contains bang label and outputText", "a!c|def",
+ "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped bang label with outputText", "\\!bc|def",
+ "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with comma outputText", "abc|a,b",
+ "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped comma outputText", "abc|a\\,b",
+ "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with outputText starts with bang", "abc|!bc",
+ "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with outputText contains bang", "abc|a!c",
+ "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped bang outputText", "abc|\\!bc",
+ "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with escaped bar outputText", "abc|d\\|f",
+ "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f",
+ "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with code", "abc|" + CODE_SETTINGS,
+ "abc", null, ICON_UNDEFINED, mCodeSettings);
+ assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS,
+ "a|c", null, ICON_UNDEFINED, mCodeSettings);
+ }
+
+ public void testCodes() {
+ assertParser("Hexadecimal code", "a|0x1000",
+ "a", null, ICON_UNDEFINED, 0x1000);
+ assertParserError("Illegal hexadecimal code", "a|0x100X",
+ "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParser("Escaped hexadecimal code 1", "a|\\0x1000",
+ "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped hexadecimal code 2", "a|0\\x1000",
+ "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped hexadecimal code 2", "a|0\\x1000",
+ "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParserError("Illegally escaped hexadecimal code", "a|0x1\\000",
+ "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ // This is a workaround to have a key that has a supplementary code point. We can't put a
+ // string in resource as a XML entity of a supplementary code point or a surrogate pair.
+ // TODO: Should pass this test.
+// assertParser("Hexadecimal supplementary code", String.format("a|0x%06x", SURROGATE_CODE2),
+// SURROGATE_PAIR2, null, ICON_UNDEFINED, SURROGATE_CODE2);
+ assertParser("Zero is treated as output text", "a|0",
+ "a", null, ICON_UNDEFINED, '0');
+ assertParser("Digit is treated as output text", "a|3",
+ "a", null, ICON_UNDEFINED, '3');
+ assertParser("Decimal number is treated as an output text", "a|2014",
+ "a", "2014", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ }
+
+ public void testIcons() {
+ assertParser("Icon with single letter", ICON_SETTINGS + "|a",
+ null, null, mSettingsIconId, 'a');
+ assertParser("Icon with outputText", ICON_SETTINGS + "|abc",
+ null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT);
+ assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc",
+ null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
+ assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c",
+ null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT);
+ assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc",
+ null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
+ assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS,
+ "!bc", null, ICON_UNDEFINED, mCodeSettings);
+ assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS,
+ "a!c", null, ICON_UNDEFINED, mCodeSettings);
+ assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS,
+ "!bc", null, ICON_UNDEFINED, mCodeSettings);
+ assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS,
+ null, null, mSettingsIconId, mCodeSettings);
+ }
+
+ public void testResourceReference() {
+ assertParser("Settings as more key", "!text/keyspec_settings",
+ null, null, mSettingsIconId, mCodeSettings);
+
+ assertParser("Action next as more key", "!text/label_next_key|!code/key_action_next",
+ "Next", null, ICON_UNDEFINED, mCodeActionNext);
+
+ assertParser("Popular domain",
+ "!text/keyspec_popular_domain|!text/keyspec_popular_domain ",
+ ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ }
+
+ public void testFormatError() {
+ assertParserError("Empty label with outputText", "|a",
+ null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Empty label with code", "|" + CODE_SETTINGS,
+ null, null, ICON_UNDEFINED, mCodeSettings);
+ assertParserError("Empty outputText with label", "a|",
+ "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Empty outputText with icon", ICON_SETTINGS + "|",
+ null, null, mSettingsIconId, CODE_UNSPECIFIED);
+ assertParserError("Icon without code", ICON_SETTINGS,
+ null, null, mSettingsIconId, CODE_UNSPECIFIED);
+ assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc",
+ null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING,
+ "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Third bar at end", "a|b|",
+ "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Multiple bar", "a|b|c",
+ "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c",
+ "a", null, ICON_UNDEFINED, mCodeSettings);
+ assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c",
+ null, null, mSettingsIconId, CODE_UNSPECIFIED);
+ assertParserError("Multiple bar with icon and code",
+ ICON_SETTINGS + "|" + CODE_SETTINGS + "|c",
+ null, null, mSettingsIconId, mCodeSettings);
+ }
+
+ public void testUselessUpperCaseSpecifier() {
+ assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE,
+ "a", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE,
+ "abc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE,
+ "a|c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc",
+ "!ICON/SETTINGS_KEY", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc",
+ "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c",
+ "!ICON/SETTINGS_KEY", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc",
+ "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE,
+ "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE,
+ "a!c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE,
+ "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE,
+ "!ICON/SETTINGS_KEY", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParser("SETTINGS AS MORE KEY", "!TEXT/SETTINGS_AS_MORE_KEY",
+ "!TEXT/SETTINGS_AS_MORE_KEY", "!TEXT/SETTINGS_AS_MORE_KEY", ICON_UNDEFINED,
+ CODE_OUTPUT_TEXT);
+ assertParser("ACTION NEXT AS MORE KEY", "!TEXT/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT",
+ "!TEXT/LABEL_NEXT_KEY", "!CODE/KEY_ACTION_NEXT", ICON_UNDEFINED,
+ CODE_OUTPUT_TEXT);
+ assertParser("POPULAR DOMAIN",
+ "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN|!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
+ "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN", "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
+ ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE,
+ null, null, ICON_UNDEFINED, mCodeSettings);
+ assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|",
+ null, null, mSettingsIconId, CODE_UNSPECIFIED);
+ assertParser("ICON without code", ICON_SETTINGS_UPPERCASE,
+ "!ICON/SETTINGS_KEY", "!ICON/SETTINGS_KEY", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+ assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c",
+ "a", null, ICON_UNDEFINED, mCodeSettings);
+ assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c",
+ null, null, mSettingsIconId, CODE_UNSPECIFIED);
+ assertParserError("Multiple bar with ICON and CODE",
+ ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c",
+ null, null, mSettingsIconId, mCodeSettings);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java
new file mode 100644
index 000000000..72211015f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+public final class KeyboardTextsSetTests extends AndroidTestCase {
+ // All input method subtypes of LatinIME.
+ private List<InputMethodSubtype> mAllSubtypesList;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ RichInputMethodManager.init(getContext());
+ final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+
+ final ArrayList<InputMethodSubtype> allSubtypesList = new ArrayList<>();
+ final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme();
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int index = 0; index < subtypeCount; index++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ allSubtypesList.add(subtype);
+ }
+ mAllSubtypesList = Collections.unmodifiableList(allSubtypesList);
+ }
+
+ // Test that the text {@link KeyboardTextsSet#SWITCH_TO_ALPHA_KEY_LABEL} exists for all
+ // subtypes. The text is needed to implement Emoji Keyboard, see
+ // {@link KeyboardSwitcher#setEmojiKeyboard()}.
+ public void testSwitchToAlphaKeyLabel() {
+ final Context context = getContext();
+ final KeyboardTextsSet textsSet = new KeyboardTextsSet();
+ for (final InputMethodSubtype subtype : mAllSubtypesList) {
+ final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ textsSet.setLocale(locale, context);
+ final String switchToAlphaKeyLabel = textsSet.getText(
+ KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL);
+ assertNotNull("Switch to alpha key label of " + locale, switchToAlphaKeyLabel);
+ assertFalse("Switch to alpha key label of " + locale, switchToAlphaKeyLabel.isEmpty());
+ }
+ }
+
+ private static final String[] TEXT_NAMES_FROM_RESOURCE = {
+ // Labels for action.
+ "label_go_key",
+ "label_send_key",
+ "label_next_key",
+ "label_done_key",
+ "label_previous_key",
+ // Other labels.
+ "label_pause_key",
+ "label_wait_key",
+ };
+
+ // Test that the text from resources are correctly loaded for all subtypes.
+ public void testTextFromResources() {
+ final Context context = getContext();
+ final KeyboardTextsSet textsSet = new KeyboardTextsSet();
+ for (final InputMethodSubtype subtype : mAllSubtypesList) {
+ final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ textsSet.setLocale(locale, context);
+ for (final String name : TEXT_NAMES_FROM_RESOURCE) {
+ final String text = textsSet.getText(name);
+ assertNotNull(name + " of " + locale, text);
+ assertFalse(name + " of " + locale, text.isEmpty());
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
new file mode 100644
index 000000000..6ea27588e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import static com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper.FORMAT_TYPE_FULL_LOCALE;
+import static com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper.FORMAT_TYPE_LANGUAGE_ONLY;
+import static com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper.FORMAT_TYPE_NONE;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
+ private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper =
+ new LanguageOnSpacebarHelper();
+
+ private RichInputMethodManager mRichImm;
+
+ InputMethodSubtype EN_US_QWERTY;
+ InputMethodSubtype EN_GB_QWERTY;
+ InputMethodSubtype FR_AZERTY;
+ InputMethodSubtype FR_CA_QWERTY;
+ InputMethodSubtype FR_CH_SWISS;
+ InputMethodSubtype FR_CH_QWERTY;
+ InputMethodSubtype FR_CH_QWERTZ;
+ InputMethodSubtype ZZ_QWERTY;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Context context = getContext();
+ RichInputMethodManager.init(context);
+ mRichImm = RichInputMethodManager.getInstance();
+ SubtypeLocaleUtils.init(context);
+
+ EN_US_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.US.toString(), "qwerty");
+ EN_GB_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.UK.toString(), "qwerty");
+ FR_AZERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.FRENCH.toString(), "azerty");
+ FR_CA_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.CANADA_FRENCH.toString(), "qwerty");
+ FR_CH_SWISS = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "fr_CH", "swiss");
+ FR_CH_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ "fr_CH", "qwertz");
+ FR_CH_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ "fr_CH", "qwerty");
+ ZZ_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
+ }
+
+ private static List<InputMethodSubtype> asList(final InputMethodSubtype ... subtypes) {
+ return Arrays.asList(subtypes);
+ }
+
+ public void testOneSubtype() {
+ mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY));
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+ assertEquals("one same English (US)", FORMAT_TYPE_NONE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("one same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+
+ mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(FR_AZERTY));
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+ assertEquals("one diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("one diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ }
+
+ public void testTwoSubtypes() {
+ mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY, FR_AZERTY));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+ assertEquals("two same English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("two same French)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
+ assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+ assertEquals("two diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("two diff French", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
+ assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ }
+
+ public void testSameLanuageSubtypes() {
+ mLanguageOnSpacebarHelper.updateEnabledSubtypes(
+ asList(EN_US_QWERTY, EN_GB_QWERTY, FR_AZERTY, ZZ_QWERTY));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+ assertEquals("two same English (US)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("two same English (UK)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
+ assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+ assertEquals("two diff English (US)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
+ assertEquals("two diff English (UK)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
+ assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ }
+
+ public void testMultiSameLanuageSubtypes() {
+ mLanguageOnSpacebarHelper.updateEnabledSubtypes(
+ asList(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTY, FR_CH_QWERTZ));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+ assertEquals("multi same French", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
+ assertEquals("multi same French (CA)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
+ assertEquals("multi same French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
+ assertEquals("multi same French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
+ assertEquals("multi same French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
+
+ mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+ assertEquals("multi diff French", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
+ assertEquals("multi diff French (CA)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
+ assertEquals("multi diff French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
+ assertEquals("multi diff French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
+ assertEquals("multi diff French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index 6e3e37add..a353e5a35 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -125,8 +125,9 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
}
@Override
- public void requestUpdatingShiftState() {
- mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
+ public void requestUpdatingShiftState(final int currentAutoCapsState,
+ final int currentRecapitalizeState) {
+ mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
}
@Override
@@ -149,7 +150,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void loadKeyboard() {
- mState.onLoadKeyboard();
+ mState.onLoadKeyboard(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
}
public void saveKeyboardState() {
@@ -157,11 +158,17 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void onPressKey(final int code, final boolean isSinglePointer) {
- mState.onPressKey(code, isSinglePointer, mAutoCapsState);
+ mState.onPressKey(code, isSinglePointer, mAutoCapsState,
+ RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
}
public void onReleaseKey(final int code, final boolean withSliding) {
- mState.onReleaseKey(code, withSliding);
+ onReleaseKey(code, withSliding, mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
+ }
+
+ public void onReleaseKey(final int code, final boolean withSliding,
+ final int currentAutoCapsState, final int currentRecapitalizeState) {
+ mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState);
if (mLongPressTimeoutCode == code) {
mLongPressTimeoutCode = 0;
}
@@ -176,10 +183,10 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
} else {
mAutoCapsState = mAutoCapsMode;
}
- mState.onCodeInput(code, mAutoCapsState);
+ mState.onCodeInput(code, mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
}
public void onFinishSlidingInput() {
- mState.onFinishSlidingInput();
+ mState.onFinishSlidingInput(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
}
}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java
index 2eb448c82..29b169d80 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java
@@ -22,16 +22,13 @@ import android.content.res.Resources;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.RunInLocale;
-
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@MediumTest
-public class KeySpecParserSplitTests extends InstrumentationTestCase {
+public class MoreKeySpecSplitTests extends InstrumentationTestCase {
private static final Locale TEST_LOCALE = Locale.ENGLISH;
final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@@ -41,24 +38,20 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
final Instrumentation instrumentation = getInstrumentation();
final Context targetContext = instrumentation.getTargetContext();
- mTextsSet.setLanguage(TEST_LOCALE.getLanguage());
- new RunInLocale<Void>() {
- @Override
- protected Void job(final Resources res) {
- mTextsSet.loadStringResources(targetContext);
- return null;
- }
- }.runInLocale(targetContext.getResources(), TEST_LOCALE);
+ mTextsSet.setLocale(TEST_LOCALE, targetContext);
final String[] testResourceNames = getAllResourceIdNames(
com.android.inputmethod.latin.tests.R.string.class);
- mTextsSet.loadStringResourcesInternal(instrumentation.getContext(), testResourceNames,
+ final Context testContext = instrumentation.getContext();
+ final Resources testRes = testContext.getResources();
+ final String testResPackageName = testRes.getResourcePackageName(
// This dummy raw resource is needed to be able to load string resources from a test
// APK successfully.
com.android.inputmethod.latin.tests.R.raw.dummy_resource_for_testing);
+ mTextsSet.loadStringResourcesInternal(testRes, testResourceNames, testResPackageName);
}
private static String[] getAllResourceIdNames(final Class<?> resourceIdClass) {
- final ArrayList<String> names = CollectionUtils.newArrayList();
+ final ArrayList<String> names = new ArrayList<>();
for (final Field field : resourceIdClass.getFields()) {
if (field.getType() == Integer.TYPE) {
names.add(field.getName());
@@ -92,8 +85,8 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private void assertTextArray(final String message, final String value,
final String ... expectedArray) {
- final String resolvedActual = KeySpecParser.resolveTextReference(value, mTextsSet);
- final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual);
+ final String resolvedActual = mTextsSet.resolveTextReference(value);
+ final String[] actual = MoreKeySpec.splitKeySpecs(resolvedActual);
final String[] expected = (expectedArray.length == 0) ? null : expectedArray;
assertArrayEquals(message, expected, actual);
}
@@ -116,6 +109,14 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private static final String SURROGATE1 = PAIR1 + PAIR2;
private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
+ public void testResolveNullText() {
+ assertNull("resolve null", mTextsSet.resolveTextReference(null));
+ }
+
+ public void testResolveEmptyText() {
+ assertNull("resolve empty text", mTextsSet.resolveTextReference("!text/empty_string"));
+ }
+
public void testSplitZero() {
assertTextArray("Empty string", "");
assertTextArray("Empty entry", ",");
@@ -352,16 +353,16 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
}
public void testLabelReferece() {
- assertTextArray("Label time am", "!text/label_time_am", "AM");
+ assertTextArray("Label time am", "!text/keylabel_time_am", "AM");
- assertTextArray("More keys for am pm", "!text/more_keys_for_am_pm",
+ assertTextArray("More keys for am pm", "!text/morekeys_am_pm",
"!fixedColumnOrder!2", "!hasLabels!", "AM", "PM");
- assertTextArray("Settings as more key", "!text/settings_as_more_key",
+ assertTextArray("Settings as more key", "!text/keyspec_settings",
"!icon/settings_key|!code/key_settings");
assertTextArray("Indirect naviagte actions as more key",
- "!text/indirect_navigate_actions_as_more_key",
+ "!text/keyspec_indirect_navigate_actions",
"!fixedColumnOrder!2",
"!hasLabels!", "Prev|!code/key_action_previous",
"!hasLabels!", "Next|!code/key_action_next");
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
new file mode 100644
index 000000000..6c0d74941
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
+import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+@SmallTest
+public final class MoreKeySpecTests extends KeySpecParserTestsBase {
+ @Override
+ protected void assertParser(final String message, final String moreKeySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIconId,
+ final int expectedCode) {
+ final String labelResolved = mTextsSet.resolveTextReference(moreKeySpec);
+ final MoreKeySpec spec = new MoreKeySpec(
+ labelResolved, false /* needsToUpperCase */, Locale.US);
+ assertEquals(message + " [label]", expectedLabel, spec.mLabel);
+ assertEquals(message + " [ouptputText]", expectedOutputText, spec.mOutputText);
+ assertEquals(message + " [icon]",
+ KeyboardIconsSet.getIconName(expectedIconId),
+ KeyboardIconsSet.getIconName(spec.mIconId));
+ assertEquals(message + " [code]",
+ Constants.printableCode(expectedCode),
+ Constants.printableCode(spec.mCode));
+ }
+
+ // TODO: Move this method to {@link KeySpecParserBase}.
+ public void testEmptySpec() {
+ assertParserError("Null spec", null,
+ null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ assertParserError("Empty spec", "",
+ null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+ }
+
+ private static void assertArrayEquals(final String message, final Object[] expected,
+ final Object[] actual) {
+ if (expected == actual) {
+ return;
+ }
+ if (expected == null || actual == null) {
+ assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
+ return;
+ }
+ if (expected.length != actual.length) {
+ assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual));
+ return;
+ }
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(message + " [" + i + "]",
+ Arrays.toString(expected), Arrays.toString(actual));
+ }
+ }
+
+ private static void assertInsertAdditionalMoreKeys(final String message,
+ final String[] moreKeys, final String[] additionalMoreKeys, final String[] expected) {
+ final String[] actual = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
+ assertArrayEquals(message, expected, actual);
+ }
+
+ public void testEmptyEntry() {
+ assertInsertAdditionalMoreKeys("null more keys and null additons",
+ null,
+ null,
+ null);
+ assertInsertAdditionalMoreKeys("null more keys and empty additons",
+ null,
+ new String[0],
+ null);
+ assertInsertAdditionalMoreKeys("empty more keys and null additons",
+ new String[0],
+ null,
+ null);
+ assertInsertAdditionalMoreKeys("empty more keys and empty additons",
+ new String[0],
+ new String[0],
+ null);
+
+ assertInsertAdditionalMoreKeys("filter out empty more keys",
+ new String[] { null, "a", "", "b", null },
+ null,
+ new String[] { "a", "b" });
+ assertInsertAdditionalMoreKeys("filter out empty additons",
+ new String[] { "a", "%", "b", "%", "c", "%", "d" },
+ new String[] { null, "A", "", "B", null },
+ new String[] { "a", "A", "b", "B", "c", "d" });
+ }
+
+ public void testInsertAdditionalMoreKeys() {
+ // Escaped marker.
+ assertInsertAdditionalMoreKeys("escaped marker",
+ new String[] { "\\%", "%-)" },
+ new String[] { "1", "2" },
+ new String[] { "1", "2", "\\%", "%-)" });
+
+ // 0 more key.
+ assertInsertAdditionalMoreKeys("null & null", null, null, null);
+ assertInsertAdditionalMoreKeys("null & 1 additon",
+ null,
+ new String[] { "1" },
+ new String[] { "1" });
+ assertInsertAdditionalMoreKeys("null & 2 additons",
+ null,
+ new String[] { "1", "2" },
+ new String[] { "1", "2" });
+
+ // 0 additional more key.
+ assertInsertAdditionalMoreKeys("1 more key & null",
+ new String[] { "A" },
+ null,
+ new String[] { "A" });
+ assertInsertAdditionalMoreKeys("2 more keys & null",
+ new String[] { "A", "B" },
+ null,
+ new String[] { "A", "B" });
+
+ // No marker.
+ assertInsertAdditionalMoreKeys("1 more key & 1 addtional & no marker",
+ new String[] { "A" },
+ new String[] { "1" },
+ new String[] { "1", "A" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 addtionals & no marker",
+ new String[] { "A" },
+ new String[] { "1", "2" },
+ new String[] { "1", "2", "A" });
+ assertInsertAdditionalMoreKeys("2 more keys & 1 addtional & no marker",
+ new String[] { "A", "B" },
+ new String[] { "1" },
+ new String[] { "1", "A", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 addtionals & no marker",
+ new String[] { "A", "B" },
+ new String[] { "1", "2" },
+ new String[] { "1", "2", "A", "B" });
+
+ // 1 marker.
+ assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at head",
+ new String[] { "%", "A" },
+ new String[] { "1" },
+ new String[] { "1", "A" });
+ assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at tail",
+ new String[] { "A", "%" },
+ new String[] { "1" },
+ new String[] { "A", "1" });
+ assertInsertAdditionalMoreKeys("2 more keys & 1 additon & marker at middle",
+ new String[] { "A", "%", "B" },
+ new String[] { "1" },
+ new String[] { "A", "1", "B" });
+
+ // 1 marker & excess additional more keys.
+ assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at head",
+ new String[] { "%", "A", "B" },
+ new String[] { "1", "2" },
+ new String[] { "1", "A", "B", "2" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at tail",
+ new String[] { "A", "B", "%" },
+ new String[] { "1", "2" },
+ new String[] { "A", "B", "1", "2" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & marker at middle",
+ new String[] { "A", "%", "B" },
+ new String[] { "1", "2" },
+ new String[] { "A", "1", "B", "2" });
+
+ // 2 markers.
+ assertInsertAdditionalMoreKeys("0 more key & 2 addtional & 2 markers",
+ new String[] { "%", "%" },
+ new String[] { "1", "2" },
+ new String[] { "1", "2" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at head",
+ new String[] { "%", "%", "A" },
+ new String[] { "1", "2" },
+ new String[] { "1", "2", "A" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at tail",
+ new String[] { "A", "%", "%" },
+ new String[] { "1", "2" },
+ new String[] { "A", "1", "2" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle",
+ new String[] { "A", "%", "%", "B" },
+ new String[] { "1", "2" },
+ new String[] { "A", "1", "2", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle",
+ new String[] { "%", "A", "%", "B" },
+ new String[] { "1", "2" },
+ new String[] { "1", "A", "2", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail",
+ new String[] { "%", "A", "B", "%" },
+ new String[] { "1", "2" },
+ new String[] { "1", "A", "B", "2" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail",
+ new String[] { "A", "%", "B", "%" },
+ new String[] { "1", "2" },
+ new String[] { "A", "1", "B", "2" });
+
+ // 2 markers & excess additional more keys.
+ assertInsertAdditionalMoreKeys("0 more key & 2 additons & 2 markers",
+ new String[] { "%", "%" },
+ new String[] { "1", "2", "3" },
+ new String[] { "1", "2", "3" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at head",
+ new String[] { "%", "%", "A" },
+ new String[] { "1", "2", "3" },
+ new String[] { "1", "2", "A", "3" });
+ assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at tail",
+ new String[] { "A", "%", "%" },
+ new String[] { "1", "2", "3" },
+ new String[] { "A", "1", "2", "3" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle",
+ new String[] { "A", "%", "%", "B" },
+ new String[] { "1", "2", "3" },
+ new String[] { "A", "1", "2", "B", "3" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & middle",
+ new String[] { "%", "A", "%", "B" },
+ new String[] { "1", "2", "3" },
+ new String[] { "1", "A", "2", "B", "3" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & tail",
+ new String[] { "%", "A", "B", "%" },
+ new String[] { "1", "2", "3" },
+ new String[] { "1", "A", "B", "2", "3" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail",
+ new String[] { "A", "%", "B", "%" },
+ new String[] { "1", "2", "3" },
+ new String[] { "A", "1", "B", "2", "3" });
+
+ // 0 addtional more key and excess markers.
+ assertInsertAdditionalMoreKeys("0 more key & null & excess marker",
+ new String[] { "%" },
+ null,
+ null);
+ assertInsertAdditionalMoreKeys("1 more key & null & excess marker at head",
+ new String[] { "%", "A" },
+ null,
+ new String[] { "A" });
+ assertInsertAdditionalMoreKeys("1 more key & null & excess marker at tail",
+ new String[] { "A", "%" },
+ null,
+ new String[] { "A" });
+ assertInsertAdditionalMoreKeys("2 more keys & null & excess marker at middle",
+ new String[] { "A", "%", "B" },
+ null,
+ new String[] { "A", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & null & excess markers",
+ new String[] { "%", "A", "%", "B", "%" },
+ null,
+ new String[] { "A", "B" });
+
+ // Excess markers.
+ assertInsertAdditionalMoreKeys("0 more key & 1 additon & excess marker",
+ new String[] { "%", "%" },
+ new String[] { "1" },
+ new String[] { "1" });
+ assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at head",
+ new String[] { "%", "%", "A" },
+ new String[] { "1" },
+ new String[] { "1", "A" });
+ assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at tail",
+ new String[] { "A", "%", "%" },
+ new String[] { "1" },
+ new String[] { "A", "1" });
+ assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess marker at middle",
+ new String[] { "A", "%", "%", "B" },
+ new String[] { "1" },
+ new String[] { "A", "1", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess markers",
+ new String[] { "%", "A", "%", "B", "%" },
+ new String[] { "1" },
+ new String[] { "1", "A", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 2 additons & excess markers",
+ new String[] { "%", "A", "%", "B", "%" },
+ new String[] { "1", "2" },
+ new String[] { "1", "A", "2", "B" });
+ assertInsertAdditionalMoreKeys("2 more keys & 3 additons & excess markers",
+ new String[] { "%", "A", "%", "%", "B", "%" },
+ new String[] { "1", "2", "3" },
+ new String[] { "1", "A", "2", "3", "B" });
+ }
+
+ private static final String HAS_LABEL = "!hasLabel!";
+ private static final String NEEDS_DIVIDER = "!needsDividers!";
+ private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!";
+ private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
+
+ private static void assertGetBooleanValue(final String message, final String key,
+ final String[] moreKeys, final String[] expected, final boolean expectedValue) {
+ final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
+ final boolean actualValue = MoreKeySpec.getBooleanValue(actual, key);
+ assertEquals(message + " [value]", expectedValue, actualValue);
+ assertArrayEquals(message, expected, actual);
+ }
+
+ public void testGetBooleanValue() {
+ assertGetBooleanValue("Has label", HAS_LABEL,
+ new String[] { HAS_LABEL, "a", "b", "c" },
+ new String[] { null, "a", "b", "c" }, true);
+ // Upper case specification will not work.
+ assertGetBooleanValue("HAS LABEL", HAS_LABEL,
+ new String[] { HAS_LABEL.toUpperCase(Locale.ROOT), "a", "b", "c" },
+ new String[] { "!HASLABEL!", "a", "b", "c" }, false);
+
+ assertGetBooleanValue("No has label", HAS_LABEL,
+ new String[] { "a", "b", "c" },
+ new String[] { "a", "b", "c" }, false);
+ assertGetBooleanValue("No has label with fixed clumn order", HAS_LABEL,
+ new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
+ new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, false);
+
+ // Upper case specification will not work.
+ assertGetBooleanValue("Multiple has label", HAS_LABEL,
+ new String[] {
+ "a", HAS_LABEL.toUpperCase(Locale.ROOT), "b", "c", HAS_LABEL, "d" },
+ new String[] {
+ "a", "!HASLABEL!", "b", "c", null, "d" }, true);
+ // Upper case specification will not work.
+ assertGetBooleanValue("Multiple has label with needs dividers", HAS_LABEL,
+ new String[] {
+ "a", HAS_LABEL, "b", NEEDS_DIVIDER, HAS_LABEL.toUpperCase(Locale.ROOT), "d" },
+ new String[] {
+ "a", null, "b", NEEDS_DIVIDER, "!HASLABEL!", "d" }, true);
+ }
+
+ private static void assertGetIntValue(final String message, final String key,
+ final int defaultValue, final String[] moreKeys, final String[] expected,
+ final int expectedValue) {
+ final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
+ final int actualValue = MoreKeySpec.getIntValue(actual, key, defaultValue);
+ assertEquals(message + " [value]", expectedValue, actualValue);
+ assertArrayEquals(message, expected, actual);
+ }
+
+ public void testGetIntValue() {
+ assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1,
+ new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
+ new String[] { null, "a", "b", "c" }, 3);
+ // Upper case specification will not work.
+ assertGetIntValue("FIXED COLUMN ORDER 3", FIXED_COLUMN_ORDER, -1,
+ new String[] { FIXED_COLUMN_ORDER.toUpperCase(Locale.ROOT) + "3", "a", "b", "c" },
+ new String[] { "!FIXEDCOLUMNORDER!3", "a", "b", "c" }, -1);
+
+ assertGetIntValue("No fixed column order", FIXED_COLUMN_ORDER, -1,
+ new String[] { "a", "b", "c" },
+ new String[] { "a", "b", "c" }, -1);
+ assertGetIntValue("No fixed column order with auto column order", FIXED_COLUMN_ORDER, -1,
+ new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" },
+ new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, -1);
+
+ assertGetIntValue("Multiple fixed column order 3,5", FIXED_COLUMN_ORDER, -1,
+ new String[] { FIXED_COLUMN_ORDER + "3", "a", FIXED_COLUMN_ORDER + "5", "b" },
+ new String[] { null, "a", null, "b" }, 3);
+ // Upper case specification will not work.
+ assertGetIntValue("Multiple fixed column order 5,3 with has label", FIXED_COLUMN_ORDER, -1,
+ new String[] {
+ FIXED_COLUMN_ORDER.toUpperCase(Locale.ROOT) + "5", HAS_LABEL, "a",
+ FIXED_COLUMN_ORDER + "3", "b" },
+ new String[] { "!FIXEDCOLUMNORDER!5", HAS_LABEL, "a", null, "b" }, 3);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
index 279559cfe..7908b260e 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java
@@ -27,7 +27,7 @@ public class PointerTrackerQueueTests extends AndroidTestCase {
public final int mId;
public boolean mIsModifier;
- public boolean mIsInSlidingKeyInput;
+ public boolean mIsInDraggingFinger;
public long mPhantomUpEventTime = NOT_HAPPENED;
public Element(int id) {
@@ -40,8 +40,8 @@ public class PointerTrackerQueueTests extends AndroidTestCase {
}
@Override
- public boolean isInSlidingKeyInput() {
- return mIsInSlidingKeyInput;
+ public boolean isInDraggingFinger() {
+ return mIsInDraggingFinger;
}
@Override
@@ -297,19 +297,19 @@ public class PointerTrackerQueueTests extends AndroidTestCase {
assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime);
}
- public void testIsAnyInSlidingKeyInput() {
+ public void testIsAnyInDraggingFinger() {
Element.sPhantomUpCount = 0;
- assertFalse(mQueue.isAnyInSlidingKeyInput());
+ assertFalse(mQueue.isAnyInDraggingFinger());
mQueue.add(mElement1);
mQueue.add(mElement2);
mQueue.add(mElement3);
mQueue.add(mElement4);
- assertFalse(mQueue.isAnyInSlidingKeyInput());
+ assertFalse(mQueue.isAnyInDraggingFinger());
- mElement3.mIsInSlidingKeyInput = true;
- assertTrue(mQueue.isAnyInSlidingKeyInput());
+ mElement3.mIsInDraggingFinger = true;
+ assertTrue(mQueue.isAnyInDraggingFinger());
assertEquals(0, Element.sPhantomUpCount);
assertEquals(4, mQueue.size());
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java
new file mode 100644
index 000000000..fa818654e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public final class Arabic extends LayoutBase {
+ private static final String LAYOUT_NAME = "arabic";
+
+ public Arabic(final LayoutCustomizer customizer) {
+ super(customizer, ArabicSymbols.class, ArabicSymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class ArabicCustomizer extends LayoutCustomizer {
+ public ArabicCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return ARABIC_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getSymbolsKey() { return ARABIC_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey getBackToSymbolsKey() { return ARABIC_BACK_TO_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() {
+ return RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL;
+ }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() {
+ return RtlSymbols.SINGLE_ANGLE_QUOTES_LR_RTL;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ if (isPhone) {
+ // U+060C: "،" ARABIC COMMA
+ return joinKeys(key("\u060C", SETTINGS_KEY));
+ }
+ // U+060C: "،" ARABIC COMMA
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+061B: "؛" ARABIC SEMICOLON
+ return joinKeys(key("\u060C", joinMoreKeys(
+ ":", "!", "\u061F", "\u061B", "-", "\"", "'", SETTINGS_KEY)),
+ "_");
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ if (isPhone) {
+ return super.getKeysRightToSpacebar(isPhone);
+ }
+ // U+060C: "،" ARABIC COMMA
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+061B: "؛" ARABIC SEMICOLON
+ return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone)));
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return ARABIC_DIACRITICS;
+ }
+
+ // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE
+ // U+200C: ZERO WIDTH NON-JOINER
+ // U+0628: "ب" ARABIC LETTER BEH
+ // U+062C: "ج" ARABIC LETTER JEEM
+ private static final ExpectedKey ARABIC_ALPHABET_KEY = key(
+ "\u0623\u200C\u0628\u200C\u062C", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ // U+0663: "٣" ARABIC-INDIC DIGIT THREE
+ // U+0662: "٢" ARABIC-INDIC DIGIT TWO
+ // U+0661: "١" ARABIC-INDIC DIGIT ONE
+ // U+061F: "؟" ARABIC QUESTION MARK
+ private static final ExpectedKey ARABIC_SYMBOLS_KEY = key(
+ "\u0663\u0662\u0661\u061F", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ private static final ExpectedKey ARABIC_BACK_TO_SYMBOLS_KEY = key(
+ "\u0663\u0662\u0661\u061F", Constants.CODE_SHIFT);
+
+ private static final ExpectedKey[] ARABIC_DIACRITICS = {
+ // U+0655: "ٕ" ARABIC HAMZA BELOW
+ // U+0654: "ٔ" ARABIC HAMZA ABOVE
+ // U+0652: "ْ" ARABIC SUKUN
+ // U+064D: "ٍ" ARABIC KASRATAN
+ // U+064C: "ٌ" ARABIC DAMMATAN
+ // U+064B: "ً" ARABIC FATHATAN
+ // U+0651: "ّ" ARABIC SHADDA
+ // U+0656: "ٖ" ARABIC SUBSCRIPT ALEF
+ // U+0670: "ٰ" ARABIC LETTER SUPERSCRIPT ALEF
+ // U+0653: "ٓ" ARABIC MADDAH ABOVE
+ // U+0650: "ِ" ARABIC KASRA
+ // U+064F: "ُ" ARABIC DAMMA
+ // U+064E: "َ" ARABIC FATHA
+ // U+0640: "ـ" ARABIC TATWEEL
+ moreKey(" \u0655", "\u0655"), moreKey(" \u0654", "\u0654"),
+ moreKey(" \u0652", "\u0652"), moreKey(" \u064D", "\u064D"),
+ moreKey(" \u064C", "\u064C"), moreKey(" \u064B", "\u064B"),
+ moreKey(" \u0651", "\u0651"), moreKey(" \u0656", "\u0656"),
+ moreKey(" \u0670", "\u0670"), moreKey(" \u0653", "\u0653"),
+ moreKey(" \u0650", "\u0650"), moreKey(" \u064F", "\u064F"),
+ moreKey(" \u064E", "\u064E"), moreKey("\u0640\u0640\u0640", "\u0640")
+ };
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ if (isPhone) {
+ return ALPHABET_COMMON;
+ } else {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
+ builder.insertKeysAtRow(3, 2, "\u0626");
+ return builder.build();
+ }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0636: "ض" ARABIC LETTER DAD
+ // U+0661: "١" ARABIC-INDIC DIGIT ONE
+ key("\u0636", joinMoreKeys("1", "\u0661")),
+ // U+0635: "ص" ARABIC LETTER SAD
+ // U+0662: "٢" ARABIC-INDIC DIGIT TWO
+ key("\u0635", joinMoreKeys("2", "\u0662")),
+ // U+062B: "ث" ARABIC LETTER THEH
+ // U+0663: "٣" ARABIC-INDIC DIGIT THREE
+ key("\u062B", joinMoreKeys("3", "\u0663")),
+ // U+0642: "ق" ARABIC LETTER QAF
+ // U+0664: "٤" ARABIC-INDIC DIGIT FOUR
+ // U+06A8: "ڨ" ARABIC LETTER QAF WITH THREE DOTS ABOVE
+ key("\u0642", joinMoreKeys("4", "\u0664", "\u06A8")),
+ // U+0641: "ف" ARABIC LETTER FEH
+ // U+0665: "٥" ARABIC-INDIC DIGIT FIVE
+ // U+06A4: "ڤ" ARABIC LETTER VEH
+ // U+06A2: "ڢ" ARABIC LETTER FEH WITH DOT MOVED BELOW
+ // U+06A5: "ڥ" ARABIC LETTER FEH WITH THREE DOTS BELOW
+ key("\u0641", joinMoreKeys("5", "\u0665", "\u06A4", "\u06A2", "\u06A5")),
+ // U+063A: "غ" ARABIC LETTER GHAIN
+ // U+0666: "٦" ARABIC-INDIC DIGIT SIX
+ key("\u063A", joinMoreKeys("6", "\u0666")),
+ // U+0639: "ع" ARABIC LETTER AIN
+ // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
+ key("\u0639", joinMoreKeys("7", "\u0667")),
+ // U+0647: "ه" ARABIC LETTER HEH
+ // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
+ // U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
+ // U+0647 U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER
+ key("\u0647", joinMoreKeys("8", "\u0668", moreKey("\uFEEB", "\u0647\u200D"))),
+ // U+062E: "خ" ARABIC LETTER KHAH
+ // U+0669: "٩" ARABIC-INDIC DIGIT NINE
+ key("\u062E", joinMoreKeys("9", "\u0669")),
+ // U+062D: "ح" ARABIC LETTER HAH
+ // U+0660: "٠" ARABIC-INDIC DIGIT ZERO
+ key("\u062D", joinMoreKeys("0", "\u0660")),
+ // U+062C: "ج" ARABIC LETTER JEEM
+ // U+0686: "چ" ARABIC LETTER TCHEH
+ key("\u062C", moreKey("\u0686")))
+ .setKeysOfRow(2,
+ // U+0634: "ش" ARABIC LETTER SHEEN
+ // U+069C: "ڜ" ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE
+ key("\u0634", moreKey("\u069C")),
+ // U+0633: "س" ARABIC LETTER SEEN
+ "\u0633",
+ // U+064A: "ي" ARABIC LETTER YEH
+ // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
+ // U+0649: "ى" ARABIC LETTER ALEF MAKSURA
+ key("\u064A", joinMoreKeys("\u0626", "\u0649")),
+ // U+0628: "ب" ARABIC LETTER BEH
+ // U+067E: "پ" ARABIC LETTER PEH
+ key("\u0628", moreKey("\u067E")),
+ // U+0644: "ل" ARABIC LETTER LAM
+ // U+FEFB: "ﻻ" ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
+ // U+0627: "ا" ARABIC LETTER ALEF
+ // U+FEF7: "ﻷ" ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM
+ // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE
+ // U+FEF9: "ﻹ" ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM
+ // U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW
+ // U+FEF5: "ﻵ" ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM
+ // U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
+ key("\u0644",
+ moreKey("\uFEFB", "\u0644\u0627"), moreKey("\uFEF7", "\u0644\u0623"),
+ moreKey("\uFEF9", "\u0644\u0625"), moreKey("\uFEF5", "\u0644\u0622")),
+ // U+0627: "ا" ARABIC LETTER ALEF
+ // U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
+ // U+0621: "ء" ARABIC LETTER HAMZA
+ // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE
+ // U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW
+ // U+0671: "ٱ" ARABIC LETTER ALEF WASLA
+ key("\u0627", joinMoreKeys("\u0622", "\u0621", "\u0623", "\u0625", "\u0671")),
+ // U+062A: "ت" ARABIC LETTER TEH
+ // U+0646: "ن" ARABIC LETTER NOON
+ // U+0645: "م" ARABIC LETTER MEEM
+ "\u062A", "\u0646", "\u0645",
+ // U+0643: "ك" ARABIC LETTER KAF
+ // U+06AF: "گ" ARABIC LETTER GAF
+ // U+06A9: "ک" ARABIC LETTER KEHEH
+ key("\u0643", joinMoreKeys("\u06AF", "\u06A9")),
+ // U+0637: "ط" ARABIC LETTER TAH
+ "\u0637")
+ .setKeysOfRow(3,
+ // U+0630: "ذ" ARABIC LETTER THAL
+ // U+0621: "ء" ARABIC LETTER HAMZA
+ // U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE
+ // U+0631: "ر" ARABIC LETTER REH
+ "\u0630", "\u0621", "\u0624", "\u0631",
+ // U+0649: "ى" ARABIC LETTER ALEF MAKSURA
+ // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
+ key("\u0649", moreKey("\u0626")),
+ // U+0629: "ة" ARABIC LETTER TEH MARBUTA
+ // U+0648: "و" ARABIC LETTER WAW
+ "\u0629", "\u0648",
+ // U+0632: "ز" ARABIC LETTER ZAIN
+ // U+0698: "ژ" ARABIC LETTER JEH
+ key("\u0632", moreKey("\u0698")),
+ // U+0638: "ظ" ARABIC LETTER ZAH
+ // U+062F: "د" ARABIC LETTER DAL
+ "\u0638", "\u062F")
+ .build();
+
+ private static class ArabicSymbols extends RtlSymbols {
+ public ArabicSymbols(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+0661: "١" ARABIC-INDIC DIGIT ONE
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ .replaceKeyOfLabel("1", key("\u0661",
+ joinMoreKeys("1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")))
+ // U+0662: "٢" ARABIC-INDIC DIGIT TWO
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ .replaceKeyOfLabel("2", key("\u0662", joinMoreKeys("2", "\u00B2", "\u2154")))
+ // U+0663: "٣" ARABIC-INDIC DIGIT THREE
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ .replaceKeyOfLabel("3", key("\u0663",
+ joinMoreKeys("3", "\u00B3", "\u00BE", "\u215C")))
+ // U+0664: "٤" ARABIC-INDIC DIGIT FOUR
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ .replaceKeyOfLabel("4", key("\u0664", joinMoreKeys("4", "\u2074")))
+ // U+0665: "٥" ARABIC-INDIC DIGIT FIVE
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ .replaceKeyOfLabel("5", key("\u0665", joinMoreKeys("5", "\u215D")))
+ // U+0666: "٦" ARABIC-INDIC DIGIT SIX
+ .replaceKeyOfLabel("6", key("\u0666", moreKey("6")))
+ // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ .replaceKeyOfLabel("7", key("\u0667", joinMoreKeys("7", "\u215E")))
+ // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
+ .replaceKeyOfLabel("8", key("\u0668", moreKey("8")))
+ // U+0669: "٩" ARABIC-INDIC DIGIT NINE
+ .replaceKeyOfLabel("9", key("\u0669", moreKey("9")))
+ // U+0660: "٠" ARABIC-INDIC DIGIT ZERO
+ // U+066B: "٫" ARABIC DECIMAL SEPARATOR
+ // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ .replaceKeyOfLabel("0", key("\u0660",
+ joinMoreKeys("0", "\u066B", "\u066C", "\u207F", "\u2205")))
+ // U+066A: "٪" ARABIC PERCENT SIGN
+ // U+2030: "‰" PER MILLE SIGN
+ .replaceKeyOfLabel("%", key("\u066A", joinMoreKeys("%", "\u2030")))
+ // U+061B: "؛" ARABIC SEMICOLON
+ .replaceKeyOfLabel(";", key("\u061B", moreKey(";")))
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ .replaceKeyOfLabel("?", key("\u061F", joinMoreKeys("?", "\u00BF")))
+ // U+060C: "،" ARABIC COMMA
+ .replaceKeyOfLabel(",", "\u060C")
+ // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
+ // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
+ .replaceKeyOfLabel("(", key("(", ")",
+ moreKey("\uFD3E", "\uFD3F"), moreKey("<", ">"), moreKey("{", "}"),
+ moreKey("[", "]")))
+ // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
+ // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
+ .replaceKeyOfLabel(")", key(")", "(",
+ moreKey("\uFD3F", "\uFD3E"), moreKey(">", "<"), moreKey("}", "{"),
+ moreKey("]", "[")))
+ // U+2605: "★" BLACK STAR
+ // U+066D: "٭" ARABIC FIVE POINTED STAR
+ .setMoreKeysOf("*", "\u2605", "\u066D")
+ .build();
+ }
+ }
+
+ private static class ArabicSymbolsShifted extends RtlSymbolsShifted {
+ public ArabicSymbolsShifted(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+2022: "•" BULLET
+ // U+266A: "♪" EIGHTH NOTE
+ .setMoreKeysOf("\u2022", "\u266A")
+ // U+060C: "،" ARABIC COMMA
+ .replaceKeyOfLabel(",", "\u060C")
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java
new file mode 100644
index 000000000..42ce0c1ea
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Armenian Phonetic alphabet keyboard.
+ */
+public final class ArmenianPhonetic extends LayoutBase {
+ private static final String LAYOUT_NAME = "armenian_phonetic";
+
+ public ArmenianPhonetic(final LayoutCustomizer customizer) {
+ super(customizer, ArmenianSymbols.class, ArmenianSymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class ArmenianPhoneticCustomizer extends LayoutCustomizer {
+ public ArmenianPhoneticCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return ARMENIAN_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ if (isPhone) {
+ return EMPTY_KEYS;
+ }
+ // U+055C: "՜" ARMENIAN EXCLAMATION MARK
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ // U+055E: "՞" ARMENIAN QUESTION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ return joinKeys(key("!", joinMoreKeys("\u055C", "\u00A1")),
+ key("?", joinMoreKeys("\u055E", "\u00BF")),
+ SHIFT_KEY);
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ // U+055D: "՝" ARMENIAN COMMA
+ return isPhone ? joinKeys(key("\u055D", SETTINGS_KEY))
+ : joinKeys(key("\u055D", SETTINGS_KEY), "_");
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ // U+0589: "։" ARMENIAN FULL STOP
+ final ExpectedKey fullStopKey = key("\u0589", getPunctuationMoreKeys(isPhone));
+ return isPhone ? joinKeys(fullStopKey) : joinKeys("/", fullStopKey);
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return ARMENIAN_PUNCTUATION_MORE_KEYS;
+ }
+
+ // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB
+ // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN
+ // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM
+ private static final ExpectedKey ARMENIAN_ALPHABET_KEY = key(
+ "\u0531\u0532\u0533", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+055E: "՞" ARMENIAN QUESTION MARK
+ // U+055C: "՜" ARMENIAN EXCLAMATION MARK
+ // U+055A: "՚" ARMENIAN APOSTROPHE
+ // U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING
+ // U+055D: "՝" ARMENIAN COMMA
+ // U+055B: "՛" ARMENIAN EMPHASIS MARK
+ // U+058A: "֊" ARMENIAN HYPHEN
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+055F: "՟" ARMENIAN ABBREVIATION MARK
+ private static final ExpectedKey[] ARMENIAN_PUNCTUATION_MORE_KEYS = joinMoreKeys(
+ ",", "\u055E", "\u055C", ".", "\u055A", "\u0559", "?", "!",
+ "\u055D", "\u055B", "\u058A", "\u00BB", "\u00AB", "\u055F", ";", ":");
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ if (isPhone) {
+ // U+056D: "խ" ARMENIAN SMALL LETTER XEH
+ // U+0577: "շ" ARMENIAN SMALL LETTER SHA
+ builder.addKeysOnTheRightOfRow(3, "\u056D")
+ .addKeysOnTheRightOfRow(4, "\u0577");
+ } else {
+ // U+056D: "խ" ARMENIAN SMALL LETTER XEH
+ // U+0577: "շ" ARMENIAN SMALL LETTER SHA
+ builder.addKeysOnTheRightOfRow(2, "\u056D")
+ .addKeysOnTheRightOfRow(3, "\u0577");
+ }
+ return builder.build();
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(4, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0567: "է" ARMENIAN SMALL LETTER EH
+ key("\u0567", moreKey("1")),
+ // U+0569: "թ" ARMENIAN SMALL LETTER TO
+ key("\u0569", moreKey("2")),
+ // U+0583: "փ" ARMENIAN SMALL LETTER PIWR
+ key("\u0583", moreKey("3")),
+ // U+0571: "ձ" ARMENIAN SMALL LETTER JA
+ key("\u0571", moreKey("4")),
+ // U+057B: "ջ" ARMENIAN SMALL LETTER JHEH
+ key("\u057B", moreKey("5")),
+ // U+0580: "ր" ARMENIAN SMALL LETTER REH
+ key("\u0580", moreKey("6")),
+ // U+0579: "չ" ARMENIAN SMALL LETTER CHA
+ key("\u0579", moreKey("7")),
+ // U+0573: "ճ" ARMENIAN SMALL LETTER CHEH
+ key("\u0573", moreKey("8")),
+ // U+056A: "ժ" ARMENIAN SMALL LETTER ZHE
+ key("\u056A", moreKey("9")),
+ // U+056E: "ծ" ARMENIAN SMALL LETTER CA
+ key("\u056E", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+0584: "ք" ARMENIAN SMALL LETTER KEH
+ // U+0578: "ո" ARMENIAN SMALL LETTER VO
+ "\u0584", "\u0578",
+ // U+0565: "ե" ARMENIAN SMALL LETTER ECH
+ // U+0587: "և" ARMENIAN SMALL LIGATURE ECH YIWN
+ key("\u0565", moreKey("\u0587")),
+ // U+057C: "ռ" ARMENIAN SMALL LETTER RA
+ // U+057F: "տ" ARMENIAN SMALL LETTER TIWN
+ // U+0568: "ը" ARMENIAN SMALL LETTER ET
+ // U+0582: "ւ" ARMENIAN SMALL LETTER YIWN
+ // U+056B: "ի" ARMENIAN SMALL LETTER INI
+ // U+0585: "օ" ARMENIAN SMALL LETTER OH
+ // U+057A: "պ" ARMENIAN SMALL LETTER PEH
+ "\u057C", "\u057F", "\u0568", "\u0582", "\u056B", "\u0585", "\u057A")
+ .setKeysOfRow(3,
+ // U+0561: "ա" ARMENIAN SMALL LETTER AYB
+ // U+057D: "ս" ARMENIAN SMALL LETTER SEH
+ // U+0564: "դ" ARMENIAN SMALL LETTER DA
+ // U+0586: "ֆ" ARMENIAN SMALL LETTER FEH
+ // U+0563: "գ" ARMENIAN SMALL LETTER GIM
+ // U+0570: "հ" ARMENIAN SMALL LETTER HO
+ // U+0575: "յ" ARMENIAN SMALL LETTER YI
+ // U+056F: "կ" ARMENIAN SMALL LETTER KEN
+ // U+056C: "լ" ARMENIAN SMALL LETTER LIWN
+ "\u0561", "\u057D", "\u0564", "\u0586", "\u0563", "\u0570", "\u0575", "\u056F",
+ "\u056C")
+ .setKeysOfRow(4,
+ // U+0566: "զ" ARMENIAN SMALL LETTER ZA
+ // U+0572: "ղ" ARMENIAN SMALL LETTER GHAD
+ // U+0581: "ց" ARMENIAN SMALL LETTER CO
+ // U+057E: "վ" ARMENIAN SMALL LETTER VEW
+ // U+0562: "բ" ARMENIAN SMALL LETTER BEN
+ // U+0576: "ն" ARMENIAN SMALL LETTER NOW
+ // U+0574: "մ" ARMENIAN SMALL LETTER MEN
+ "\u0566", "\u0572", "\u0581", "\u057E", "\u0562", "\u0576", "\u0574")
+ .build();
+
+ private static final class ArmenianSymbols extends Symbols {
+ public ArmenianSymbols(final LayoutCustomizer customizer) { super(customizer); }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ super.getLayout(isPhone));
+ // U+055D: "՝" ARMENIAN COMMA
+ builder.replaceKeyOfLabel(",", "\u055D");
+ // U+055C: "՜" ARMENIAN EXCLAMATION MARK
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ // U+055E: "՞" ARMENIAN QUESTION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ builder.setMoreKeysOf("!", "\u055C", "\u00A1")
+ .setMoreKeysOf("?", "\u055E", "\u00BF");
+ return builder.build();
+ }
+ }
+
+ private static final class ArmenianSymbolsShifted extends SymbolsShifted {
+ public ArmenianSymbolsShifted(final LayoutCustomizer customizer) { super(customizer); }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ super.getLayout(isPhone));
+ // U+055D: "՝" ARMENIAN COMMA
+ builder.replaceKeyOfLabel(",", "\u055D");
+ return builder.build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java
new file mode 100644
index 000000000..a0949637b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The AZERTY alphabet keyboard.
+ */
+public final class Azerty extends LayoutBase {
+ public Azerty(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return "azerty"; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ customizer.setAccentedLetters(builder);
+ builder.replaceKeyOfLabel(ROW3_QUOTE, key("'", joinMoreKeys(
+ customizer.getSingleQuoteMoreKeys(),
+ customizer.getSingleAngleQuoteKeys())));
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ final ExpectedKeyboardBuilder builder;
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
+ || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+ builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+ } else {
+ builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ getCustomizer().setAccentedLetters(builder);
+ builder.replaceKeyOfLabel(ROW3_QUOTE, "?");
+ }
+ builder.toUpperCase(getLocale());
+ return builder.build();
+ }
+
+ private static final String ROW3_QUOTE = "ROW3_QUOUTE";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("a", additionalMoreKey("1")),
+ key("z", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("y", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")))
+ .setKeysOfRow(2, "q", "s", "d", "f", "g", "h", "j", "k", "l", "m")
+ .setKeysOfRow(3, "w", "x", "c", "v", "b", "n", ROW3_QUOTE)
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java b/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java
new file mode 100644
index 000000000..2101ddf33
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Bengali keyboard.
+ */
+public final class Bengali extends LayoutBase {
+ private static final String LAYOUT_NAME = "bengali";
+
+ public Bengali(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class BengaliCustomizer extends LayoutCustomizer {
+ public BengaliCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return BENGALI_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0995: "क" BENGALI LETTER KA
+ // U+0996: "ख" BENGALI LETTER KHA
+ // U+0997: "ग" BENGALI LETTER GA
+ private static final ExpectedKey BENGALI_ALPHABET_KEY = key(
+ "\u0995\u0996\u0997", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0994: "ঔ" BENGALI LETTER AU
+ // U+09CC: "ৌ" BENGALI VOWEL SIGN AU
+ // U+09E7: "১" BENGALI DIGIT ONE
+ key("\u0994", joinMoreKeys("\u09CC", "\u09E7", "1")),
+ // U+0990: "ঐ" BENGALI LETTER AI
+ // U+09C8: "ৈ" BENGALI VOWEL SIGN AI
+ // U+09E8: "২" BENGALI DIGIT TWO
+ key("\u0990", joinMoreKeys("\u09C8", "\u09E8", "2")),
+ // U+0986: "আ" BENGALI LETTER AA
+ // U+09BE: "া" BENGALI VOWEL SIGN AA
+ // U+09E9: "৩" BENGALI DIGIT THREE
+ key("\u0986", joinMoreKeys("\u09BE", "\u09E9", "3")),
+ // U+0988: "ঈ" BENGALI LETTER II
+ // U+09C0: "ী" BENGALI VOWEL SIGN II
+ // U+09EA: "৪" BENGALI DIGIT FOUR
+ key("\u0988", joinMoreKeys("\u09C0", "\u09EA", "4")),
+ // U+098A: "ঊ" BENGALI LETTER UU
+ // U+09C2: "ূ" BENGALI VOWEL SIGN UU
+ // U+09EB: "৫" BENGALI DIGIT FIVE
+ key("\u098A", joinMoreKeys("\u09C2", "\u09EB", "5")),
+ // U+09AC: "ব" BENGALI LETTER BA
+ // U+09AD: "ভ" BENGALI LETTER BHA
+ // U+09EC: "৬" BENGALI DIGIT SIX
+ key("\u09AC", joinMoreKeys("\u09AD", "\u09EC", "6")),
+ // U+09B9: "হ" BENGALI LETTER HA
+ // U+09ED: "৭" BENGALI DIGIT SEVEN
+ key("\u09B9", joinMoreKeys("\u09ED", "7")),
+ // U+0997: "গ" BENGALI LETTER GA
+ // U+0998: "ঘ" BENGALI LETTER GHA
+ // U+09EE: "৮" BENGALI DIGIT EIGHT
+ key("\u0997", joinMoreKeys("\u0998", "\u09EE", "8")),
+ // U+09A6: "দ" BENGALI LETTER DA
+ // U+09A7: "ধ" BENGALI LETTER DHA
+ // U+09EF: "৯" BENGALI DIGIT NINE
+ key("\u09A6", joinMoreKeys("\u09A7", "\u09EF", "9")),
+ // U+099C: "জ" BENGALI LETTER JA
+ // U+099D: "ঝ" BENGALI LETTER JHA
+ // U+099C/U+09CD/U+099E:
+ // "জ্ঞ" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER NYA
+ // U+09E6: "০" BENGALI DIGIT ZERO
+ key("\u099C", joinMoreKeys("\u099D", "\u099C\u09CD\u099E", "\u09E6", "0")),
+ // U+09A1: "ড" BENGALI LETTER DDA
+ // U+09A1/U+09BC: "ড়" BENGALI LETTER DDA/BENGALI SIGN NUKTA
+ key("\u09A1", moreKey("\u09A1\u09BC")))
+ .setKeysOfRow(2,
+ // U+0993: "ও" BENGALI LETTER O
+ // U+09CB: "ো" BENGALI VOWEL SIGN O
+ key("\u0993", moreKey("\u09CB")),
+ // U+098F: "এ" BENGALI LETTER E
+ // U+09C7: "ে" BENGALI VOWEL SIGN E
+ key("\u098F", moreKey("\u09C7")),
+ // U+0985: "অ" BENGALI LETTER A
+ // U+09CD: "্" BENGALI SIGN VIRAMA
+ key("\u0985", moreKey("\u09CD")),
+ // U+0987: "ই" BENGALI LETTER I
+ // U+09BF: "ি" BENGALI VOWEL SIGN I
+ key("\u0987", moreKey("\u09BF")),
+ // U+0989: "উ" BENGALI LETTER U
+ // U+09C1: "ু" BENGALI VOWEL SIGN U
+ key("\u0989", moreKey("\u09C1")),
+ // U+09AA: "প" BENGALI LETTER PA
+ // U+09AB: "ফ" BENGALI LETTER PHA
+ key("\u09AA", moreKey("\u09AB")),
+ // U+09B0: "র" BENGALI LETTER RA
+ // U+09C3: "ৃ" BENGALI VOWEL SIGN VOCALIC R
+ // U+098B: "ঋ" BENGALI LETTER VOCALIC R
+ // U+09A4/U+09CD/U+09B0:
+ // "ত্র" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+ key("\u09B0", joinMoreKeys("\u09C3", "\u098B", "\u09A4\u09CD\u09B0")),
+ // U+0995: "ক" BENGALI LETTER KA
+ // U+0996: "খ" BENGALI LETTER KHA
+ key("\u0995", moreKey("\u0996")),
+ // U+09A4: "ত" BENGALI LETTER TA
+ // U+09CE: "ৎ" BENGALI LETTER KHANDA TA
+ // U+09A5: "থ" BENGALI LETTER THA
+ // U+09A4/U+09CD/U+09A4:
+ // "ত্ত" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+ key("\u09A4", joinMoreKeys("\u09CE", "\u09A5", "\u09A4\u09CD\u09A4")),
+ // U+099A: "চ" BENGALI LETTER CA
+ // U+099B: "ছ" BENGALI LETTER CHA
+ key("\u099A", moreKey("\u099B")),
+ // U+099F: "ট" BENGALI LETTER TTA
+ // U+09A0: "ঠ" BENGALI LETTER TTHA
+ key("\u099F", moreKey("\u09A0")))
+ .setKeysOfRow(3,
+ // U+0981: "ঁ" BENGALI SIGN CANDRABINDU
+ // U+0983: "ঃ" BENGALI SIGN VISARGA
+ // U+0982: "ং" BENGALI SIGN ANUSVARA
+ key("\u0981", joinMoreKeys("\u0983", "\u0982")),
+ // U+09A2: "ঢ" BENGALI LETTER DDHA
+ // U+09A2/U+09BC: "ঢ়" BENGALI LETTER DDHA/BENGALI SIGN NUKTA
+ key("\u09A2", moreKey("\u09A2\u09BC")),
+ // U+09AE: "ম" BENGALI LETTER MA
+ "\u09AE",
+ // U+09A8: "ন" BENGALI LETTER NA
+ // U+09A3: "ণ" BENGALI LETTER NNA
+ key("\u09A8", moreKey("\u09A3")),
+ // U+099E: "ঞ" BENGALI LETTER NYA
+ // U+0999: "ঙ" BENGALI LETTER NGA
+ // U+099E/U+09CD/U+099C:
+ // "ঞ্জ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+ key("\u099E", joinMoreKeys("\u0999", "\u099E\u09CD\u099C")),
+ // U+09B2: "ল" BENGALI LETTER LA
+ "\u09B2",
+ // U+09B7: "ষ" BENGALI LETTER SSA
+ // U+0995/U+09CD/U+09B7:
+ // "ক্ষ" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER SSA
+ key("\u09B7", moreKey("\u0995\u09CD\u09B7")),
+ // U+09B8: "স" BENGALI LETTER SA
+ // U+09B6: "শ" BENGALI LETTER SHA
+ key("\u09B8", moreKey("\u09B6")),
+ // U+09DF: "য়" BENGALI LETTER YYA
+ // U+09AF: "য" BENGALI LETTER YA
+ key("\u09DF", moreKey("\u09AF")),
+ // U+0964: "।" DEVANAGARI DANDA
+ // U+0965: "॥" DEVANAGARI DOUBLE DANDA
+ key("\u0964", moreKey("\u0965")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java b/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java
new file mode 100644
index 000000000..3282e44ae
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+public final class Bulgarian extends LayoutBase {
+ private static final String LAYOUT_NAME = "bulgarian";
+
+ public Bulgarian(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class BulgarianCustomizer extends LayoutCustomizer {
+ private final EastSlavicCustomizer mEastSlavicCustomizer;
+
+ public BulgarianCustomizer(final Locale locale) {
+ super(locale);
+ mEastSlavicCustomizer = new EastSlavicCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getAlphabetKey() {
+ return mEastSlavicCustomizer.getAlphabetKey();
+ }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+044F: "я" CYRILLIC SMALL LETTER YA
+ key("\u044F", moreKey("1")),
+ // U+0432: "в" CYRILLIC SMALL LETTER VE
+ key("\u0432", moreKey("2")),
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ key("\u0435", moreKey("3")),
+ // U+0440: "р" CYRILLIC SMALL LETTER ER
+ key("\u0440", moreKey("4")),
+ // U+0442: "т" CYRILLIC SMALL LETTER TE
+ key("\u0442", moreKey("5")),
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ key("\u044A", moreKey("6")),
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ key("\u0443", moreKey("7")),
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
+ key("\u0438", joinMoreKeys("8", "\u045D")),
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ key("\u043E", moreKey("9")),
+ // U+043F: "п" CYRILLIC SMALL LETTER PE
+ key("\u043F", moreKey("0")),
+ // U+0447: "ч" CYRILLIC SMALL LETTER CHE
+ "\u0447")
+ .setKeysOfRow(2,
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+0441: "с" CYRILLIC SMALL LETTER ES
+ // U+0434: "д" CYRILLIC SMALL LETTER DE
+ // U+0444: "ф" CYRILLIC SMALL LETTER EF
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ // U+0445: "х" CYRILLIC SMALL LETTER HA
+ // U+0439: "й" CYRILLIC SMALL LETTER SHORT I
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ // U+043B: "л" CYRILLIC SMALL LETTER EL
+ // U+0448: "ш" CYRILLIC SMALL LETTER SHA
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ "\u0430", "\u0441", "\u0434", "\u0444", "\u0433", "\u0445", "\u0439", "\u043A",
+ "\u043B", "\u0448", "\u0449")
+ .setKeysOfRow(3,
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+0446: "ц" CYRILLIC SMALL LETTER TSE
+ // U+0436: "ж" CYRILLIC SMALL LETTER ZHE
+ // U+0431: "б" CYRILLIC SMALL LETTER BE
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ // U+043C: "м" CYRILLIC SMALL LETTER EM
+ // U+044E: "ю" CYRILLIC SMALL LETTER YU
+ "\u0437", "\u044C", "\u0446", "\u0436", "\u0431", "\u043D", "\u043C", "\u044E")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java b/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java
new file mode 100644
index 000000000..20a5f7dac
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+public final class BulgarianBds extends LayoutBase {
+ private static final String LAYOUT_NAME = "bulgarian_bds";
+
+ public BulgarianBds(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class BulgarianBdsCustomizer extends EastSlavicCustomizer {
+ public BulgarianBdsCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ key("\u0443", moreKey("1")),
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ key("\u0435", moreKey("2")),
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
+ key("\u0438", joinMoreKeys("3", "\u045D")),
+ // U+0448: "ш" CYRILLIC SMALL LETTER SHA
+ key("\u0448", moreKey("4")),
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ key("\u0449", moreKey("5")),
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ key("\u043A", moreKey("6")),
+ // U+0441: "с" CYRILLIC SMALL LETTER ES
+ key("\u0441", moreKey("7")),
+ // U+0434: "д" CYRILLIC SMALL LETTER DE
+ key("\u0434", moreKey("8")),
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ key("\u0437", moreKey("9")),
+ // U+0446: "ц" CYRILLIC SMALL LETTER TSE
+ key("\u0446", moreKey("0")),
+ // U+0431: "б" CYRILLIC SMALL LETTER BE
+ "\u0431")
+ .setKeysOfRow(2,
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044F: "я" CYRILLIC SMALL LETTER YA
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ // U+0436: "ж" CYRILLIC SMALL LETTER ZHE
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ // U+0442: "т" CYRILLIC SMALL LETTER TE
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ // U+0432: "в" CYRILLIC SMALL LETTER VE
+ // U+043C: "м" CYRILLIC SMALL LETTER EM
+ // U+0447: "ч" CYRILLIC SMALL LETTER CHE
+ "\u044C", "\u044F", "\u0430", "\u043E", "\u0436", "\u0433", "\u0442", "\u043D",
+ "\u0432", "\u043C", "\u0447")
+ .setKeysOfRow(3,
+ // U+044E: "ю" CYRILLIC SMALL LETTER YU
+ // U+0439: "й" CYRILLIC SMALL LETTER SHORT I
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ // U+0444: "ф" CYRILLIC SMALL LETTER EF
+ // U+0445: "х" CYRILLIC SMALL LETTER HA
+ // U+043F: "п" CYRILLIC SMALL LETTER PE
+ // U+0440: "р" CYRILLIC SMALL LETTER ER
+ // U+043B: "л" CYRILLIC SMALL LETTER EL
+ "\u044E", "\u0439", "\u044A", "\u044D", "\u0444", "\u0445", "\u043F", "\u0440",
+ "\u043B")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
new file mode 100644
index 000000000..a4a9701cd
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The Colemak alphabet keyboard.
+ */
+public final class Colemak extends LayoutBase {
+ private static final String LAYOUT_NAME = "colemak";
+
+ public Colemak(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ getCustomizer().setAccentedLetters(builder);
+ builder.replaceKeyOfLabel(ROW1_10, key(";", additionalMoreKey("0"), moreKey(":")));
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ final ExpectedKeyboardBuilder builder;
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
+ || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+ builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+ } else {
+ builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ getCustomizer().setAccentedLetters(builder);
+ builder.replaceKeyOfLabel(ROW1_10, key(":", additionalMoreKey("0")));
+ }
+ builder.toUpperCase(getLocale());
+ return builder.build();
+ }
+
+ private static final String ROW1_10 = "ROW1_10";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("f", additionalMoreKey("3")),
+ key("p", additionalMoreKey("4")),
+ key("g", additionalMoreKey("5")),
+ key("j", additionalMoreKey("6")),
+ key("l", additionalMoreKey("7")),
+ key("u", additionalMoreKey("8")),
+ key("y", additionalMoreKey("9")),
+ ROW1_10)
+ .setKeysOfRow(2, "a", "r", "s", "t", "d", "h", "n", "e", "i", "o")
+ .setKeysOfRow(3, "z", "x", "c", "v", "b", "k", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/DevanagariLetterConstants.java b/tests/src/com/android/inputmethod/keyboard/layout/DevanagariLetterConstants.java
new file mode 100644
index 000000000..bcf06f085
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/DevanagariLetterConstants.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import android.os.Build;
+
+/**
+ * This class offers label strings of Devanagari letters that need the dotted circle to draw
+ * its glyph.
+ */
+class DevanagariLetterConstants {
+ private static final boolean NEEDS_DOTTED_CIRCLE =
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN;
+ // U+25CC: "◌" DOTTED CIRCLE
+ private static final String DOTTED_CIRCLE = NEEDS_DOTTED_CIRCLE ? "\u25CC" : "";
+
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ static final String SIGN_CANDRABINDU = DOTTED_CIRCLE + "\u0901";
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ static final String SIGN_ANUSVARA = DOTTED_CIRCLE + "\u0902";
+ // U+0903: "ः" DEVANAGARI SIGN VISARGA
+ static final String SIGN_VISARGA = DOTTED_CIRCLE + "\u0903";
+ // U+093C: "़" DEVANAGARI SIGN NUKTA
+ static final String SIGN_NUKTA = DOTTED_CIRCLE + "\u093C";
+ // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+ static final String SIGN_AVAGRAHA = DOTTED_CIRCLE + "\u093D";
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ static final String VOWEL_SIGN_AA = DOTTED_CIRCLE + "\u093E";
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ static final String VOWEL_SIGN_I = DOTTED_CIRCLE + "\u093F";
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ static final String VOWEL_SIGN_II = DOTTED_CIRCLE + "\u0940";
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ static final String VOWEL_SIGN_U = DOTTED_CIRCLE + "\u0941";
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ static final String VOWEL_SIGN_UU = DOTTED_CIRCLE + "\u0942";
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ static final String VOWEL_SIGN_VOCALIC_R = DOTTED_CIRCLE + "\u0943";
+ // U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR
+ static final String VOWEL_SIGN_VOCALIC_RR = DOTTED_CIRCLE + "\u0944";
+ // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+ static final String VOWEL_SIGN_CANDRA_E = DOTTED_CIRCLE + "\u0945";
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ static final String VOWEL_SIGN_E = DOTTED_CIRCLE + "\u0947";
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ static final String VOWEL_SIGN_AI = DOTTED_CIRCLE + "\u0948";
+ // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+ static final String VOWEL_SIGN_CANDRA_O = DOTTED_CIRCLE + "\u0949";
+ // U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O
+ static final String VOWEL_SIGN_SHORT_O = DOTTED_CIRCLE + "\u094A";
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ static final String VOWEL_SIGN_O = DOTTED_CIRCLE + "\u094B";
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ static final String VOWEL_SIGN_AU = DOTTED_CIRCLE + "\u094C";
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ static final String SIGN_VIRAMA = DOTTED_CIRCLE + "\u094D";
+ // U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
+ static final String ABBREVIATION_SIGN = DOTTED_CIRCLE + "\u0970";
+ // U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
+ static final String LETTER_GLOTTAL_STOP = DOTTED_CIRCLE + "\u097D";
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
new file mode 100644
index 000000000..e75cfd0ff
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+
+import java.util.Locale;
+
+/**
+ * The QWERTY alphabet keyboard.
+ */
+public final class Dvorak extends LayoutBase {
+ private static final String LAYOUT_NAME = "dvorak";
+
+ public Dvorak(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class DvorakCustomizer extends LayoutCustomizer {
+ public DvorakCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return isPhone ? joinKeys(SHIFT_KEY): joinKeys(SHIFT_KEY, key("q"));
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : joinKeys(key("z"), SHIFT_KEY);
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ return isPhone ? joinKeys(key("q", SETTINGS_KEY)) :
+ joinKeys(SETTINGS_KEY, key("_", moreKey("-")));
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ final ExpectedAdditionalMoreKey[] punctuationMoreKeys =
+ convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone));
+ return isPhone
+ ? joinKeys(key("z", punctuationMoreKeys))
+ : joinKeys("/", key("?", moreKey("!")));
+ }
+
+ private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys(
+ final ExpectedKey ... moreKeys) {
+ final ExpectedAdditionalMoreKey[] additionalMoreKeys =
+ new ExpectedAdditionalMoreKey[moreKeys.length];
+ for (int index = 0; index < moreKeys.length; index++) {
+ additionalMoreKeys[index] = ExpectedAdditionalMoreKey.newInstance(moreKeys[index]);
+ }
+ return additionalMoreKeys;
+ }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_SYMBOLS
+ || elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
+ return super.getLayout(isPhone, elementId);
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ getCommonAlphabetLayout(isPhone));
+ if (elementId == KeyboardId.ELEMENT_ALPHABET
+ || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ builder.addKeysOnTheLeftOfRow(1,
+ key("'", joinMoreKeys(additionalMoreKey("1"), "!", "\"")),
+ key(",", joinMoreKeys(additionalMoreKey("2"), "?", "<")),
+ key(".", joinMoreKeys(additionalMoreKey("3"), ">")));
+ } else {
+ builder.addKeysOnTheLeftOfRow(1,
+ key("\"", additionalMoreKey("1")),
+ key("<", additionalMoreKey("2")),
+ key(">", additionalMoreKey("3")));
+ }
+ convertCommonLayoutToKeyboard(builder, isPhone);
+ getCustomizer().setAccentedLetters(builder);
+ if (elementId != KeyboardId.ELEMENT_ALPHABET) {
+ builder.toUpperCase(getLocale());
+ builder.replaceKeysOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY);
+ }
+ return builder.build();
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("p", additionalMoreKey("4")),
+ key("y", additionalMoreKey("5")),
+ key("f", additionalMoreKey("6")),
+ key("g", additionalMoreKey("7")),
+ key("c", additionalMoreKey("8")),
+ key("r", additionalMoreKey("9")),
+ key("l", additionalMoreKey("0")))
+ .setKeysOfRow(2, "a", "o", "e", "u", "i", "d", "h", "t", "n", "s")
+ .setKeysOfRow(3, "j", "k", "x", "b", "m", "w", "v")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java b/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java
new file mode 100644
index 000000000..7fcc974c2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public final class EastSlavic extends LayoutBase {
+ private static final String LAYOUT_NAME = "east_slavic";
+
+ public EastSlavic(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class EastSlavicCustomizer extends LayoutCustomizer {
+ public EastSlavicCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public final ExpectedKey getAlphabetKey() { return EAST_SLAVIC_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0410: "А" CYRILLIC CAPITAL LETTER A
+ // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
+ // U+0412: "В" CYRILLIC CAPITAL LETTER VE
+ private static final ExpectedKey EAST_SLAVIC_ALPHABET_KEY = key(
+ "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ public static final String ROW1_9 = "ROW1_9";
+ public static final String ROW2_2 = "ROW2_2";
+ public static final String ROW2_11 = "ROW2_11";
+ public static final String ROW3_5 = "ROW3_5";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ key("\u0439", additionalMoreKey("1")),
+ // U+0446: "ц" CYRILLIC SMALL LETTER TSE
+ key("\u0446", additionalMoreKey("2")),
+ // U+0439: "й" CYRILLIC SMALL LETTER SHORT I
+ key("\u0443", additionalMoreKey("3")),
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ key("\u043A", additionalMoreKey("4")),
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ key("\u0435", additionalMoreKey("5")),
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ key("\u043D", additionalMoreKey("6")),
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ key("\u0433", additionalMoreKey("7")),
+ // U+0448: "ш" CYRILLIC SMALL LETTER SHA
+ key("\u0448", additionalMoreKey("8")),
+ key(ROW1_9, additionalMoreKey("9")),
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ key("\u0437", additionalMoreKey("0")),
+ // U+0445: "х" CYRILLIC SMALL LETTER HA
+ "\u0445")
+ .setKeysOfRow(2,
+ // U+0444: "ф" CYRILLIC SMALL LETTER EF
+ // U+0432: "в" CYRILLIC SMALL LETTER VE
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+043F: "п" CYRILLIC SMALL LETTER PE
+ // U+0440: "р" CYRILLIC SMALL LETTER ER
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ // U+043B: "л" CYRILLIC SMALL LETTER EL
+ // U+0434: "д" CYRILLIC SMALL LETTER DE
+ // U+0436: "ж" CYRILLIC SMALL LETTER ZHE
+ "\u0444", ROW2_2, "\u0432", "\u0430", "\u043F", "\u0440", "\u043E", "\u043B",
+ "\u0434", "\u0436", ROW2_11)
+ .setKeysOfRow(3,
+ // U+044F: "я" CYRILLIC SMALL LETTER YA
+ // U+0447: "ч" CYRILLIC SMALL LETTER CHE
+ // U+0441: "с" CYRILLIC SMALL LETTER ES
+ // U+043C: "м" CYRILLIC SMALL LETTER EM
+ // U+0442: "т" CYRILLIC SMALL LETTER TE
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+0431: "б" CYRILLIC SMALL LETTER BE
+ // U+044E: "ю" CYRILLIC SMALL LETTER YU
+ "\u044F", "\u0447", "\u0441", "\u043C", ROW3_5, "\u0442", "\u044C", "\u0431",
+ "\u044E")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java
new file mode 100644
index 000000000..a513740e7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public final class Farsi extends LayoutBase {
+ private static final String LAYOUT_NAME = "farsi";
+
+ public Farsi(final LayoutCustomizer customizer) {
+ super(customizer, FarsiSymbols.class, FarsiSymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class FarsiCustomizer extends LayoutCustomizer {
+ public FarsiCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return FARSI_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getSymbolsKey() { return FARSI_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey getBackToSymbolsKey() { return FARSI_BACK_TO_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RIAL; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ if (isPhone) {
+ // U+060C: "،" ARABIC COMMA
+ return joinKeys(key("\u060C", SETTINGS_KEY));
+ }
+ // U+060C: "،" ARABIC COMMA
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+061B: "؛" ARABIC SEMICOLON
+ return joinKeys(key("\u060C", joinMoreKeys(
+ ":", "!", "\u061F", "\u061B", "-", RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL,
+ SETTINGS_KEY)),
+ "_");
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ if (isPhone) {
+ return super.getKeysRightToSpacebar(isPhone);
+ }
+ return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone)));
+ }
+
+ @Override
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return FARSI_DIACRITICS;
+ }
+
+ // U+0627: "ا" ARABIC LETTER ALEF
+ // U+200C: ZERO WIDTH NON-JOINER
+ // U+0628: "ب" ARABIC LETTER BEH
+ // U+067E: "پ" ARABIC LETTER PEH
+ private static final ExpectedKey FARSI_ALPHABET_KEY = key(
+ "\u0627\u200C\u0628\u200C\u067E", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
+ // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
+ // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
+ // U+061F: "؟" ARABIC QUESTION MARK
+ private static final ExpectedKey FARSI_SYMBOLS_KEY = key(
+ "\u06F3\u06F2\u06F1\u061F", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ private static final ExpectedKey FARSI_BACK_TO_SYMBOLS_KEY = key(
+ "\u06F3\u06F2\u06F1\u061F", Constants.CODE_SHIFT);
+ // U+FDFC: "﷼" RIAL SIGN
+ private static final ExpectedKey CURRENCY_RIAL = key("\uFDFC",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ private static final ExpectedKey[] FARSI_DIACRITICS = {
+ // U+0655: "ٕ" ARABIC HAMZA BELOW
+ // U+0652: "ْ" ARABIC SUKUN
+ // U+0651: "ّ" ARABIC SHADDA
+ // U+064C: "ٌ" ARABIC DAMMATAN
+ // U+064D: "ٍ" ARABIC KASRATAN
+ // U+064B: "ً" ARABIC FATHATAN
+ // U+0654: "ٔ" ARABIC HAMZA ABOVE
+ // U+0656: "ٖ" ARABIC SUBSCRIPT ALEF
+ // U+0670: "ٰ" ARABIC LETTER SUPERSCRIPT ALEF
+ // U+0653: "ٓ" ARABIC MADDAH ABOVE
+ // U+064F: "ُ" ARABIC DAMMA
+ // U+0650: "ِ" ARABIC KASRA
+ // U+064E: "َ" ARABIC FATHA
+ // U+0640: "ـ" ARABIC TATWEEL
+ moreKey(" \u0655", "\u0655"), moreKey(" \u0652", "\u0652"),
+ moreKey(" \u0651", "\u0651"), moreKey(" \u064C", "\u064C"),
+ moreKey(" \u064D", "\u064D"), moreKey(" \u064B", "\u064B"),
+ moreKey(" \u0654", "\u0654"), moreKey(" \u0656", "\u0656"),
+ moreKey(" \u0670", "\u0670"), moreKey(" \u0653", "\u0653"),
+ moreKey(" \u064F", "\u064F"), moreKey(" \u0650", "\u0650"),
+ moreKey(" \u064E", "\u064E"), moreKey("\u0640\u0640\u0640", "\u0640")
+ };
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ if (isPhone) {
+ return ALPHABET_COMMON;
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ // U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
+ builder.insertKeysAtRow(3, 10, "\u0622");
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0636: "ض" ARABIC LETTER DAD
+ // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
+ key("\u0636", joinMoreKeys("\u06F1", "1")),
+ // U+0635: "ص" ARABIC LETTER SAD
+ // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
+ key("\u0635", joinMoreKeys("\u06F2", "2")),
+ // U+062B: "ث" ARABIC LETTER THEH
+ // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
+ key("\u062B", joinMoreKeys("\u06F3", "3")),
+ // U+0642: "ق" ARABIC LETTER QAF
+ // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
+ key("\u0642", joinMoreKeys("\u06F4", "4")),
+ // U+0641: "ف" ARABIC LETTER FEH
+ // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
+ key("\u0641", joinMoreKeys("\u06F5", "5")),
+ // U+063A: "غ" ARABIC LETTER GHAIN
+ // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
+ key("\u063A", joinMoreKeys("\u06F6", "6")),
+ // U+0639: "ع" ARABIC LETTER AIN
+ // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
+ key("\u0639", joinMoreKeys("\u06F7", "7")),
+ // U+0647: "ه" ARABIC LETTER HEH
+ // U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
+ // U+0647/U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER
+ // U+0647/U+0654: ARABIC LETTER HEH + ARABIC HAMZA ABOVE
+ // U+0629: "ة" ARABIC LETTER TEH MARBUTA
+ // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
+ key("\u0647", joinMoreKeys(moreKey("\uFEEB", "\u0647\u200D"), "\u0647\u0654",
+ "\u0629", "\u06F8", "8")),
+ // U+062E: "خ" ARABIC LETTER KHAH
+ // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
+ key("\u062E", joinMoreKeys("\u06F9", "9")),
+ // U+062D: "ح" ARABIC LETTER HAH
+ // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
+ key("\u062D", joinMoreKeys("\u06F0", "0")),
+ // U+062C: "ج" ARABIC LETTER JEEM
+ "\u062C")
+ .setKeysOfRow(2,
+ // U+0634: "ش" ARABIC LETTER SHEEN
+ // U+0633: "س" ARABIC LETTER SEEN
+ "\u0634", "\u0633",
+ // U+06CC: "ی" ARABIC LETTER FARSI YEH
+ // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
+ // U+064A: "ي" ARABIC LETTER YEH
+ // U+FBE8: "ﯨ" ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM
+ // U+0649: "ى" ARABIC LETTER ALEF MAKSURA
+ key("\u06CC", joinMoreKeys("\u0626", "\u064A", moreKey("\uFBE8", "\u0649"))),
+ // U+0628: "ب" ARABIC LETTER BEH
+ // U+0644: "ل" ARABIC LETTER LAM
+ "\u0628", "\u0644",
+ // U+0627: "ا" ARABIC LETTER ALEF
+ // U+0671: "ٱ" ARABIC LETTER ALEF WASLA
+ // U+0621: "ء" ARABIC LETTER HAMZA
+ // U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
+ // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE
+ // U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW
+ key("\u0627", joinMoreKeys("\u0671", "\u0621", "\u0622", "\u0623", "\u0625")),
+ // U+062A: "ت" ARABIC LETTER TEH
+ // U+0629: "ة": ARABIC LETTER TEH MARBUTA
+ key("\u062A", moreKey("\u0629")),
+ // U+0646: "ن" ARABIC LETTER NOON
+ // U+0645: "م" ARABIC LETTER MEEM
+ "\u0646", "\u0645",
+ // U+06A9: "ک" ARABIC LETTER KEHEH
+ // U+0643: "ك" ARABIC LETTER KAF
+ key("\u06A9", moreKey("\u0643")),
+ // U+06AF: "گ" ARABIC LETTER GAF
+ "\u06AF")
+ .setKeysOfRow(3,
+ // U+0638: "ظ" ARABIC LETTER ZAH
+ // U+0637: "ط" ARABIC LETTER TAH
+ // U+0698: "ژ" ARABIC LETTER JEH
+ // U+0632: "ز" ARABIC LETTER ZAIN
+ // U+0631: "ر" ARABIC LETTER REH
+ // U+0630: "ذ" ARABIC LETTER THAL
+ // U+062F: "د" ARABIC LETTER DAL
+ // U+067E: "پ" ARABIC LETTER PEH
+ "\u0638", "\u0637", "\u0698", "\u0632", "\u0631", "\u0630", "\u062F", "\u067E",
+ // U+0648: "و" ARABIC LETTER WAW
+ // U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE
+ key("\u0648", moreKey("\u0624")),
+ // U+0686: "چ" ARABIC LETTER TCHEH
+ "\u0686")
+ .build();
+
+ private static class FarsiSymbols extends RtlSymbols {
+ public FarsiSymbols(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ .replaceKeyOfLabel("1", key("\u06F1",
+ joinMoreKeys("1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")))
+ // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ .replaceKeyOfLabel("2", key("\u06F2", joinMoreKeys("2", "\u00B2", "\u2154")))
+ // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ .replaceKeyOfLabel("3", key("\u06F3",
+ joinMoreKeys("3", "\u00B3", "\u00BE", "\u215C")))
+ // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ .replaceKeyOfLabel("4", key("\u06F4", joinMoreKeys("4", "\u2074")))
+ // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ .replaceKeyOfLabel("5", key("\u06F5", joinMoreKeys("5", "\u215D")))
+ // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
+ .replaceKeyOfLabel("6", key("\u06F6", moreKey("6")))
+ // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ .replaceKeyOfLabel("7", key("\u06F7", joinMoreKeys("7", "\u215E")))
+ // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
+ .replaceKeyOfLabel("8", key("\u06F8", moreKey("8")))
+ // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
+ .replaceKeyOfLabel("9", key("\u06F9", moreKey("9")))
+ // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
+ .replaceKeyOfLabel("@", key("\u066C", moreKey("@")))
+ // U+066B: "٫" ARABIC DECIMAL SEPARATOR
+ .replaceKeyOfLabel("#", key("\u066B", moreKey("#")))
+ // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
+ // U+066B: "٫" ARABIC DECIMAL SEPARATOR
+ // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ .replaceKeyOfLabel("0", key("\u06F0",
+ joinMoreKeys("0", "\u066B", "\u066C", "\u207F", "\u2205")))
+ // U+066A: "٪" ARABIC PERCENT SIGN
+ // U+2030: "‰" PER MILLE SIGN
+ .replaceKeyOfLabel("%", key("\u066A", joinMoreKeys("%", "\u2030")))
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ .replaceKeyOfLabel("\"", key("\u00AB", "\u00BB", joinMoreKeys(
+ DOUBLE_QUOTES_9LR, DOUBLE_ANGLE_QUOTES_LR_RTL)))
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ .replaceKeyOfLabel("'", key("\u00BB", "\u00AB", joinMoreKeys(
+ SINGLE_QUOTES_9LR, SINGLE_ANGLE_QUOTES_LR_RTL)))
+ // U+061B: "؛" ARABIC SEMICOLON
+ .replaceKeyOfLabel(";", key("\u061B", moreKey(";")))
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ .replaceKeyOfLabel("?", key("\u061F", joinMoreKeys("?", "\u00BF")))
+ // U+060C: "،" ARABIC COMMA
+ .replaceKeyOfLabel(",", "\u060C")
+ // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
+ // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
+ .replaceKeyOfLabel("(", key("(", ")",
+ moreKey("\uFD3E", "\uFD3F"), moreKey("<", ">"), moreKey("{", "}"),
+ moreKey("[", "]")))
+ // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
+ // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
+ .replaceKeyOfLabel(")", key(")", "(",
+ moreKey("\uFD3F", "\uFD3E"), moreKey(">", "<"), moreKey("}", "{"),
+ moreKey("]", "[")))
+ // U+2605: "★" BLACK STAR
+ // U+066D: "٭" ARABIC FIVE POINTED STAR
+ .setMoreKeysOf("*", "\u2605", "\u066D")
+ .build();
+ }
+ }
+
+ private static class FarsiSymbolsShifted extends RtlSymbolsShifted {
+ public FarsiSymbolsShifted(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+2022: "•" BULLET
+ // U+266A: "♪" EIGHTH NOTE
+ .setMoreKeysOf("\u2022", "\u266A")
+ // U+060C: "،" ARABIC COMMA
+ .replaceKeyOfLabel(",", "\u060C")
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ .replaceKeyOfLabel("<", key("\u00AB", "\u00BB",
+ moreKey("\u2039", "\u203A"), moreKey("\u2264", "\u2265"),
+ moreKey("<", ">")))
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ .replaceKeyOfLabel(">", key("\u00BB", "\u00AB",
+ moreKey("\u203A", "\u2039"), moreKey("\u2265", "\u2264"),
+ moreKey(">", "<")))
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java
new file mode 100644
index 000000000..6f20dfcd1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Georgian alphabet keyboard.
+ */
+public final class Georgian extends LayoutBase {
+ private static final String LAYOUT_NAME = "georgian";
+
+ public Georgian(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class GeorgianCustomizer extends LayoutCustomizer {
+ public GeorgianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return GEORGIAN_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ // U+10D0: "ა" GEORGIAN LETTER AN
+ // U+10D1: "ბ" GEORGIAN LETTER BAN
+ // U+10D2: "გ" GEORGIAN LETTER GAN
+ private static final ExpectedKey GEORGIAN_ALPHABET_KEY = key(
+ "\u10D0\u10D1\u10D2", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ return ALPHABET_COMMON;
+ }
+
+ @Override
+ public ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone,
+ final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+10E5: "ქ" GEORGIAN LETTER GHAN
+ key("\u10E5", moreKey("1")),
+ // U+10EC: "წ" GEORGIAN LETTER CIL
+ key("\u10EC", moreKey("2")),
+ // U+10D4: "ე" GEORGIAN LETTER EN
+ // U+10F1: "ჱ" GEORGIAN LETTER HE
+ key("\u10D4", joinMoreKeys("3", "\u10F1")),
+ // U+10E0: "რ" GEORGIAN LETTER RAE
+ key("\u10E0", moreKey("4")),
+ // U+10E2: "ტ" GEORGIAN LETTER TAR
+ key("\u10E2", moreKey("5")),
+ // U+10E7: "ყ" GEORGIAN LETTER QAR
+ // U+10F8: "ჸ" GEORGIAN LETTER ELIFI
+ key("\u10E7", joinMoreKeys("6", "\u10F8")),
+ // U+10E3: "უ" GEORGIAN LETTER UN
+ key("\u10E3", moreKey("7")),
+ // U+10D8: "ი" GEORGIAN LETTER IN
+ // U+10F2: "ჲ" GEORGIAN LETTER HIE
+ key("\u10D8", joinMoreKeys("8", "\u10F2")),
+ // U+10DD: "ო" GEORGIAN LETTER ON
+ key("\u10DD", moreKey("9")),
+ // U+10DE: "პ" GEORGIAN LETTER PAR
+ key("\u10DE", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+10D0: "ა" GEORGIAN LETTER AN
+ // U+10FA: "ჺ" GEORGIAN LETTER AIN
+ key("\u10D0", moreKey("\u10FA")),
+ // U+10E1: "ს" GEORGIAN LETTER SAN
+ // U+10D3: "დ" GEORGIAN LETTER DON
+ "\u10E1", "\u10D3",
+ // U+10E4: "ფ" GEORGIAN LETTER PHAR
+ // U+10F6: "ჶ" GEORGIAN LETTER FI
+ key("\u10E4", moreKey("\u10F6")),
+ // U+10D2: "გ" GEORGIAN LETTER GAN
+ // U+10F9: "ჹ" GEORGIAN LETTER TURNED GAN
+ key("\u10D2", moreKey("\u10F9")),
+ // U+10F0: "ჰ" GEORGIAN LETTER HAE
+ // U+10F5: "ჵ" GEORGIAN LETTER HOE
+ key("\u10F0", moreKey("\u10F5")),
+ // U+10EF: "ჯ" GEORGIAN LETTER JHAN
+ // U+10F7: "ჷ" GEORGIAN LETTER YN
+ key("\u10EF", moreKey("\u10F7")),
+ // U+10D9: "კ" GEORGIAN LETTER KAN
+ // U+10DA: "ლ" GEORGIAN LETTER LAS
+ "\u10D9", "\u10DA")
+ .setKeysOfRow(3,
+ // U+10D6: "ზ" GEORGIAN LETTER ZEN
+ "\u10D6",
+ // U+10EE: "ხ" GEORGIAN LETTER XAN
+ // U+10F4: "ჴ" GEORGIAN LETTER HAR
+ key("\u10EE", moreKey("\u10F4")),
+ // U+10EA: "ც" GEORGIAN LETTER CAN
+ "\u10EA",
+ // U+10D5: "ვ" GEORGIAN LETTER VIN
+ // U+10F3: "ჳ" GEORGIAN LETTER WE
+ key("\u10D5", moreKey("\u10F3")),
+ // U+10D1: "ბ" GEORGIAN LETTER BAN
+ "\u10D1",
+ // U+10DC: "ნ" GEORGIAN LETTER NAR
+ // U+10FC: "ჼ" MODIFIER LETTER GEORGIAN NAR
+ key("\u10DC", moreKey("\u10FC")),
+ // U+10DB: "მ" GEORGIAN LETTER MAN
+ "\u10DB")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("Q", moreKey("1")),
+ // U+10ED: "ჭ" GEORGIAN LETTER CHAR
+ key("\u10ED", moreKey("2")),
+ key("E", moreKey("3")),
+ // U+10E6: "ღ" GEORGIAN LETTER GHAN
+ key("\u10E6", moreKey("4")),
+ // U+10D7: "თ" GEORGIAN LETTER TAN
+ key("\u10D7", moreKey("5")),
+ key("Y", moreKey("6")),
+ key("U", moreKey("7")),
+ key("I", moreKey("8")),
+ key("O", moreKey("9")),
+ key("P", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+10E8: "შ" GEORGIAN LETTER SHIN
+ // U+10DF: "ჟ" GEORGIAN LETTER ZHAR
+ "A", "\u10E8", "D", "F", "G", "H", "\u10DF", "K", "L")
+ .setKeysOfRow(3,
+ // U+10EB: "ძ" GEORGIAN LETTER JIL
+ // U+10E9: "ჩ" GEORGIAN LETTER CHIN
+ "\u10EB", "X", "\u10E9", "V", "B", "N", "M")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java
new file mode 100644
index 000000000..475052c75
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Greek alphabet keyboard.
+ */
+public final class Greek extends LayoutBase {
+ private static final String LAYOUT_NAME = "greek";
+
+ public Greek(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class GreekCustomizer extends EuroCustomizer {
+ public GreekCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return GREEK_ALPHABET_KEY; }
+
+ // U+0391: "Α" GREEK CAPITAL LETTER ALPHA
+ // U+0392: "Β" GREEK CAPITAL LETTER BETA
+ // U+0393: "Γ" GREEK CAPITAL LETTER GAMMA
+ private static final ExpectedKey GREEK_ALPHABET_KEY = key(
+ "\u0391\u0392\u0393", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ builder.replaceKeyOfLabel(ROW1_1, ROW1_1_SEMICOLON);
+ builder.replaceKeyOfLabel(ROW1_2, ROW1_2_FINAL_SIGMA);
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ builder.toUpperCase(getLocale());
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED
+ || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED) {
+ builder.replaceKeyOfLabel(ROW1_1, ROW1_1_COLON);
+ } else {
+ builder.replaceKeyOfLabel(ROW1_1, ROW1_1_SEMICOLON);
+ }
+ builder.replaceKeyOfLabel(ROW1_2, ROW1_2_FINAL_SIGMA);
+ return builder.build();
+ }
+
+ private static final String ROW1_1 = "ROW1_1";
+ private static final ExpectedKey ROW1_1_SEMICOLON = key(";", joinMoreKeys("1", ":"));
+ private static final ExpectedKey ROW1_1_COLON = key(":", joinMoreKeys("1", ";"));
+
+ private static final String ROW1_2 = "ROW2_2";
+ // U+03C2: "ς" GREEK SMALL LETTER FINAL SIGMA
+ private static final ExpectedKey ROW1_2_FINAL_SIGMA = key("\u03C2", moreKey("2"));
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key(ROW1_1, moreKey("1")),
+ key(ROW1_2, moreKey("2")),
+ // U+03B5: "ε" GREEK SMALL LETTER EPSILON
+ // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS
+ key("\u03B5", joinMoreKeys("\u03AD", "3")),
+ // U+03C1: "ρ" GREEK SMALL LETTER RHO
+ key("\u03C1", moreKey("4")),
+ // U+03C4: "τ" GREEK SMALL LETTER TAU
+ key("\u03C4", moreKey("5")),
+ // U+03C5: "υ" GREEK SMALL LETTER UPSILON
+ // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS
+ // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+ // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+ key("\u03C5", joinMoreKeys("\u03CD", "6", "\u03CB", "\u03B0")),
+ // U+03B8: "θ" GREEK SMALL LETTER THETA
+ key("\u03B8", moreKey("7")),
+ // U+03B9: "ι" GREEK SMALL LETTER IOTA
+ // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS
+ // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA
+ // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+ key("\u03B9", joinMoreKeys("\u03AF", "8", "\u03CA", "\u0390")),
+ // U+03BF: "ο" GREEK SMALL LETTER OMICRON
+ // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS
+ key("\u03BF", joinMoreKeys("\u03CC", "9")),
+ // U+03C0: "π" GREEK SMALL LETTER PI
+ key("\u03C0", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+03B1: "α" GREEK SMALL LETTER ALPHA
+ // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS
+ key("\u03B1", moreKey("\u03AC")),
+ // U+03C3: "σ" GREEK SMALL LETTER SIGMA
+ // U+03B4: "δ" GREEK SMALL LETTER DELTA
+ // U+03C6: "φ" GREEK SMALL LETTER PHI
+ // U+03B3: "γ" GREEK SMALL LETTER GAMMA
+ "\u03C3", "\u03B4", "\u03C6", "\u03B3",
+ // U+03B7: "η" GREEK SMALL LETTER ETA
+ // U+03AE: "ή" GREEK SMALL LETTER ETA WITH TONOS
+ key("\u03B7", moreKey("\u03AE")),
+ // U+03BE: "ξ" GREEK SMALL LETTER XI
+ // U+03BA: "κ" GREEK SMALL LETTER KAPPA
+ // U+03BB: "λ" GREEK SMALL LETTER LAMDA
+ "\u03BE", "\u03BA", "\u03BB")
+ .setKeysOfRow(3,
+ // U+03B6: "ζ" GREEK SMALL LETTER ZETA
+ // U+03C7: "χ" GREEK SMALL LETTER CHI
+ // U+03C8: "ψ" GREEK SMALL LETTER PSI
+ "\u03B6", "\u03C7", "\u03C8",
+ // U+03C9: "ω" GREEK SMALL LETTER OMEGA
+ // U+03CE: "ώ" GREEK SMALL LETTER OMEGA WITH TONOS
+ key("\u03C9", moreKey("\u03CE")),
+ // U+03B2: "β" GREEK SMALL LETTER BETA
+ // U+03BD: "ν" GREEK SMALL LETTER NU
+ // U+03BC: "μ" GREEK SMALL LETTER MU
+ "\u03B2", "\u03BD", "\u03BC")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java
new file mode 100644
index 000000000..552f0d3d5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public final class Hebrew extends LayoutBase {
+ private static final String LAYOUT_NAME = "hebrew";
+
+ public Hebrew(final LayoutCustomizer customizer) {
+ super(customizer, HebrewSymbols.class, RtlSymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class HebrewCustomizer extends LayoutCustomizer {
+ public HebrewCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return HEBREW_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_NEW_SHEQEL; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_LR9; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_LR9; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() {
+ return RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL;
+ }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() {
+ return RtlSymbols.SINGLE_ANGLE_QUOTES_LR_RTL;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? RTL_PHONE_PUNCTUATION_MORE_KEYS
+ : RTL_TABLET_PUNCTUATION_MORE_KEYS;
+ }
+
+ // U+05D0: "א" HEBREW LETTER ALEF
+ // U+05D1: "ב" HEBREW LETTER BET
+ // U+05D2: "ג" HEBREW LETTER GIMEL
+ private static final ExpectedKey HEBREW_ALPHABET_KEY = key(
+ "\u05D0\u05D1\u05D2", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ // U+20AA: "₪" NEW SHEQEL SIGN
+ private static final ExpectedKey CURRENCY_NEW_SHEQEL = key("\u20AA",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ private static final ExpectedKey[] RTL_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "?", "!", "#", key(")", "("), key("(", ")"), "/", ";",
+ "'", "@", ":", "-", "\"", "+", "%", "&");
+ // Punctuation more keys for tablet form factor.
+ private static final ExpectedKey[] RTL_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "'", "#", key(")", "("), key("(", ")"), "/", ";",
+ "@", ":", "-", "\"", "+", "%", "&");
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("'", joinMoreKeys("1", "\"")),
+ key("-", joinMoreKeys("2", "_")),
+ // U+05E7: "ק" HEBREW LETTER QOF
+ key("\u05E7", moreKey("3")),
+ // U+05E8: "ר" HEBREW LETTER RESH
+ key("\u05E8", moreKey("4")),
+ // U+05D0: "א" HEBREW LETTER ALEF
+ key("\u05D0", moreKey("5")),
+ // U+05D8: "ט" HEBREW LETTER TET
+ key("\u05D8", moreKey("6")),
+ // U+05D5: "ו" HEBREW LETTER VAV
+ key("\u05D5", moreKey("7")),
+ // U+05DF: "ן" HEBREW LETTER FINAL NUN
+ key("\u05DF", moreKey("8")),
+ // U+05DD: "ם" HEBREW LETTER FINAL MEM
+ key("\u05DD", moreKey("9")),
+ // U+05E4: "פ" HEBREW LETTER PE
+ key("\u05E4", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+05E9: "ש" HEBREW LETTER SHIN
+ // U+05D3: "ד" HEBREW LETTER DALET
+ "\u05E9", "\u05D3",
+ // U+05D2: "ג" HEBREW LETTER GIMEL
+ // U+05D2 U+05F3: "ג׳" HEBREW LETTER GIMEL + HEBREW PUNCTUATION GERESH
+ key("\u05D2", moreKey("\u05D2\u05F3")),
+ // U+05DB: "כ" HEBREW LETTER KAF
+ // U+05E2: "ע" HEBREW LETTER AYIN
+ "\u05DB", "\u05E2",
+ // U+05D9: "י" HEBREW LETTER YOD
+ // U+05F2 U+05B7: "ײַ" HEBREW LIGATURE YIDDISH DOUBLE YOD + HEBREW POINT PATAH
+ key("\u05D9", moreKey("\u05F2\u05B7")),
+ // U+05D7: "ח" HEBREW LETTER HET
+ // U+05D7 U+05F3: "ח׳" HEBREW LETTER HET + HEBREW PUNCTUATION GERESH
+ key("\u05D7", moreKey("\u05D7\u05F3")),
+ // U+05DC: "ל" HEBREW LETTER LAMED
+ // U+05DA: "ך" HEBREW LETTER FINAL KAF
+ // U+05E3: "ף" HEBREW LETTER FINAL PE
+ "\u05DC", "\u05DA", "\u05E3")
+ .setKeysOfRow(3,
+ // U+05D6: "ז" HEBREW LETTER ZAYIN
+ // U+05D6 U+05F3: "ז׳" HEBREW LETTER ZAYIN + HEBREW PUNCTUATION GERESH
+ key("\u05D6", moreKey("\u05D6\u05F3")),
+ // U+05E1: "ס" HEBREW LETTER SAMEKH
+ // U+05D1: "ב" HEBREW LETTER BET
+ // U+05D4: "ה" HEBREW LETTER HE
+ // U+05E0: "נ" HEBREW LETTER NUN
+ // U+05DE: "מ" HEBREW LETTER MEM
+ "\u05E1", "\u05D1", "\u05D4", "\u05E0", "\u05DE",
+ // U+05E6: "צ" HEBREW LETTER TSADI
+ // U+05E6 U+05F3: "צ׳" HEBREW LETTER TSADI + HEBREW PUNCTUATION GERESH
+ key("\u05E6", moreKey("\u05E6\u05F3")),
+ // U+05EA: "ת" HEBREW LETTER TAV
+ // U+05EA U+05F3: "ת׳" HEBREW LETTER TAV + HEBREW PUNCTUATION GERESH
+ key("\u05EA", moreKey("\u05EA\u05F3")),
+ // U+05E5: "ץ" HEBREW LETTER FINAL TSADI
+ // U+05E5 U+05F3: "ץ׳" HEBREW LETTER FINAL TSADI + HEBREW PUNCTUATION GERESH
+ key("\u05E5", moreKey("\u05E5\u05F3")))
+ .build();
+
+ private static class HebrewSymbols extends RtlSymbols {
+ public HebrewSymbols(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+00B1: "±" PLUS-MINUS SIGN
+ // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
+ .setMoreKeysOf("+", "\u00B1", "\uFB29")
+ // U+2605: "★" BLACK STAR
+ .setMoreKeysOf("*", "\u2605")
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java b/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java
new file mode 100644
index 000000000..b12b8be64
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Hindi keyboard.
+ */
+public final class Hindi extends LayoutBase {
+ private static final String LAYOUT_NAME = "hindi";
+
+ public Hindi(final LayoutCustomizer customizer) {
+ super(customizer, HindiSymbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class HindiCustomizer extends LayoutCustomizer {
+ public HindiCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return HINDI_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getSymbolsKey() { return HINDI_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey getBackToSymbolsKey() { return HINDI_BACK_TO_SYMBOLS_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ // U+0917: "ग" DEVANAGARI LETTER GA
+ private static final ExpectedKey HINDI_ALPHABET_KEY = key(
+ "\u0915\u0916\u0917", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ private static final String HINDI_SYMBOLS_LABEL = "?\u0967\u0968\u0969";
+ private static final ExpectedKey HINDI_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
+ Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ private static final ExpectedKey HINDI_BACK_TO_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
+ Constants.CODE_SHIFT);
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ // U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ key(VOWEL_SIGN_AU, "\u094C", joinMoreKeys(
+ moreKey(VOWEL_SIGN_AU + "\u0902", "\u094C\u0902"),
+ "\u0967", "1")),
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ // U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ key(VOWEL_SIGN_AI, "\u0948", joinMoreKeys(
+ moreKey(VOWEL_SIGN_AI + "\u0902", "\u0948\u0902"),
+ "\u0968", "2")),
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ // U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
+ // U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ key(VOWEL_SIGN_AA, "\u093E", joinMoreKeys(
+ moreKey(VOWEL_SIGN_AA + "\u0902", "\u093E\u0902"),
+ moreKey(VOWEL_SIGN_AA + "\u0901", "\u093E\u0901"),
+ "\u0969", "3")),
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ // U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ key(VOWEL_SIGN_II, "\u0940", joinMoreKeys(
+ moreKey(VOWEL_SIGN_II + "\u0902", "\u0940\u0902"),
+ "\u096A", "4")),
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ // U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
+ // U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ key(VOWEL_SIGN_UU, "\u0942", joinMoreKeys(
+ moreKey(VOWEL_SIGN_UU + "\u0902", "\u0942\u0902"),
+ moreKey(VOWEL_SIGN_UU + "\u0901", "\u0942\u0901"),
+ "\u096B", "5")),
+ // U+092C: "ब" DEVANAGARI LETTER BA
+ // U+092C/U+0952: "ब॒" DEVANAGARI LETTER BA/DEVANAGARI STRESS SIGN ANUDATTA
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ key("\u092C", joinMoreKeys("\u092C\u0952", "\u096C", "6")),
+ // U+0939: "ह" DEVANAGARI LETTER HA
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ key("\u0939", joinMoreKeys("\u096D", "7")),
+ // U+0917: "ग" DEVANAGARI LETTER GA
+ // U+091C/U+094D/U+091E:
+ // "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+ // U+0917/U+093C: "ग़" DEVANAGARI LETTER GA/DEVANAGARI SIGN NUKTA
+ // U+0917/U+0952: "ग॒" DEVANAGARI LETTER GA/DEVANAGARI STRESS SIGN ANUDATTA
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ key("\u0917", joinMoreKeys("\u091C\u094D\u091E", "\u0917\u093C", "\u0917\u0952",
+ "\u096E", "8")),
+ // U+0926: "द" DEVANAGARI LETTER DA
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ key("\u0926", joinMoreKeys("\u096F", "9")),
+ // U+091C: "ज" DEVANAGARI LETTER JA
+ // U+091C/U+0952: "ज॒" DEVANAGARI LETTER JA/DEVANAGARI STRESS SIGN ANUDATTA
+ // U+091C/U+094D/U+091E:
+ // "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+ // U+091C/U+093C: "ज़" DEVANAGARI LETTER JA/DEVANAGARI SIGN NUKTA
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ key("\u091C", joinMoreKeys("\u091C\u0952", "\u091C\u094D\u091E", "\u091C\u093C",
+ "\u0966", "0")),
+ // U+0921: "ड" DEVANAGARI LETTER DDA
+ // U+0921/U+0952: "ड॒" DEVANAGARI LETTER DDA/DEVANAGARI STRESS SIGN ANUDATTA
+ // U+0921/U+093C: "ड़" DEVANAGARI LETTER DDA/DEVANAGARI SIGN NUKTA
+ key("\u0921", joinMoreKeys("\u0921\u0952", "\u0921\u093C")))
+ .setKeysOfRow(2,
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ // U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
+ // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+ // U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O
+ key(VOWEL_SIGN_O, "\u094B", joinMoreKeys(
+ moreKey(VOWEL_SIGN_O + "\u0902", "\u094B\u0902"),
+ moreKey(VOWEL_SIGN_CANDRA_O, "\u0949"),
+ moreKey(VOWEL_SIGN_SHORT_O, "\u094A"))),
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ // U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA
+ key(VOWEL_SIGN_E, "\u0947",
+ moreKey(VOWEL_SIGN_E + "\u0902", "\u0947\u0902")),
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ key(SIGN_VIRAMA, "\u094D"),
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ // U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA
+ key(VOWEL_SIGN_I, "\u093F",
+ moreKey("\u093F" + SIGN_ANUSVARA, "\u093F\u0902")),
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ // U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
+ // U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU
+ key(VOWEL_SIGN_U, "\u0941", joinMoreKeys(
+ moreKey(VOWEL_SIGN_U + "\u0902", "\u0941\u0902"),
+ moreKey(VOWEL_SIGN_U + "\u0901", "\u0941\u0901"))),
+ // U+092A: "प" DEVANAGARI LETTER PA
+ "\u092A",
+ // U+0930: "र" DEVANAGARI LETTER RA
+ // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+ // U+0930/U+093C: "ऱ" DEVANAGARI LETTER RA/DEVANAGARI SIGN NUKTA
+ // U+0960: "ॠ" DEVANAGARI LETTER VOCALIC RR
+ key("\u0930", joinMoreKeys("\u090B", "\u0930\u093C", "\u0960")),
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+0915/U+093C: "क़" DEVANAGARI LETTER KA/DEVANAGARI SIGN NUKTA
+ key("\u0915", moreKey("\u0915\u093C")),
+ // U+0924: "त" DEVANAGARI LETTER TA
+ // U+0924/U+094D/U+0930:
+ // "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0924", moreKey("\u0924\u094D\u0930")),
+ // U+091A: "च" DEVANAGARI LETTER CA
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ "\u091A","\u091F")
+ .setKeysOfRow(3,
+ // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+ key(VOWEL_SIGN_CANDRA_O, "\u0949"),
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ key(SIGN_ANUSVARA, "\u0902"),
+ // U+092E: "म" DEVANAGARI LETTER MA
+ // U+0950: "ॐ" DEVANAGARI OM
+ key("\u092E", moreKey("\u0950")),
+ // U+0928: "न" DEVANAGARI LETTER NA
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ // U+0928/U+093C: "ऩ" DEVANAGARI LETTER NA/DEVANAGARI SIGN NUKTA
+ key("\u0928", joinMoreKeys("\u091E", "\u0919", "\u0928\u093C")),
+ // U+0935: "व" DEVANAGARI LETTER VA
+ "\u0935",
+ // U+0932: "ल" DEVANAGARI LETTER LA
+ // U+090C: "ऌ" DEVANAGARI LETTER VOCALIC L
+ // U+0961: "ॡ" DEVANAGARI LETTER VOCALIC LL
+ key("\u0932", joinMoreKeys("\u090C", "\u0961")),
+ // U+0938: "स" DEVANAGARI LETTER SA
+ "\u0938",
+ // U+092F: "य" DEVANAGARI LETTER YA
+ // U+095F: "य़" DEVANAGARI LETTER YYA
+ key("\u092F", moreKey("\u095F")),
+ // U+093C: "़" DEVANAGARI SIGN NUKTA
+ // U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
+ // U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
+ // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+ key(SIGN_NUKTA, "\u093C", joinMoreKeys(
+ moreKey(LETTER_GLOTTAL_STOP, "\u097D"),
+ moreKey(ABBREVIATION_SIGN, "\u0970"),
+ moreKey(SIGN_AVAGRAHA, "\u093D"))))
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0914: "औ" DEVANAGARI LETTER AU
+ // U+0912/U+0902: "ऒं" DEVANAGARI LETTER SHORT O//DEVANAGARI SIGN ANUSVARA
+ key("\u0914", moreKey("\u0912\u0902")),
+ // U+0910: "ऐ" DEVANAGARI LETTER AI
+ // U+0910/U+0902: "ऐं" DEVANAGARI LETTER AI/DEVANAGARI SIGN ANUSVARA
+ key("\u0910", moreKey("\u0910\u0902")),
+ // U+0906: "आ" DEVANAGARI LETTER AA
+ // U+0906/U+0902: "आं" DEVANAGARI LETTER AA/DEVANAGARI SIGN ANUSVARA
+ // U+0906/U+0901: "आँ" DEVANAGARI LETTER AA/DEVANAGARI SIGN CANDRABINDU
+ key("\u0906", joinMoreKeys("\u0906\u0902", "\u0906\u0901")),
+ // U+0908: "ई" DEVANAGARI LETTER II
+ // U+0908/U+0902: "ईं" DEVANAGARI LETTER II/DEVANAGARI SIGN ANUSVARA
+ key("\u0908", moreKey("\u0908\u0902")),
+ // U+090A: "ऊ" DEVANAGARI LETTER UU
+ // U+090A/U+0902: "ऊं" DEVANAGARI LETTER UU/DEVANAGARI SIGN ANUSVARA
+ // U+090A/U+0901: "ऊँ" DEVANAGARI LETTER UU/DEVANAGARI SIGN CANDRABINDU
+ key("\u090A", joinMoreKeys("\u090A\u0902", "\u090A\u0901")),
+ // U+092D: "भ" DEVANAGARI LETTER BHA
+ // U+0903: "ः" DEVANAGARI SIGN VISARGA
+ // U+0918: "घ" DEVANAGARI LETTER GHA
+ "\u092D", key(SIGN_VISARGA, "\u0903"), "\u0918",
+ // U+0927: "ध" DEVANAGARI LETTER DHA
+ // U+0915/U+094D/U+0937:
+ // "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+ // U+0936/U+094D/U+0930:
+ // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0927", joinMoreKeys("\u0915\u094D\u0937", "\u0936\u094D\u0930")),
+ // U+091D: "झ" DEVANAGARI LETTER JHA
+ // U+0922: "ढ" DEVANAGARI LETTER DDHA
+ "\u091D", "\u0922")
+ .setKeysOfRow(2,
+ // U+0913: "ओ" DEVANAGARI LETTER O
+ // U+0913/U+0902: "ओं" DEVANAGARI LETTER O/DEVANAGARI SIGN ANUSVARA
+ // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
+ // U+0912: "ऒ" DEVANAGARI LETTER SHORT O
+ key("\u0913", joinMoreKeys("\u0913\u0902", "\u0911", "\u0912")),
+ // U+090F: "ए" DEVANAGARI LETTER E
+ // U+090F/U+0902: "एं" DEVANAGARI LETTER E/DEVANAGARI SIGN ANUSVARA
+ // U+090F/U+0901: "एँ" DEVANAGARI LETTER E/DEVANAGARI SIGN CANDRABINDU
+ // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+ // U+090E: "ऎ" DEVANAGARI LETTER SHORT E
+ key("\u090F", joinMoreKeys("\u090F\u0902", "\u090F\u0901", "\u090D", "\u090E")),
+ // U+0905: "अ" DEVANAGARI LETTER A
+ // U+0905/U+0902: "अं" DEVANAGARI LETTER A/DEVANAGARI SIGN ANUSVARA
+ // U+0905/U+0901: "अँ" DEVANAGARI LETTER A/DEVANAGARI SIGN CANDRABINDU
+ key("\u0905", joinMoreKeys("\u0905\u0902", "\u0905\u0901")),
+ // U+0907: "इ" DEVANAGARI LETTER I
+ // U+0907/U+0902: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN ANUSVARA
+ // U+0907/U+0901: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN CANDRABINDU
+ key("\u0907", joinMoreKeys("\u0907\u0902", "\u0907\u0901")),
+ // U+0909: "उ" DEVANAGARI LETTER U
+ // U+0909/U+0902: "उं" DEVANAGARI LETTER U/DEVANAGARI SIGN ANUSVARA
+ // U+0909/U+0901: "उँ" DEVANAGARI LETTER U/DEVANAGARI SIGN CANDRABINDU
+ key("\u0909", joinMoreKeys("\u0909\u0902", "\u0909\u0901")),
+ // U+092B: "फ" DEVANAGARI LETTER PHA
+ // U+092B/U+093C: "फ़" DEVANAGARI LETTER PHA/DEVANAGARI SIGN NUKTA
+ key("\u092B", moreKey("\u092B\u093C")),
+ // U+0931: "ऱ" DEVANAGARI LETTER RRA
+ // U+094D/U+0930: "्र" DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ // U+0930/U+094D: "र्" DEVANAGARI LETTER RA/DEVANAGARI SIGN VIRAMA
+ key("\u0931", joinMoreKeys("\u094D\u0930", "\u0930\u094D")),
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ // U+0916/U+093C: "ख़" DEVANAGARI LETTER KHA/DEVANAGARI SIGN NUKTA
+ key("\u0916", moreKey("\u0916\u093C")),
+ // U+0925: "थ" DEVANAGARI LETTER THA
+ // U+091B: "छ" DEVANAGARI LETTER CHA
+ // U+0920: "ठ" DEVANAGARI LETTER TTHA
+ "\u0925", "\u091B", "\u0920")
+ .setKeysOfRow(3,
+ // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
+ "\u0911",
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+ key(SIGN_CANDRABINDU, "\u0901", moreKey(VOWEL_SIGN_CANDRA_E, "\u0945")),
+ // U+0923: "ण" DEVANAGARI LETTER NNA
+ // U+0929: "ऩ" DEVANAGARI LETTER NNNA
+ "\u0923", "\u0929",
+ // U+0933: "ळ" DEVANAGARI LETTER LLA
+ // U+0934: "ऴ" DEVANAGARI LETTER LLLA
+ key("\u0933", moreKey("\u0934")),
+ // U+0936: "श" DEVANAGARI LETTER SHA
+ // U+0937: "ष" DEVANAGARI LETTER SSA
+ "\u0936", "\u0937",
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ // U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR
+ key(VOWEL_SIGN_VOCALIC_R, "\u0943", moreKey(VOWEL_SIGN_VOCALIC_RR, "\u0944")),
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ "\u091E")
+ .build();
+
+ static class HindiSymbols extends Symbols {
+ public HindiSymbols(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ .replaceKeyOfLabel("1", key("\u0967",
+ joinMoreKeys("1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")))
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ .replaceKeyOfLabel("2", key("\u0968", joinMoreKeys("2", "\u00B2", "\u2154")))
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ .replaceKeyOfLabel("3", key("\u0969",
+ joinMoreKeys("3", "\u00B3", "\u00BE","\u215C")))
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ .replaceKeyOfLabel("4", key("\u096A", joinMoreKeys("4", "\u2074")))
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ .replaceKeyOfLabel("5", key("\u096B", joinMoreKeys("5", "\u215D")))
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ .replaceKeyOfLabel("6", key("\u096C", moreKey("6")))
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ .replaceKeyOfLabel("7", key("\u096D", joinMoreKeys("7", "\u215E")))
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ .replaceKeyOfLabel("8", key("\u096E", moreKey("8")))
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ .replaceKeyOfLabel("9", key("\u096F", moreKey("9")))
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ .replaceKeyOfLabel("0", key("\u0966", joinMoreKeys("0", "\u207F", "\u2205")))
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
new file mode 100644
index 000000000..2b625c32b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
+
+import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The Hindi Compact keyboard.
+ */
+public final class HindiCompact extends LayoutBase {
+ private static final String LAYOUT_NAME = "hindi_compact";
+
+ public HindiCompact(final LayoutCustomizer customizer) {
+ super(customizer, HindiSymbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class HindiCompactCustomizer extends HindiCustomizer {
+ public HindiCompactCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ // U+0964: "।" DEVANAGARI DANDA
+ final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone));
+ return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey);
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? HINDI_PHONE_PUNCTUATION_MORE_KEYS : HINDI_TABLET_PUNCTUATION_MORE_KEYS;
+ }
+
+ // Punctuation more keys for phone form factor.
+ private static final ExpectedKey[] HINDI_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", ".", "?", "!", "#", ")", "(", "/", ";",
+ "'", "@", ":", "-", "\"", "+", "%", "&");
+ // Punctuation more keys for tablet form factor.
+ private static final ExpectedKey[] HINDI_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", ".", "'", "#", ")", "(", "/", ";",
+ "@", ":", "-", "\"", "+", "%", "&");
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0914: "औ" DEVANAGARI LETTER AU
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ key("\u0914", joinMoreKeys(moreKey(VOWEL_SIGN_AU, "\u094C"), "\u0967", "1")),
+ // U+0910: "ऐ" DEVANAGARI LETTER AI
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ key("\u0910", joinMoreKeys(moreKey(VOWEL_SIGN_AI, "\u0948"), "\u0968", "2")),
+ // U+0906: "आ" DEVANAGARI LETTER AA
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ key("\u0906", joinMoreKeys(moreKey(VOWEL_SIGN_AA, "\u093E"), "\u0969", "3")),
+ // U+0908: "ई" DEVANAGARI LETTER II
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ key("\u0908", joinMoreKeys(moreKey(VOWEL_SIGN_II, "\u0940"), "\u096A", "4")),
+ // U+090A: "ऊ" DEVANAGARI LETTER UU
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ key("\u090A", joinMoreKeys(moreKey(VOWEL_SIGN_UU, "\u0942"), "\u096B", "5")),
+ // U+092C: "ब" DEVANAGARI LETTER BA
+ // U+092D: "भ" DEVANAGARI LETTER BHA
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ key("\u092C", joinMoreKeys("\u092D", "\u096C", "6")),
+ // U+0939: "ह" DEVANAGARI LETTER HA
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ key("\u0939", joinMoreKeys("\u096D", "7")),
+ // U+0917: "ग" DEVANAGARI LETTER GA
+ // U+0918: "घ" DEVANAGARI LETTER GHA
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ key("\u0917", joinMoreKeys("\u0918", "\u096E", "8")),
+ // U+0926: "द" DEVANAGARI LETTER DA
+ // U+0927: "ध" DEVANAGARI LETTER DHA
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ key("\u0926", joinMoreKeys("\u0927", "\u096F", "9")),
+ // U+091C: "ज" DEVANAGARI LETTER JA
+ // U+091D: "झ" DEVANAGARI LETTER JHA
+ // U+091C/U+094D/U+091E:
+ // "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ key("\u091C", joinMoreKeys("\u091D", "\u091C\u094D\u091E", "\u0966", "0")),
+ // U+0921: "ड" DEVANAGARI LETTER DDA
+ // U+0922: "ढ" DEVANAGARI LETTER DDHA
+ key("\u0921", moreKey("\u0922")))
+ .setKeysOfRow(2,
+ // U+0913: "ओ" DEVANAGARI LETTER O
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ key("\u0913", moreKey(VOWEL_SIGN_O, "\u094B")),
+ // U+090F: "ए" DEVANAGARI LETTER E
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ key("\u090F", moreKey(VOWEL_SIGN_E, "\u0947")),
+ // U+0905: "अ" DEVANAGARI LETTER A
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ key("\u0905", moreKey(SIGN_VIRAMA, "\u094D")),
+ // U+0907: "इ" DEVANAGARI LETTER I
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ key("\u0907", moreKey(VOWEL_SIGN_I, "\u093F")),
+ // U+0909: "उ" DEVANAGARI LETTER U
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ key("\u0909", moreKey(VOWEL_SIGN_U, "\u0941")),
+ // U+092A: "प" DEVANAGARI LETTER PA
+ // U+092B: "फ" DEVANAGARI LETTER PHA
+ key("\u092A", moreKey("\u092B")),
+ // U+0930: "र" DEVANAGARI LETTER RA
+ // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ key("\u0930", joinMoreKeys("\u090B", moreKey(VOWEL_SIGN_VOCALIC_R, "\u0943"))),
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ key("\u0915", moreKey("\u0916")),
+ // U+0924: "त" DEVANAGARI LETTER TA
+ // U+0925: "थ" DEVANAGARI LETTER THA
+ // U+0924/U+094D/U+0930:
+ // "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0924", joinMoreKeys("\u0925", "\u0924\u094D\u0930")),
+ // U+091A: "च" DEVANAGARI LETTER CA
+ // U+091B: "छ" DEVANAGARI LETTER CHA
+ key("\u091A", moreKey("\u091B")),
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ // U+0920: "ठ" DEVANAGARI LETTER TTHA
+ key("\u091F", moreKey("\u0920")))
+ .setKeysOfRow(3,
+ // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
+ // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+ key("\u0911", moreKey(VOWEL_SIGN_CANDRA_O, "\u0949")),
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+ // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+ key(SIGN_VIRAMA, "\u094D", joinMoreKeys(
+ moreKey(VOWEL_SIGN_CANDRA_E, "\u0945"), "\u090D")),
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ // U+093C: "़" DEVANAGARI SIGN NUKTA
+ key(SIGN_ANUSVARA, "\u0902", joinMoreKeys(
+ moreKey(SIGN_VISARGA, "\u0903"),
+ moreKey(SIGN_CANDRABINDU, "\u0901"),
+ moreKey(SIGN_NUKTA, "\u093C"))),
+ // U+092E: "म" DEVANAGARI LETTER MA
+ // U+0950: "ॐ" DEVANAGARI OM
+ key("\u092E", moreKey("\u0950")),
+ // U+0928: "न" DEVANAGARI LETTER NA
+ // U+0923: "ण" DEVANAGARI LETTER NNA
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ key("\u0928", joinMoreKeys("\u0923", "\u091E", "\u0919")),
+ // U+0935: "व" DEVANAGARI LETTER VA
+ // U+0932: "ल" DEVANAGARI LETTER LA
+ "\u0935", "\u0932",
+ // U+0938: "स" DEVANAGARI LETTER SA
+ // U+0936: "श" DEVANAGARI LETTER SHA
+ // U+0937: "ष" DEVANAGARI LETTER SSA
+ // U+0936/U+094D/U+0930:
+ // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0938", joinMoreKeys("\u0936", "\u0937", "\u0936\u094D\u0930")),
+ // U+092F: "य" DEVANAGARI LETTER YA
+ // U+0915/U+094D/U+0937:
+ // "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+ "\u092F", "\u0915\u094D\u0937")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java
new file mode 100644
index 000000000..5ce7f4d9c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Kannada keyboard.
+ */
+public final class Kannada extends LayoutBase {
+ private static final String LAYOUT_NAME = "kannada";
+
+ public Kannada(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class KannadaCustomizer extends LayoutCustomizer {
+ public KannadaCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return KANNADA_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ @Override
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+ }
+
+ // U+0C85: "ಅ" KANNADA LETTER A
+ // U+0C86: "ಆ" KANNADA LETTER AA
+ // U+0C87: "ಇ" KANNADA LETTER I
+ private static final ExpectedKey KANNADA_ALPHABET_KEY = key(
+ "\u0C85\u0C86\u0C87", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0CCC: "ೌ" KANNADA VOWEL SIGN AU
+ // U+0C94: "ಔ" KANNADA LETTER AU
+ // U+0CE7: "೧" KANNADA DIGIT ONE
+ key("\u0CCC", joinMoreKeys("\u0C94", "\u0CE7", "1")),
+ // U+0CC8: "ೈ" KANNADA VOWEL SIGN AI
+ // U+0C90: "ಐ" KANNADA LETTER AI
+ // U+0CE8: "೨" KANNADA DIGIT TWO
+ key("\u0CC8", joinMoreKeys("\u0C90", "\u0CE8", "2")),
+ // U+0CBE: "ಾ" KANNADA VOWEL SIGN AA
+ // U+0C86: "ಆ" KANNADA LETTER AA
+ // U+0CE9: "೩" KANNADA DIGIT THREE
+ key("\u0CBE", joinMoreKeys("\u0C86", "\u0CE9", "3")),
+ // U+0CC0: "ೀ" KANNADA VOWEL SIGN II
+ // U+0C88: "ಈ" KANNADA LETTER II
+ // U+0CEA: "೪" KANNADA DIGIT FOUR
+ key("\u0CC0", joinMoreKeys("\u0C88", "\u0CEA", "4")),
+ // U+0CC2: "ೂ" KANNADA VOWEL SIGN UU
+ // U+0C8A: "ಊ" KANNADA LETTER UU
+ // U+0CEB: "೫" KANNADA DIGIT FIVE
+ key("\u0CC2", joinMoreKeys("\u0C8A", "\u0CEB", "5")),
+ // U+0CAC: "ಬ" KANNADA LETTER BA
+ // U+0CAD: "ಭ" KANNADA LETTER BHA
+ // U+0CEC: "೬" KANNADA DIGIT SIX
+ key("\u0CAC", joinMoreKeys("\u0CAD", "\u0CEC", "6")),
+ // U+0CB9: "ಹ" KANNADA LETTER HA
+ // U+0C99: "ಙ" KANNADA LETTER NGA
+ // U+0CED: "೭" KANNADA DIGIT SEVEN
+ key("\u0CB9", joinMoreKeys("\u0C99", "\u0CED", "7")),
+ // U+0C97: "ಗ" KANNADA LETTER GA
+ // U+0C98: "ಘ" KANNADA LETTER GHA
+ // U+0CEE: "೮" KANNADA DIGIT EIGHT
+ key("\u0C97", joinMoreKeys("\u0C98", "\u0CEE", "8")),
+ // U+0CA6: "ದ" KANNADA LETTER DA
+ // U+0CA7: "ಧ" KANNADA LETTER DHA
+ // U+0CEF: "೯" KANNADA DIGIT NINE
+ key("\u0CA6", joinMoreKeys("\u0CA7", "\u0CEF", "9")),
+ // U+0C9C: "ಜ" KANNADA LETTER JA
+ // U+0C9D: "ಝ" KANNADA LETTER JHA
+ // U+0CE6: "೦" KANNADA DIGIT ZERO
+ key("\u0C9C", joinMoreKeys("\u0C9D", "\u0CE6", "0")),
+ // U+0CA1: "ಡ" KANNADA LETTER DDA
+ // U+0CA2: "ಢ" KANNADA LETTER DDHA
+ key("\u0CA1", moreKey("\u0CA2")))
+ .setKeysOfRow(2,
+ // U+0CCB: "ೋ" KANNADA VOWEL SIGN OO
+ // U+0C93: "ಓ" KANNADA LETTER OO
+ key("\u0CCB", moreKey("\u0C93")),
+ // U+0CC7: "ೇ" KANNADA VOWEL SIGN EE
+ // U+0C8F: "ಏ" KANNADA LETTER EE
+ key("\u0CC7", moreKey("\u0C8F")),
+ // U+0CCD: "್" KANNADA SIGN VIRAMA
+ // U+0C85: "ಅ" KANNADA LETTER A
+ key("\u0CCD", moreKey("\u0C85")),
+ // U+0CBF: "ಿ" KANNADA VOWEL SIGN I
+ // U+0C87: "ಇ" KANNADA LETTER I
+ key("\u0CBF", moreKey("\u0C87")),
+ // U+0CC1: "ು" KANNADA VOWEL SIGN U
+ // U+0C89: "ಉ" KANNADA LETTER U
+ key("\u0CC1", moreKey("\u0C89")),
+ // U+0CAA: "ಪ" KANNADA LETTER PA
+ // U+0CAB: "ಫ" KANNADA LETTER PHA
+ key("\u0CAA", moreKey("\u0CAB")),
+ // U+0CB0: "ರ" KANNADA LETTER RA
+ // U+0CB1: "ಱ" KANNADA LETTER RRA
+ // U+0CC3: "ೃ" KANNADA VOWEL SIGN VOCALIC R
+ key("\u0CB0", joinMoreKeys("\u0CB1", "\u0CC3")),
+ // U+0C95: "ಕ" KANNADA LETTER KA
+ // U+0C96: "ಖ" KANNADA LETTER KHA
+ key("\u0C95", moreKey("\u0C96")),
+ // U+0CA4: "ತ" KANNADA LETTER TA
+ // U+0CA5: "ಥ" KANNADA LETTER THA
+ key("\u0CA4", moreKey("\u0CA5")),
+ // U+0C9A: "ಚ" KANNADA LETTER CA
+ // U+0C9B: "ಛ" KANNADA LETTER CHA
+ key("\u0C9A", moreKey("\u0C9B")),
+ // U+0C9F: "ಟ" KANNADA LETTER TTA
+ // U+0CA0: "ಠ" KANNADA LETTER TTHA
+ key("\u0C9F", moreKey("\u0CA0")))
+ .setKeysOfRow(3,
+ // U+0CC6: "ೆ" KANNADA VOWEL SIGN E
+ // U+0C92: "ಒ" KANNADA LETTER O
+ key("\u0CC6", moreKey("\u0C92")),
+ // U+0C82: "ಂ" KANNADA SIGN ANUSVARA
+ // U+0C8E: "ಎ" KANNADA LETTER E
+ key("\u0C82", moreKey("\u0C8E")),
+ // U+0CAE: "ಮ" KANNADA LETTER MA
+ // U+0CA3: "ಣ" KANNADA LETTER NNA
+ key("\u0CAE", moreKey("\u0CA3")),
+ // U+0CA8: "ನ" KANNADA LETTER NA
+ // U+0CB5: "ವ" KANNADA LETTER VA
+ "\u0CA8", "\u0CB5",
+ // U+0CB2: "ಲ" KANNADA LETTER LA
+ // U+0CB3: "ಳ" KANNADA LETTER LLA
+ key("\u0CB2", moreKey("\u0CB3")),
+ // U+0CB8: "ಸ" KANNADA LETTER SA
+ // U+0CB6: "ಶ" KANNADA LETTER SHA
+ key("\u0CB8", moreKey("\u0CB6")),
+ // U+0C8B: "ಋ" KANNADA LETTER VOCALIC R
+ // U+0CCD/U+0CB0: "್ರ" KANNADA SIGN VIRAMA/KANNADA LETTER RA
+ key("\u0C8B", moreKey("\u0CCD\u0CB0")),
+ // U+0CB7: "ಷ" KANNADA LETTER SSA
+ // U+0C95/U+0CCD/U+0CB7:
+ // "ಕ್ಷ" KANNADA LETTER RA/KANNADA SIGN VIRAMA/KANNADA LETTER SSA
+ key("\u0CB7", moreKey("\u0C95\u0CCD\u0CB7")),
+ // U+0CAF: "ಯ" KANNADA LETTER YA
+ // U+0C9C/U+0CCD/U+0C9E:
+ // "ಜ್ಞ" KANNADA LETTER JA/KANNADA SIGN VIRAMA/KANNADA LETTER NYA
+ key("\u0CAF", moreKey("\u0C9C\u0CCD\u0C9E")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java
new file mode 100644
index 000000000..143ccf6eb
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Khmer alphabet keyboard.
+ */
+public final class Khmer extends LayoutBase {
+ private static final String LAYOUT_NAME = "khmer";
+
+ public Khmer(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class KhmerCustomizer extends LayoutCustomizer {
+ public KhmerCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return KHMER_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_DOLLAR_WITH_RIEL; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
+
+ // U+1780: "ក" KHMER LETTER KA
+ // U+1781: "ខ" KHMER LETTER KHA
+ // U+1782: "គ" KHMER LETTER KO
+ private static final ExpectedKey KHMER_ALPHABET_KEY = key(
+ "\u1780\u1781\u1782", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
+ private static final ExpectedKey CURRENCY_DOLLAR_WITH_RIEL = key(Symbols.DOLLAR_SIGN,
+ moreKey("\u17DB"), Symbols.CENT_SIGN, Symbols.POUND_SIGN, Symbols.EURO_SIGN,
+ Symbols.YEN_SIGN, Symbols.PESO_SIGN);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ if (isPhone) {
+ return ALPHABET_COMMON;
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ builder.addKeysOnTheRightOfRow(4, (Object[])EXCLAMATION_AND_QUESTION_MARKS);
+ return builder.build();
+ }
+
+ @Override
+ public ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone,
+ final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(4, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+17E1: "១" KHMER DIGIT ONE
+ // U+17F1: "៱" KHMER SYMBOL LEK ATTAK MUOY
+ key("\u17E1", joinMoreKeys("1", "\u17F1")),
+ // U+17E2: "២" KHMER DIGIT TWO
+ // U+17F2: "៲" KHMER SYMBOL LEK ATTAK PII
+ key("\u17E2", joinMoreKeys("2", "\u17F2")),
+ // U+17E3: "៣" KHMER DIGIT THREE
+ // U+17F3: "៳" KHMER SYMBOL LEK ATTAK BEI
+ key("\u17E3", joinMoreKeys("3", "\u17F3")),
+ // U+17E4: "៤" KHMER DIGIT FOUR
+ // U+17F4: "៴" KHMER SYMBOL LEK ATTAK BUON
+ key("\u17E4", joinMoreKeys("4", "\u17F4")),
+ // U+17E5: "៥" KHMER DIGIT FIVE
+ // U+17F5: "៵" KHMER SYMBOL LEK ATTAK PRAM
+ key("\u17E5", joinMoreKeys("5", "\u17F5")),
+ // U+17E6: "៦" KHMER DIGIT SIX
+ // U+17F6: "៶" KHMER SYMBOL LEK ATTAK PRAM-MUOY
+ key("\u17E6", joinMoreKeys("6", "\u17F6")),
+ // U+17E7: "៧" KHMER DIGIT SEVEN
+ // U+17F7: "៷" KHMER SYMBOL LEK ATTAK PRAM-PII
+ key("\u17E7", joinMoreKeys("7", "\u17F7")),
+ // U+17E8: "៨" KHMER DIGIT EIGHT
+ // U+17F8: "៸" KHMER SYMBOL LEK ATTAK PRAM-BEI
+ key("\u17E8", joinMoreKeys("8", "\u17F8")),
+ // U+17E9: "៩" KHMER DIGIT NINE
+ // U+17F9: "៹" KHMER SYMBOL LEK ATTAK PRAM-BUON
+ key("\u17E9", joinMoreKeys("9", "\u17F9")),
+ // U+17E0: "០" KHMER DIGIT ZERO
+ // U+17F0: "៰" KHMER SYMBOL LEK ATTAK SON
+ key("\u17E0", joinMoreKeys("0", "\u17F0")),
+ // U+17A5: "ឥ" KHMER INDEPENDENT VOWEL QI
+ // U+17A6: "ឦ" KHMER INDEPENDENT VOWEL QII
+ key("\u17A5", moreKey("\u17A6")),
+ // U+17B2: "ឲ" KHMER INDEPENDENT VOWEL QOO TYPE TWO
+ // U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE
+ key("\u17B2", moreKey("\u17B1")))
+ .setKeysOfRow(2,
+ // U+1786: "ឆ" KHMER LETTER CHA
+ // U+17B9: "ឹ" KHMER VOWEL SIGN Y
+ // U+17C1: "េ" KHMER VOWEL SIGN E
+ // U+179A: "រ" KHMER LETTER RO
+ // U+178F: "ត" KHMER LETTER TA
+ // U+1799: "យ" KHMER LETTER YO
+ // U+17BB: "ុ" KHMER VOWEL SIGN U
+ // U+17B7: "ិ" KHMER VOWEL SIGN I
+ // U+17C4: "ោ" KHMER VOWEL SIGN OO
+ // U+1795: "ផ" KHMER LETTER PHA
+ // U+17C0: "ៀ" KHMER VOWEL SIGN IE
+ "\u1786", "\u17B9", "\u17C1", "\u179A", "\u178F", "\u1799", "\u17BB", "\u17B7",
+ "\u17C4", "\u1795", "\u17C0",
+ // U+17AA: "ឪ" KHMER INDEPENDENT VOWEL QUUV
+ // U+17A7: "ឧ" KHMER INDEPENDENT VOWEL QU
+ // U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE
+ // U+17B3: "ឳ" KHMER INDEPENDENT VOWEL QAU
+ // U+17A9: "ឩ" KHMER INDEPENDENT VOWEL QUU
+ // U+17A8: "ឨ" KHMER INDEPENDENT VOWEL QUK
+ key("\u17AA", joinMoreKeys("\u17A7", "\u17B1", "\u17B3", "\u17A9", "\u17A8")))
+ .setKeysOfRow(3,
+ // U+17B6: "ា" KHMER VOWEL SIGN AA
+ // U+179F: "ស" KHMER LETTER SA
+ // U+178A: "ដ" KHMER LETTER DA
+ // U+1790: "ថ" KHMER LETTER THA
+ // U+1784: "ង" KHMER LETTER NGO
+ // U+17A0: "ហ" KHMER LETTER HA
+ // U+17D2: "្" KHMER SIGN COENG
+ // U+1780: "ក" KHMER LETTER KA
+ // U+179B: "ល" KHMER LETTER LO
+ // U+17BE: "ើ" KHMER VOWEL SIGN OE
+ // U+17CB: "់" KHMER SIGN BANTOC
+ "\u17B6", "\u179F", "\u178A", "\u1790", "\u1784", "\u17A0", "\u17D2", "\u1780",
+ "\u179B", "\u17BE", "\u17CB",
+ // U+17AE: "ឮ" KHMER INDEPENDENT VOWEL LYY
+ // U+17AD: "ឭ" KHMER INDEPENDENT VOWEL LY
+ // U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI
+ key("\u17AE", joinMoreKeys("\u17AD", "\u17B0")))
+ .setKeysOfRow(4,
+ // U+178B: "ឋ" KHMER LETTER TTHA
+ // U+1781: "ខ" KHMER LETTER KHA
+ // U+1785: "ច" KHMER LETTER CA
+ // U+179C: "វ" KHMER LETTER VO
+ // U+1794: "ប" KHMER LETTER BA
+ // U+1793: "ន" KHMER LETTER NO
+ // U+1798: "ម" KHMER LETTER MO
+ // U+17BB/U+17C6: "ុំ" KHMER VOWEL SIGN U/KHMER SIGN NIKAHIT
+ // U+17D4: "។" KHMER SIGN KHAN
+ // U+17CA: "៊" KHMER SIGN TRIISAP
+ "\u178B", "\u1781", "\u1785", "\u179C", "\u1794", "\u1793", "\u1798",
+ "\u17BB\u17C6", "\u17D4", "\u17CA")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("!", ZWJ_KEY),
+ // U+17D7: "ៗ" KHMER SIGN LEK TOO
+ key("\u17D7", ZWNJ_KEY),
+ // U+17D1: "៑" KHMER SIGN VIRIAM
+ key("\"", moreKey("\u17D1")),
+ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
+ key("\u17DB", joinMoreKeys(Symbols.DOLLAR_SIGN, Symbols.EURO_SIGN)),
+ // U+17D6: "៖" KHMER SIGN CAMNUC PII KUUH
+ key("%", moreKey("\u17D6")),
+ // U+17CD: "៍" KHMER SIGN TOANDAKHIAT
+ // U+17D9: "៙" KHMER SIGN PHNAEK MUAN
+ key("\u17CD", moreKey("\u17D9")),
+ // U+17D0: "័" KHMER SIGN SAMYOK SANNYA
+ // U+17DA: "៚" KHMER SIGN KOOMUUT
+ key("\u17D0", moreKey("\u17DA")),
+ // U+17CF: "៏" KHMER SIGN AHSDA
+ key("\u17CF", moreKey("*")),
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ key("(", joinMoreKeys("{", "\u00AB")),
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ key(")", joinMoreKeys("}", "\u00BB")),
+ // U+17CC: "៌" KHMER SIGN ROBAT
+ // U+00D7: "×" MULTIPLICATION SIGN
+ key("\u17CC", moreKey("\u00D7")),
+ // U+17CE: "៎" KHMER SIGN KAKABAT
+ "\u17CE")
+ .setKeysOfRow(2,
+ // U+1788: "ឈ" KHMER LETTER CHO
+ // U+17DC: "ៜ" KHMER SIGN AVAKRAHASANYA
+ key("\u1788", moreKey("\u17DC")),
+ // U+17BA: "ឺ" KHMER VOWEL SIGN YY
+ // U+17DD: "៝" KHMER SIGN ATTHACAN
+ key("\u17BA", moreKey("\u17DD")),
+ // U+17C2: "ែ" KHMER VOWEL SIGN AE
+ "\u17C2",
+ // U+17AC: "ឬ" KHMER INDEPENDENT VOWEL RYY
+ // U+17AB: "ឫ" KHMER INDEPENDENT VOWEL RY
+ key("\u17AC", moreKey("\u17AB")),
+ // U+1791: "ទ" KHMER LETTER TO
+ // U+17BD: "ួ" KHMER VOWEL SIGN UA
+ // U+17BC: "ូ" KHMER VOWEL SIGN UU
+ // U+17B8: "ី" KHMER VOWEL SIGN II
+ // U+17C5: "ៅ" KHMER VOWEL SIGN AU
+ // U+1797: "ភ" KHMER LETTER PHO
+ // U+17BF: "ឿ" KHMER VOWEL SIGN YA
+ // U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI
+ "\u1791", "\u17BD", "\u17BC", "\u17B8", "\u17C5", "\u1797", "\u17BF", "\u17B0")
+ .setKeysOfRow(3,
+ // U+17B6/U+17C6: "ាំ" KHMER VOWEL SIGN AA/KHMER SIGN NIKAHIT
+ // U+17C3: "ៃ" KHMER VOWEL SIGN AI
+ // U+178C: "ឌ" KHMER LETTER DO
+ // U+1792: "ធ" KHMER LETTER THO
+ // U+17A2: "អ" KHMER LETTER QAE
+ "\u17B6\u17C6", "\u17C3", "\u178C", "\u1792", "\u17A2",
+ // U+17C7: "ះ" KHMER SIGN REAHMUK
+ // U+17C8: "ៈ" KHMER SIGN YUUKALEAPINTU
+ key("\u17C7", moreKey("\u17C8")),
+ // U+1789: "ញ" KHMER LETTER NYO
+ "\u1789",
+ // U+1782: "គ" KHMER LETTER KO
+ // U+179D: "ឝ" KHMER LETTER SHA
+ key("\u1782", moreKey("\u179D")),
+ // U+17A1: "ឡ" KHMER LETTER LA
+ // U+17C4/U+17C7: "ោះ" KHMER VOWEL SIGN OO/KHMER SIGN REAHMUK
+ // U+17C9: "៉" KHMER SIGN MUUSIKATOAN
+ // U+17AF: "ឯ" KHMER INDEPENDENT VOWEL QE
+ "\u17A1", "\u17C4\u17C7", "\u17C9", "\u17AF")
+ .setKeysOfRow(4,
+ // U+178D: "ឍ" KHMER LETTER TTHO
+ // U+1783: "ឃ" KHMER LETTER KHO
+ // U+1787: "ជ" KHMER LETTER CO
+ // U+17C1/U+17C7: "េះ" KHMER VOWEL SIGN E/KHMER SIGN REAHMUK
+ "\u178D", "\u1783", "\u1787", "\u17C1\u17C7",
+ // U+1796: "ព" KHMER LETTER PO
+ // U+179E: "ឞ" KHMER LETTER SSO
+ key("\u1796", moreKey("\u179E")),
+ // U+178E: "ណ" KHMER LETTER NNO
+ // U+17C6: "ំ" KHMER SIGN NIKAHIT
+ // U+17BB/U+17C7: "ុះ" KHMER VOWEL SIGN U/KHMER SIGN REAHMUK
+ // U+17D5: "៕" KHMER SIGN BARIYOOSAN
+ "\u178E", "\u17C6", "\u17BB\u17C7", "\u17D5", "?")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java
new file mode 100644
index 000000000..e7be9982a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Khmer alphabet keyboard.
+ */
+public final class Lao extends LayoutBase {
+ private static final String LAYOUT_NAME = "lao";
+
+ public Lao(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class LaoCustomizer extends LayoutCustomizer {
+ public LaoCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return LAO_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_KIP; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
+
+ // U+0E81: "ກ" LAO LETTER KO
+ // U+0E82: "ຂ" LAO LETTER KHO SUNG
+ // U+0E84: "ຄ" LAO LETTER KHO TAM
+ private static final ExpectedKey LAO_ALPHABET_KEY = key(
+ "\u0E81\u0E82\u0E84", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+20AD: "₭" KIP SIGN
+ private static final ExpectedKey CURRENCY_KIP = key("\u20AD",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ if (isPhone) {
+ return ALPHABET_COMMON;
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ builder.addKeysOnTheRightOfRow(4, (Object[])EXCLAMATION_AND_QUESTION_MARKS);
+ return builder.build();
+ }
+
+ @Override
+ public ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone,
+ final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(4, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0EA2: "ຢ" LAO LETTER YO
+ // U+0ED1: "໑" LAO DIGIT ONE
+ key("\u0EA2", joinMoreKeys("1", "\u0ED1")),
+ // U+0E9F: "ຟ" LAO LETTER FO SUNG
+ // U+0ED2: "໒" LAO DIGIT TWO
+ key("\u0E9F", joinMoreKeys("2", "\u0ED2")),
+ // U+0EC2: "ໂ" LAO VOWEL SIGN O
+ // U+0ED3: "໓" LAO DIGIT THREE
+ key("\u0EC2", joinMoreKeys("3", "\u0ED3")),
+ // U+0E96: "ຖ" LAO LETTER THO SUNG
+ // U+0ED4: "໔" LAO DIGIT FOUR
+ key("\u0E96", joinMoreKeys("4", "\u0ED4")),
+ // U+0EB8: "ຸ" LAO VOWEL SIGN U
+ // U+0EB9: "ູ" LAO VOWEL SIGN UU
+ "\u0EB8", "\u0EB9",
+ // U+0E84: "ຄ" LAO LETTER KHO TAM
+ // U+0ED5: "໕" LAO DIGIT FIVE
+ key("\u0E84", joinMoreKeys("5", "\u0ED5")),
+ // U+0E95: "ຕ" LAO LETTER TO
+ // U+0ED6: "໖" LAO DIGIT SIX
+ key("\u0E95", joinMoreKeys("6", "\u0ED6")),
+ // U+0E88: "ຈ" LAO LETTER CO
+ // U+0ED7: "໗" LAO DIGIT SEVEN
+ key("\u0E88", joinMoreKeys("7", "\u0ED7")),
+ // U+0E82: "ຂ" LAO LETTER KHO SUNG
+ // U+0ED8: "໘" LAO DIGIT EIGHT
+ key("\u0E82", joinMoreKeys("8", "\u0ED8")),
+ // U+0E8A: "ຊ" LAO LETTER SO TAM
+ // U+0ED9: "໙" LAO DIGIT NINE
+ key("\u0E8A", joinMoreKeys("9", "\u0ED9")),
+ // U+0ECD: "ໍ" LAO NIGGAHITA
+ "\u0ECD")
+ .setKeysOfRow(2,
+ // U+0EBB: "ົ" LAO VOWEL SIGN MAI KON
+ "\u0EBB",
+ // U+0EC4: "ໄ" LAO VOWEL SIGN AI
+ // U+0ED0: "໐" LAO DIGIT ZERO
+ key("\u0EC4", joinMoreKeys("0", "\u0ED0")),
+ // U+0EB3: "ຳ" LAO VOWEL SIGN AM
+ // U+0E9E: "ພ" LAO LETTER PHO TAM
+ // U+0EB0: "ະ" LAO VOWEL SIGN A
+ // U+0EB4: "ິ" LAO VOWEL SIGN I
+ // U+0EB5: "ີ" LAO VOWEL SIGN II
+ // U+0EAE: "ຮ" LAO LETTER HO TAM
+ // U+0E99: "ນ" LAO LETTER NO
+ // U+0E8D: "ຍ" LAO LETTER NYO
+ // U+0E9A: "ບ" LAO LETTER BO
+ // U+0EA5: "ລ" LAO LETTER LO LOOT
+ "\u0EB3", "\u0E9E", "\u0EB0", "\u0EB4", "\u0EB5", "\u0EAE", "\u0E99", "\u0E8D",
+ "\u0E9A", "\u0EA5")
+ .setKeysOfRow(3,
+ // U+0EB1: "ັ" LAO VOWEL SIGN MAI KAN
+ // U+0EAB: "ຫ" LAO LETTER HO SUNG
+ // U+0E81: "ກ" LAO LETTER KO
+ // U+0E94: "ດ" LAO LETTER DO
+ // U+0EC0: "ເ" LAO VOWEL SIGN E
+ // U+0EC9: "້" LAO TONE MAI THO
+ // U+0EC8: "່" LAO TONE MAI EK
+ // U+0EB2: "າ" LAO VOWEL SIGN AA
+ // U+0EAA: "ສ" LAO LETTER SO SUNG
+ // U+0EA7: "ວ" LAO LETTER WO
+ // U+0E87: "ງ" LAO LETTER NGO
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ "\u0EB1", "\u0EAB", "\u0E81", "\u0E94", "\u0EC0", "\u0EC9", "\u0EC8", "\u0EB2",
+ "\u0EAA", "\u0EA7", "\u0E87", "\u201C")
+ .setKeysOfRow(4,
+ // U+0E9C: "ຜ" LAO LETTER PHO SUNG
+ // U+0E9B: "ປ" LAO LETTER PO
+ // U+0EC1: "ແ" LAO VOWEL SIGN EI
+ // U+0EAD: "ອ" LAO LETTER O
+ // U+0EB6: "ຶ" LAO VOWEL SIGN Y
+ // U+0EB7: "ື" LAO VOWEL SIGN YY
+ // U+0E97: "ທ" LAO LETTER THO TAM
+ // U+0EA1: "ມ" LAO LETTER MO
+ // U+0EC3: "ໃ" LAO VOWEL SIGN AY
+ // U+0E9D: "ຝ" LAO LETTER FO TAM
+ "\u0E9C", "\u0E9B", "\u0EC1", "\u0EAD", "\u0EB6", "\u0EB7", "\u0E97", "\u0EA1",
+ "\u0EC3", "\u0E9D")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0ED1: "໑" LAO DIGIT ONE
+ // U+0ED2: "໒" LAO DIGIT TWO
+ // U+0ED3: "໓" LAO DIGIT THREE
+ // U+0ED4: "໔" LAO DIGIT FOUR
+ // U+0ECC: "໌" LAO CANCELLATION MARK
+ // U+0EBC: "ຼ" LAO SEMIVOWEL SIGN LO
+ // U+0ED5: "໕" LAO DIGIT FIVE
+ // U+0ED6: "໖" LAO DIGIT SIX
+ // U+0ED7: "໗" LAO DIGIT SEVEN
+ // U+0ED8: "໘" LAO DIGIT EIGHT
+ // U+0ED9: "໙" LAO DIGIT NINE
+ // U+0ECD/U+0EC8: "ໍ່" LAO NIGGAHITA/LAO TONE MAI EK
+ "\u0ED1", "\u0ED2", "\u0ED3", "\u0ED4", "\u0ECC", "\u0EBC", "\u0ED5", "\u0ED6",
+ "\u0ED7", "\u0ED8", "\u0ED9", "\u0ECD\u0EC8")
+ .setKeysOfRow(2,
+ // U+0EBB/U+0EC9: "" LAO VOWEL SIGN MAI KON/LAO TONE MAI THO
+ // U+0ED0: "໐" LAO DIGIT ZERO
+ // U+0EB3/U+0EC9: "ຳ້" LAO VOWEL SIGN AM/LAO TONE MAI THO
+ // U+0EB4/U+0EC9: "ິ້" LAO VOWEL SIGN I/LAO TONE MAI THO
+ // U+0EB5/U+0EC9: "ີ້" LAO VOWEL SIGN II/LAO TONE MAI THO
+ // U+0EA3: "ຣ" LAO LETTER LO LING
+ // U+0EDC: "ໜ" LAO HO NO
+ // U+0EBD: "ຽ" LAO SEMIVOWEL SIGN NYO
+ // U+0EAB/U+0EBC: "" LAO LETTER HO SUNG/LAO SEMIVOWEL SIGN LO
+ // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
+ "\u0EBB\u0EC9", "\u0ED0", "\u0EB3\u0EC9", "_", "+", "\u0EB4\u0EC9",
+ "\u0EB5\u0EC9", "\u0EA3", "\u0EDC", "\u0EBD", "\u0EAB\u0EBC", "\u201D")
+ .setKeysOfRow(3,
+ // U+0EB1/U+0EC9: "ັ້" LAO VOWEL SIGN MAI KAN/LAO TONE MAI THO
+ // U+0ECA: "໊" LAO TONE MAI TI
+ // U+0ECB: "໋" LAO TONE MAI CATAWA
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ "\u0EB1\u0EC9", ";", ".", ",", ":", "\u0ECA", "\u0ECB", "!", "?", "%", "=",
+ "\u201C")
+ .setKeysOfRow(4,
+ // U+20AD: "₭" KIP SIGN
+ // U+0EAF: "ຯ" LAO ELLIPSIS
+ // U+0EB6/U+0EC9: "ຶ້" LAO VOWEL SIGN Y/LAO TONE MAI THO
+ // U+0EB7/U+0EC9: "ື້" LAO VOWEL SIGN YY/LAO TONE MAI THO
+ // U+0EC6: "ໆ" LAO KO LA
+ // U+0EDD: "ໝ" LAO HO MO
+ "\u20AD", "(", "\u0EAF", "@", "\u0EB6\u0EC9", "\u0EB7\u0EC9", "\u0EC6",
+ "\u0EDD", "$", ")")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java
new file mode 100644
index 000000000..c5223720c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The base class of keyboard layout.
+ */
+public abstract class LayoutBase extends AbstractLayoutBase {
+ /**
+ * This class is used to customize common keyboard layout to language specific layout.
+ */
+ public static class LayoutCustomizer {
+ private final Locale mLocale;
+
+ // Empty keys definition to remove keys by adding this.
+ protected static final ExpectedKey[] EMPTY_KEYS = joinKeys();
+
+ public LayoutCustomizer(final Locale locale) {
+ mLocale = locale;
+ }
+
+ public final Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Set accented letters to common layout.
+ * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
+ * layout.
+ * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
+ * "more keys".
+ */
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder;
+ }
+
+ /**
+ * Get the function key to switch to alphabet layout.
+ * @return the {@link ExpectedKey} of the alphabet key.
+ */
+ public ExpectedKey getAlphabetKey() { return ALPHABET_KEY; }
+
+ /**
+ * Get the function key to switch to symbols layout.
+ * @return the {@link ExpectedKey} of the symbols key.
+ */
+ public ExpectedKey getSymbolsKey() { return SYMBOLS_KEY; }
+
+ /**
+ * Get the function key to switch to symbols shift layout.
+ * @param isPhone true if requesting phone's key.
+ * @return the {@link ExpectedKey} of the symbols shift key.
+ */
+ public ExpectedKey getSymbolsShiftKey(boolean isPhone) {
+ return isPhone ? SYMBOLS_SHIFT_KEY : TABLET_SYMBOLS_SHIFT_KEY;
+ }
+
+ /**
+ * Get the function key to switch from symbols shift to symbols layout.
+ * @return the {@link ExpectedKey} of the back to symbols key.
+ */
+ public ExpectedKey getBackToSymbolsKey() { return BACK_TO_SYMBOLS_KEY; }
+
+ /**
+ * Get the currency key.
+ * @return the {@link ExpectedKey} of the currency key.
+ */
+ public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_DOLLAR; }
+
+ /**
+ * Get other currencies keys.
+ * @return the array of {@link ExpectedKey} that represents other currency keys.
+ */
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_THAN_DOLLAR;
+ }
+
+ /**
+ * Get "more keys" of double quotation mark.
+ * @return the array of {@link ExpectedKey} of more double quotation marks in natural order.
+ */
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_9LR; }
+
+ /**
+ * Get "more keys" of single quotation mark.
+ * @return the array of {@link ExpectedKey} of more single quotation marks in natural order.
+ */
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_9LR; }
+
+ /**
+ * Get double angle quotation marks in natural order.
+ * @return the array of {@link ExpectedKey} of double angle quotation marks in natural
+ * order.
+ */
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_LR; }
+
+ /**
+ * Get single angle quotation marks in natural order.
+ * @return the array of {@link ExpectedKey} of single angle quotation marks in natural
+ * order.
+ */
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_LR; }
+
+ /**
+ * Get the left shift keys.
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that should be placed at left edge of the
+ * keyboard.
+ */
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return joinKeys(SHIFT_KEY);
+ }
+
+ /**
+ * Get the right shift keys.
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that should be placed at right edge of the
+ * keyboard.
+ */
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : joinKeys(EXCLAMATION_AND_QUESTION_MARKS, SHIFT_KEY);
+ }
+
+ /**
+ * Get the space keys.
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that should be placed at the center of the
+ * keyboard.
+ */
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY);
+ }
+
+ /**
+ * Get the keys left to the spacebar.
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that should be placed at left of the spacebar.
+ */
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ // U+002C: "," COMMA
+ return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY))
+ : joinKeys(key("\u002C", SETTINGS_KEY), "_");
+ }
+
+ /**
+ * Get the keys right to the spacebar.
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that should be placed at right of the spacebar.
+ */
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone));
+ return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey);
+ }
+
+ /**
+ * Get "more keys" for the punctuation key (usually the period key).
+ * @param isPhone true if requesting phone's keys.
+ * @return the array of {@link ExpectedKey} that are "more keys" of the punctuation key.
+ */
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
+ }
+ }
+
+ /**
+ * The layout customize class for countries that use Euro.
+ */
+ public static class EuroCustomizer extends LayoutCustomizer {
+ public EuroCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public final ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
+
+ @Override
+ public final ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
+ }
+ }
+
+ private final LayoutCustomizer mCustomizer;
+ private final Symbols mSymbols;
+ private final SymbolsShifted mSymbolsShifted;
+
+ LayoutBase(final LayoutCustomizer customizer, final Class<? extends Symbols> symbolsClass,
+ final Class<? extends SymbolsShifted> symbolsShiftedClass) {
+ mCustomizer = customizer;
+ try {
+ mSymbols = symbolsClass.getDeclaredConstructor(LayoutCustomizer.class)
+ .newInstance(customizer);
+ mSymbolsShifted = symbolsShiftedClass.getDeclaredConstructor(LayoutCustomizer.class)
+ .newInstance(customizer);
+ } catch (final Exception e) {
+ throw new RuntimeException("Unknown Symbols/SymbolsShifted class", e);
+ }
+ }
+
+ /**
+ * The layout name.
+ * @return the name of this layout.
+ */
+ public abstract String getName();
+
+ /**
+ * The locale of this layout.
+ * @return the locale of this layout.
+ */
+ public final Locale getLocale() { return mCustomizer.getLocale(); }
+
+ /**
+ * The layout customizer for this layout.
+ * @return the layout customizer;
+ */
+ public final LayoutCustomizer getCustomizer() { return mCustomizer; }
+
+ // Icon id.
+ private static final int ICON_SHIFT = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_SHIFT_KEY);
+ private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED);
+ private static final int ICON_ZWNJ = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_ZWNJ_KEY);
+ private static final int ICON_ZWJ = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_ZWJ_KEY);
+
+ // Functional key.
+ static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK);
+ static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT,
+ Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
+ static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT,
+ Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
+ static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT);
+ static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT);
+ static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT);
+
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ static final ExpectedKey[] EXCLAMATION_AND_QUESTION_MARKS = joinKeys(
+ key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF")));
+ // U+200C: ZERO WIDTH NON-JOINER
+ // U+200D: ZERO WIDTH JOINER
+ static final ExpectedKey ZWNJ_KEY = key(ICON_ZWNJ, "\u200C");
+ static final ExpectedKey ZWJ_KEY = key(ICON_ZWJ, "\u200D");
+
+ // Punctuation more keys for phone form factor.
+ public static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "?", "!", "#", ")", "(", "/", ";",
+ "'", "@", ":", "-", "\"", "+", "%", "&");
+ // Punctuation more keys for tablet form factor.
+ public static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "'", "#", ")", "(", "/", ";",
+ "@", ":", "-", "\"", "+", "%", "&");
+
+ /**
+ * Helper method to create alphabet layout adding special function keys.
+ * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
+ * layout
+ * @param isPhone true if requesting phone's layout.
+ * @return the {@link ExpectedKeyboardBuilder} object that is customized and have special keys.
+ */
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(4, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(4, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(3, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(3, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ /**
+ * Get common alphabet layout. This layout doesn't contain any special keys.
+ *
+ * A keyboard layout is an array of rows, and a row consists of an array of
+ * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
+ *
+ * @param isPhone true if requesting phone's layout.
+ * @return the common alphabet keyboard layout.
+ */
+ abstract ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone);
+
+ /**
+ * Get common alphabet shifted layout. This layout doesn't contain any special keys.
+ *
+ * A keyboard layout is an array of rows, and a row consists of an array of
+ * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
+ *
+ * @param isPhone true if requesting phone's layout.
+ * @param elementId the element id of the requesting shifted mode.
+ * @return the common alphabet shifted keyboard layout.
+ */
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ getCommonAlphabetLayout(isPhone));
+ getCustomizer().setAccentedLetters(builder);
+ builder.toUpperCase(getLocale());
+ return builder.build();
+ }
+
+ /**
+ * Get the complete expected keyboard layout.
+ *
+ * A keyboard layout is an array of rows, and a row consists of an array of
+ * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s.
+ *
+ * @param isPhone true if requesting phone's layout.
+ * @param elementId the element id of the requesting keyboard mode.
+ * @return the keyboard layout of the <code>elementId</code>.
+ */
+ public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_SYMBOLS) {
+ return mSymbols.getLayout(isPhone);
+ }
+ if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
+ return mSymbolsShifted.getLayout(isPhone);
+ }
+ final ExpectedKeyboardBuilder builder;
+ if (elementId == KeyboardId.ELEMENT_ALPHABET) {
+ builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+ getCustomizer().setAccentedLetters(builder);
+ } else {
+ final ExpectedKey[][] commonLayout = getCommonAlphabetShiftLayout(isPhone, elementId);
+ if (commonLayout == null) {
+ return null;
+ }
+ builder = new ExpectedKeyboardBuilder(commonLayout);
+ }
+ convertCommonLayoutToKeyboard(builder, isPhone);
+ if (elementId != KeyboardId.ELEMENT_ALPHABET) {
+ builder.replaceKeysOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY);
+ }
+ return builder.build();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java
new file mode 100644
index 000000000..b44b888e1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Malayalam keyboard.
+ */
+public final class Malayalam extends LayoutBase {
+ private static final String LAYOUT_NAME = "malayalam";
+
+ public Malayalam(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class MalayalamCustomizer extends LayoutCustomizer {
+ public MalayalamCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return MALAYALAM_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0D05: "അ" MALAYALAM LETTER A
+ private static final ExpectedKey MALAYALAM_ALPHABET_KEY = key(
+ "\u0D05", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0D4D: "്" MALAYALAM SIGN VIRAMA
+ // U+0D05: "അ" MALAYALAM LETTER A
+ key("\u0D4D", joinMoreKeys("\u0D05", "1")),
+ // U+0D3E: "ാ" MALAYALAM VOWEL SIGN AA
+ // U+0D06: "ആ" MALAYALAM LETTER AA
+ key("\u0D3E", joinMoreKeys("\u0D06", "2")),
+ // U+0D3F: "ി" MALAYALAM VOWEL SIGN I
+ // U+0D07: "ഇ" MALAYALAM LETTER I
+ key("\u0D3F", joinMoreKeys("\u0D07", "3")),
+ // U+0D40: "ീ" MALAYALAM VOWEL SIGN II
+ // U+0D08: "ഈ" MALAYALAM LETTER II
+ key("\u0D40", joinMoreKeys("\u0D08", "4")),
+ // U+0D41: "ു" MALAYALAM VOWEL SIGN U
+ // U+0D09: "ഉ" MALAYALAM LETTER U
+ key("\u0D41", joinMoreKeys("\u0D09", "5")),
+ // U+0D42: "ൂ" MALAYALAM VOWEL SIGN UU
+ // U+0D0A: "ഊ" MALAYALAM LETTER UU
+ key("\u0D42", joinMoreKeys("\u0D0A", "6")),
+ // U+0D43: "ൃ" MALAYALAM VOWEL SIGN VOCALIC R
+ // U+0D0B: "ഋ" MALAYALAM LETTER VOCALIC R
+ key("\u0D43", joinMoreKeys("\u0D0B", "7")),
+ // U+0D46: "െ" MALAYALAM VOWEL SIGN E
+ // U+0D0E: "എ" MALAYALAM LETTER E
+ // U+0D10: "ഐ" MALAYALAM LETTER AI
+ // U+0D48: "ൈ" MALAYALAM VOWEL SIGN AI
+ key("\u0D46", joinMoreKeys("\u0D0E", "\u0D10", "\u0D48", "8")),
+ // U+0D47: "േ" MALAYALAM VOWEL SIGN EE
+ // U+0D0F: "ഏ" MALAYALAM LETTER EE
+ key("\u0D47", joinMoreKeys("\u0D0F", "9")),
+ // U+0D4A: "ൊ" MALAYALAM VOWEL SIGN O
+ // U+0D12: "ഒ" MALAYALAM LETTER O
+ key("\u0D4A", joinMoreKeys("\u0D12", "0")),
+ // U+0D4B: "ോ" MALAYALAM VOWEL SIGN OO
+ // U+0D13: "ഓ" MALAYALAM LETTER OO
+ // U+0D14: "ഔ" MALAYALAM LETTER AU
+ // U+0D57: "ൗ" MALAYALAM AU LENGTH MARK
+ key("\u0D4B", joinMoreKeys("\u0D13", "\u0D14", "\u0D57")))
+ .setKeysOfRow(2,
+ // U+0D15: "ക" MALAYALAM LETTER KA
+ // U+0D16: "ഖ" MALAYALAM LETTER KHA
+ key("\u0D15", moreKey("\u0D16")),
+ // U+0D17: "ഗ" MALAYALAM LETTER GA
+ // U+0D18: "ഘ" MALAYALAM LETTER GHA
+ key("\u0D17", moreKey("\u0D18")),
+ // U+0D19: "ങ" MALAYALAM LETTER NGA
+ // U+0D1E: "ഞ" MALAYALAM LETTER NYA
+ key("\u0D19", moreKey("\u0D1E")),
+ // U+0D1A: "ച" MALAYALAM LETTER CA
+ // U+0D1B: "ഛ" MALAYALAM LETTER CHA
+ key("\u0D1A", moreKey("\u0D1B")),
+ // U+0D1C: "ജ" MALAYALAM LETTER JA
+ // U+0D1D: "ഝ" MALAYALAM LETTER JHA
+ key("\u0D1C", moreKey("\u0D1D")),
+ // U+0D1F: "ട" MALAYALAM LETTER TTA
+ // U+0D20: "ഠ" MALAYALAM LETTER TTHA
+ key("\u0D1F", moreKey("\u0D20")),
+ // U+0D21: "ഡ" MALAYALAM LETTER DDA
+ // U+0D22: "ഢ" MALAYALAM LETTER DDHA
+ key("\u0D21", moreKey("\u0D22")),
+ // U+0D23: "ണ" MALAYALAM LETTER NNA
+ // U+0D7A: "ൺ" MALAYALAM LETTER CHILLU NN
+ key("\u0D23", moreKey("\u0D7A")),
+ // U+0D24: "ത" MALAYALAM LETTER TA
+ // U+0D25: "ഥ" MALAYALAM LETTER THA
+ key("\u0D24", moreKey("\u0D25")),
+ // U+0D26: "ദ" MALAYALAM LETTER DA
+ // U+0D27: "ധ" MALAYALAM LETTER DHA
+ key("\u0D26", moreKey("\u0D27")),
+ // U+0D28: "ഗന" MALAYALAM LETTER NA
+ // U+0D7B: "ൻ" MALAYALAM LETTER CHILLU N
+ key("\u0D28", moreKey("\u0D7B")))
+ .setKeysOfRow(3,
+ // U+0D2A: "പ" MALAYALAM LETTER PA
+ // U+0D2B: "ഫ" MALAYALAM LETTER PHA
+ key("\u0D2A", moreKey("\u0D2B")),
+ // U+0D2C: "ബ" MALAYALAM LETTER BA
+ // U+0D2D: "ഭ" MALAYALAM LETTER BHA
+ key("\u0D2C", moreKey("\u0D2D")),
+ // U+0D2E: "മ" MALAYALAM LETTER MA
+ // U+0D02: "ം" MALAYALAM SIGN ANUSVARA
+ key("\u0D2E", moreKey("\u0D02")),
+ // U+0D2F: "യ" MALAYALAM LETTER YA
+ // U+0D4D/U+0D2F: "്യ" MALAYALAM SIGN VIRAMA/MALAYALAM LETTER YA
+ key("\u0D2F", moreKey("\u0D4D\u0D2F")),
+ // U+0D30: "ര" MALAYALAM LETTER RA
+ // U+0D4D/U+0D30: "്ര" MALAYALAM SIGN VIRAMA/MALAYALAM LETTER RA
+ // U+0D7C: "ർ" MALAYALAM LETTER CHILLU RR
+ // U+0D31: "റ" MALAYALAM LETTER RRA
+ key("\u0D30", joinMoreKeys("\u0D4D\u0D30", "\u0D7C", "\u0D31")),
+ // U+0D32: "ല" MALAYALAM LETTER LA
+ // U+0D7D: "ൽ" MALAYALAM LETTER CHILLU L
+ key("\u0D32", moreKey("\u0D7D")),
+ // U+0D35: "വ" MALAYALAM LETTER VA
+ // U+0D4D/U+0D35: "്വ" MALAYALAM SIGN VIRAMA/MALAYALAM LETTER VA
+ key("\u0D35", moreKey("\u0D4D\u0D35")),
+ // U+0D36: "ശ" MALAYALAM LETTER SHA
+ // U+0D37: "ഷ" MALAYALAM LETTER SSA
+ // U+0D38: "സ" MALAYALAM LETTER SA
+ key("\u0D36", joinMoreKeys("\u0D37", "\u0D38")),
+ // U+0D39: "ഹ" MALAYALAM LETTER HA
+ // U+0D03: "ഃ" MALAYALAM SIGN VISARGA
+ key("\u0D39", moreKey("\u0D03")),
+ // U+0D33: "ള" MALAYALAM LETTER LLA
+ // U+0D7E: "ൾ" MALAYALAM LETTER CHILLU LL
+ // U+0D34: "ഴ" MALAYALAM LETTER LLLA
+ key("\u0D33", joinMoreKeys("\u0D7E", "\u0D34")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java
new file mode 100644
index 000000000..00cf838f9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
+
+import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The Marathi keyboard.
+ */
+public final class Marathi extends LayoutBase {
+ private static final String LAYOUT_NAME = "marathi";
+
+ public Marathi(final LayoutCustomizer customizer) {
+ super(customizer, HindiSymbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class MarathiCustomizer extends HindiCustomizer {
+ public MarathiCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ // U+0914: "औ" DEVANAGARI LETTER AU
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ key(VOWEL_SIGN_AU, "\u094C", joinMoreKeys("\u0914", "\u0967", "1")),
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ // U+0910: "ऐ" DEVANAGARI LETTER AI
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ key(VOWEL_SIGN_AI, "\u0948", joinMoreKeys("\u0910", "\u0968", "2")),
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ // U+0906: "आ" DEVANAGARI LETTER AA
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ key(VOWEL_SIGN_AA, "\u093E", joinMoreKeys("\u0906", "\u0969", "3")),
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ // U+0908: "ई" DEVANAGARI LETTER II
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ key(VOWEL_SIGN_II, "\u0940", joinMoreKeys("\u0908", "\u096A", "4")),
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ // U+090A: "ऊ" DEVANAGARI LETTER UU
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ key(VOWEL_SIGN_UU, "\u0942", joinMoreKeys("\u090A", "\u096B", "5")),
+ // U+092C: "ब" DEVANAGARI LETTER BA
+ // U+092D: "भ" DEVANAGARI LETTER BHA
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ key("\u092C", joinMoreKeys("\u092D", "\u096C", "6")),
+ // U+0939: "ह" DEVANAGARI LETTER HA
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ key("\u0939", joinMoreKeys("\u096D", "7")),
+ // U+0917: "ग" DEVANAGARI LETTER GA
+ // U+0918: "घ" DEVANAGARI LETTER GHA
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ key("\u0917", joinMoreKeys("\u0918", "\u096E", "8")),
+ // U+0926: "द" DEVANAGARI LETTER DA
+ // U+0927: "ध" DEVANAGARI LETTER DHA
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ key("\u0926", joinMoreKeys("\u0927", "\u096F", "9")),
+ // U+091C: "ज" DEVANAGARI LETTER JA
+ // U+091D: "झ" DEVANAGARI LETTER JHA
+ // U+091C/U+094D/U+091E:
+ // "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ key("\u091C", joinMoreKeys("\u091D", "\u091C\u094D\u091E", "\u0966", "0")),
+ // U+0921: "ड" DEVANAGARI LETTER DDA
+ // U+0922: "ढ" DEVANAGARI LETTER DDHA
+ key("\u0921", moreKey("\u0922")))
+ .setKeysOfRow(2,
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ // U+0913: "ओ" DEVANAGARI LETTER O
+ key(VOWEL_SIGN_O, "\u094B", moreKey("\u0913")),
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ // U+090F: "ए" DEVANAGARI LETTER SHORT E
+ key(VOWEL_SIGN_E, "\u0947", moreKey("\u090F")),
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ // U+0905: "अ" DEVANAGARI LETTER A
+ key(SIGN_VIRAMA, "\u094D", moreKey("\u0905")),
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ // U+0907: "इ" DEVANAGARI LETTER I
+ key(VOWEL_SIGN_I, "\u093F", moreKey("\u0907")),
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ // U+0909: "उ" DEVANAGARI LETTER U
+ key(VOWEL_SIGN_U, "\u0941", moreKey("\u0909")),
+ // U+092A: "प" DEVANAGARI LETTER PA
+ // U+092B: "फ" DEVANAGARI LETTER PHA
+ key("\u092A", moreKey("\u092B")),
+ // U+0930: "र" DEVANAGARI LETTER RA
+ // U+0931: "ऱ" DEVANAGARI LETTER RRA
+ // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ key("\u0930", joinMoreKeys(
+ "\u0931", "\u090B", moreKey(VOWEL_SIGN_VOCALIC_R, "\u0943"))),
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ key("\u0915", moreKey("\u0916")),
+ // U+0924: "त" DEVANAGARI LETTER TA
+ // U+0925: "थ" DEVANAGARI LETTER THA
+ // U+0924/U+094D/U+0930:
+ // "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0924", joinMoreKeys("\u0925", "\u0924\u094D\u0930")),
+ // U+091A: "च" DEVANAGARI LETTER CA
+ // U+091B: "छ" DEVANAGARI LETTER CHA
+ key("\u091A", moreKey("\u091B")),
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ // U+0920: "ठ" DEVANAGARI LETTER TTHA
+ key("\u091F", moreKey("\u0920")))
+ .setKeysOfRow(3,
+ // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+ // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
+ key(VOWEL_SIGN_CANDRA_O, "\u0949", moreKey("\u0911")),
+ // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+ // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+ key(VOWEL_SIGN_CANDRA_E, "\u0945", moreKey("\u090D")),
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ key(SIGN_ANUSVARA, "\u0902", joinMoreKeys(
+ moreKey(SIGN_VISARGA, "\u0903"), moreKey(SIGN_CANDRABINDU, "\u0901"))),
+ // U+092E: "म" DEVANAGARI LETTER MA
+ "\u092E",
+ // U+0928: "न" DEVANAGARI LETTER NA
+ // U+0923: "ण" DEVANAGARI LETTER NNA
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ key("\u0928", joinMoreKeys("\u0923", "\u091E", "\u0919")),
+ // U+0935: "व" DEVANAGARI LETTER VA
+ "\u0935",
+ // U+0932: "ल" DEVANAGARI LETTER LA
+ // U+0933: "ळ" DEVANAGARI LETTER LLA
+ key("\u0932", moreKey("\u0933")),
+ // U+0938: "स" DEVANAGARI LETTER SA
+ // U+0936: "श" DEVANAGARI LETTER SHA
+ // U+0937: "ष" DEVANAGARI LETTER SSA
+ // U+0936/U+094D/U+0930:
+ // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key("\u0938", joinMoreKeys("\u0936", "\u0937", "\u0936\u094D\u0930")),
+ // U+092F: "य" DEVANAGARI LETTER YA
+ "\u092F",
+ // U+0915/U+094D/U+0937:
+ // "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+ "\u0915\u094D\u0937")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java b/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java
new file mode 100644
index 000000000..3c6c05841
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+public final class Mongolian extends LayoutBase {
+ private static final String LAYOUT_NAME = "mongolian";
+
+ public Mongolian(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class MongolianMNCustomizer extends EastSlavicCustomizer {
+ public MongolianMNCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_TUGRIK; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ // U+20AE: "₮" TUGRIK SIGN
+ private static final ExpectedKey CURRENCY_TUGRIK = key("\u20AE",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0444: "ф" CYRILLIC SMALL LETTER EF
+ key("\u0444", moreKey("1")),
+ // U+0446: "ц" CYRILLIC SMALL LETTER TSE
+ key("\u0446", moreKey("2")),
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ key("\u0443", moreKey("3")),
+ // U+0436: "ж" CYRILLIC SMALL LETTER ZHE
+ key("\u0436", moreKey("4")),
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ key("\u044D", moreKey("5")),
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ key("\u043D", moreKey("6")),
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ key("\u0433", moreKey("7")),
+ // U+0448: "ш" CYRILLIC SMALL LETTER SHA
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ key("\u0448", joinMoreKeys("8", "\u0449")),
+ // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
+ key("\u04AF", moreKey("9")),
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ key("\u0437", moreKey("0")),
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ "\u043A")
+ .setKeysOfRow(2,
+ // U+0439: "й" CYRILLIC SMALL LETTER SHORT I
+ // U+044B: "ы" CYRILLIC SMALL LETTER YERU
+ // U+0431: "б" CYRILLIC SMALL LETTER BE
+ // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+0445: "х" CYRILLIC SMALL LETTER HA
+ // U+0440: "р" CYRILLIC SMALL LETTER ER
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ // U+043B: "л" CYRILLIC SMALL LETTER EL
+ // U+0434: "д" CYRILLIC SMALL LETTER DE
+ // U+043F: "п" CYRILLIC SMALL LETTER PE
+ "\u0439", "\u044B", "\u0431", "\u04E9", "\u0430", "\u0445", "\u0440", "\u043E",
+ "\u043B", "\u0434", "\u043F")
+ .setKeysOfRow(3,
+ // U+044F: "я" CYRILLIC SMALL LETTER YA
+ // U+0447: "ч" CYRILLIC SMALL LETTER CHE
+ "\u044F", "\u0447",
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ key("\u0451", moreKey("\u0435")),
+ // U+0441: "с" CYRILLIC SMALL LETTER ES
+ // U+043C: "м" CYRILLIC SMALL LETTER EM
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ // U+0442: "т" CYRILLIC SMALL LETTER TE
+ "\u0441", "\u043C", "\u0438", "\u0442",
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ key("\u044C", moreKey("\u044A")),
+ // U+0432: "в" CYRILLIC SMALL LETTER VE
+ // U+044E: "ю" CYRILLIC SMALL LETTER YU
+ key("\u0432", moreKey("\u044E")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java
new file mode 100644
index 000000000..f2a2dfdd8
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Myanmar alphabet keyboard.
+ */
+public final class Myanmar extends LayoutBase {
+ private static final String LAYOUT_NAME = "myanmar";
+
+ public Myanmar(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class MyanmarCustomizer extends LayoutCustomizer {
+ public MyanmarCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return MYANMAR_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ // U+002C: "," COMMA
+ // U+104A: "၊" MYANMAR SIGN LITTLE SECTION
+ return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY))
+ : joinKeys(key("\u104A", moreKey(","), SETTINGS_KEY), "_");
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ // U+104B: "။" MYANMAR SIGN SECTION
+ final ExpectedKey periodKey = key("\u104B", getPunctuationMoreKeys(isPhone));
+ return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey);
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? MYANMAR_PHONE_PUNCTUATION_MORE_KEYS
+ : MYANMAR_TABLET_PUNCTUATION_MORE_KEYS;
+ }
+
+ // U+1000: "က" MYANMAR LETTER KA
+ // U+1001: "ခ" MYANMAR LETTER KHA
+ // U+1002: "ဂ" MYANMAR LETTER GA
+ private static final ExpectedKey MYANMAR_ALPHABET_KEY = key(
+ "\u1000\u1001\u1002", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+104A: "၊" MYANMAR SIGN LITTLE SECTION
+ // Punctuation more keys for phone form factor.
+ private static final ExpectedKey[] MYANMAR_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+ "\u104A", ".", "?", "!", "#", ")", "(", "/", ";",
+ "...", "'", "@", ":", "-", "\"", "+", "%", "&");
+ // Punctuation more keys for tablet form factor.
+ private static final ExpectedKey[] MYANMAR_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+ ".", "'", "#", ")", "(", "/", ";", "@",
+ "...", ":", "-", "\"", "+", "%", "&");
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ public ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone,
+ final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(4, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+1041: "၁" MYANMAR DIGIT ONE
+ key("\u1041", moreKey("1")),
+ // U+1042: "၂" MYANMAR DIGIT TWO
+ key("\u1042", moreKey("2")),
+ // U+1043: "၃" MYANMAR DIGIT THREE
+ key("\u1043", moreKey("3")),
+ // U+1044: "၄" MYANMAR DIGIT FOUR
+ key("\u1044", moreKey("4")),
+ // U+1045: "၅" MYANMAR DIGIT FIVE
+ key("\u1045", moreKey("5")),
+ // U+1046: "၆" MYANMAR DIGIT SIX
+ key("\u1046", moreKey("6")),
+ // U+1047: "၇" MYANMAR DIGIT SEVEN
+ key("\u1047", moreKey("7")),
+ // U+1048: "၈" MYANMAR DIGIT EIGHT
+ key("\u1048", moreKey("8")),
+ // U+1049: "၉" MYANMAR DIGIT NINE
+ key("\u1049", moreKey("9")),
+ // U+1040: "၀" MYANMAR DIGIT ZERO
+ key("\u1040", moreKey("0")))
+ .setKeysOfRow(2,
+ // U+1006: "ဆ" MYANMAR LETTER CHA
+ // U+1039/U+1006: "္ဆ" MYANMAR SIGN VIRAMA/MYANMAR LETTER CHA
+ key("\u1006", moreKey("\u1039\u1006")),
+ // U+1010: "တ" MYANMAR LETTER TA
+ // U+1039/U+1010: "္တ" MYANMAR SIGN VIRAMA/MYANMAR LETTER TA
+ key("\u1010", moreKey("\u1039\u1010")),
+ // U+1014: "န" MYANMAR LETTER NA
+ // U+1039/U+1014: "္န" MYANMAR SIGN VIRAMA/MYANMAR LETTER NA
+ key("\u1014", moreKey("\u1039\u1014")),
+ // U+1019: "မ" MYANMAR LETTER MA
+ // U+1039/U+1019: "္မ" MYANMAR SIGN VIRAMA/MYANMAR LETTER MA
+ key("\u1019", moreKey("\u1039\u1019")),
+ // U+1021: "အ" MYANMAR LETTER A
+ // U+1015: "ပ" MYANMAR LETTER PA
+ "\u1021", "\u1015",
+ // U+1000: "က" MYANMAR LETTER KA
+ // U+1039/U+1000: "္က" MYANMAR SIGN VIRAMA/MYANMAR LETTER KA
+ key("\u1000", moreKey("\u1039\u1000")),
+ // U+1004: "င" MYANMAR LETTER NGA
+ // U+101E: "သ" MYANMAR LETTER SA
+ "\u1004", "\u101E",
+ // U+1005: "စ" MYANMAR LETTER CA
+ // U+1039/U+1005: "္စ" MYANMAR SIGN VIRAMA/MYANMAR LETTER CA
+ key("\u1005", moreKey("\u1039\u1005")))
+ .setKeysOfRow(3,
+ // U+1031: "ေ" MYANMAR VOWEL SIGN E
+ // U+103B: "ျ" MYANMAR CONSONANT SIGN MEDIAL YA
+ // U+103C: "ြ" MYANMAR CONSONANT SIGN MEDIAL RA
+ "\u1031", "\u103B", "\u103C",
+ // U+103D: "ွ" MYANMAR CONSONANT SIGN MEDIAL WA
+ // U+103E: "ှ" MYANMAR CONSONANT SIGN MEDIAL HA
+ // U+103D/U+103E:
+ // "ွှ" MYANMAR CONSONANT SIGN MEDIAL WA/MYANMAR CONSONANT SIGN MEDIAL HA
+ key("\u103D", joinMoreKeys("\u103E", "\u103D\u103E")),
+ // U+102D: "ိ" MYANMAR VOWEL SIGN I
+ // U+102E: "ီ" MYANMAR VOWEL SIGN II
+ key("\u102D", moreKey("\u102E")),
+ // U+102F: "ု" MYANMAR VOWEL SIGN U
+ // U+1030: "ူ" MYANMAR VOWEL SIGN UU
+ key("\u102F", moreKey("\u1030")),
+ // U+102C: "ာ" MYANMAR VOWEL SIGN AA
+ // U+102B: "ါ" MYANMAR VOWEL SIGN TALL AA
+ key("\u102C", moreKey("\u102B")),
+ // U+103A: "်" MYANMAR SIGN ASAT
+ // U+1032: "ဲ" MYANMAR VOWEL SIGN AI
+ key("\u103A", moreKey("\u1032")),
+ // U+1037: "့" MYANMAR SIGN DOT BELOW
+ // U+1036: "ံ" MYANMAR SIGN ANUSVARA
+ key("\u1037", moreKey("\u1036")),
+ // U+1038: "း" MYANMAR SIGN VISARGA
+ "\u1038")
+ .setKeysOfRow(4,
+ // U+1016: "ဖ" MYANMAR LETTER PHA
+ "\u1016",
+ // U+1011: "ထ" MYANMAR LETTER THA
+ // U+1039/U+1011: "္ထ" MYANMAR SIGN VIRAMA/MYANMAR LETTER THA
+ key("\u1011", moreKey("\u1039\u1011")),
+ // U+1001: "ခ" MYANMAR LETTER KHA
+ // U+1039/U+1001: "္ခ" MYANMAR SIGN VIRAMA/MYANMAR LETTER KHA
+ key("\u1001", moreKey("\u1039\u1001")),
+ // U+101C: "လ" MYANMAR LETTER LA
+ // U+1039/U+101C: "္လ" MYANMAR SIGN VIRAMA/MYANMAR LETTER LA
+ key("\u101C", moreKey("\u1039\u101C")),
+ // U+1018: "ဘ" MYANMAR LETTER BHA
+ // U+1039/U+1018: "္ဘ" MYANMAR SIGN VIRAMA/MYANMAR LETTER BHA
+ key("\u1018", moreKey("\u1039\u1018")),
+ // U+100A: "ည" MYANMAR LETTER NNYA
+ // U+1009: "ဉ" MYANMAR LETTER NYA
+ key("\u100A", moreKey("\u1009")),
+ // U+101B: "ရ" MYANMAR LETTER RA
+ // U+101D: "ဝ" MYANMAR LETTER WA
+ "\u101B", "\u101D")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+1027: "ဧ" MYANMAR LETTER E
+ // U+104F: "၏" MYANMAR SYMBOL GENITIVE
+ // U+1024: "ဤ" MYANMAR LETTER II
+ // U+1023: "ဣ" MYANMAR LETTER I
+ // U+104E: "၎" MYANMAR SYMBOL AFOREMENTIONED
+ // U+1000/U+103B/U+1015/U+103A: "ကျပ်" MYANMAR LETTER KA
+ // /MYANMAR CONSONANT SIGN MEDIAL YA/MYANMAR LETTER PA/MYANMAR SIGN ASAT
+ // U+1029: "ဩ" MYANMAR LETTER O
+ // U+102A: "ဪ" MYANMAR LETTER AU
+ // U+104D: "၍" MYANMAR SYMBOL COMPLETED
+ // U+104C: "၌" MYANMAR SYMBOL LOCATIVE
+ "\u1027", "\u104F", "\u1024", "\u1023", "\u104E", "\u1000\u103B\u1015\u103A",
+ "\u1029", "\u102A", "\u104D", "\u104C")
+ .setKeysOfRow(2,
+ // U+1017: "ဗ" MYANMAR LETTER BA
+ // U+1039/U+1017: "္ဗ" MYANMAR SIGN VIRAMA/MYANMAR LETTER BA
+ key("\u1017", moreKey("\u1039\u1017")),
+ // U+1012: "ဒ" MYANMAR LETTER DA
+ // U+1039/U+1012: "္ဒ" MYANMAR SIGN VIRAMA/MYANMAR LETTER DA
+ key("\u1012", moreKey("\u1039\u1012")),
+ // U+1013: "ဓ" MYANMAR LETTER DHA
+ // U+1039/U+1013: "္ဓ" MYANMAR SIGN VIRAMA/MYANMAR LETTER DHA
+ key("\u1013", moreKey("\u1039\u1013")),
+ // U+1003: "ဃ" MYANMAR LETTER GHA
+ // U+100E: "ဎ" MYANMAR LETTER DDHA
+ // U+103F: "ဿ" MYANMAR LETTER GREAT SA
+ // U+100F: "ဏ" MYANMAR LETTER NNA
+ "\u1003", "\u100E", "\u103F", "\u100F",
+ // U+1008: "ဈ" MYANMAR LETTER JHA
+ // U+1039/U+1008: "္ဈ" MYANMAR SIGN VIRAMA/MYANMAR LETTER JHA
+ key("\u1008", moreKey("\u1039\u1008")),
+ // U+1007: "ဇ" MYANMAR LETTER JA
+ // U+1039/U+1007: "္ဇ" MYANMAR SIGN VIRAMA/MYANMAR LETTER JA
+ key("\u1007", moreKey("\u1039\u1007")),
+ // U+1002: "ဂ" MYANMAR LETTER GA
+ // U+1039/U+1002: "္ဂ" MYANMAR SIGN VIRAMA/MYANMAR LETTER GA
+ key("\u1002", moreKey("\u1039\u1002")))
+ .setKeysOfRow(3,
+ // U+101A: "ယ" MYANMAR LETTER YA
+ // U+1039: "္" MYANMAR SIGN VIRAMA
+ // U+1004/U+103A/U+1039: "င်္င" MYANMAR LETTER NGA
+ // /MYANMAR SIGN ASAT/MYANMAR SIGN VIRAMA
+ // U+103E: "ှ" MYANMAR CONSONANT SIGN MEDIAL HA
+ // U+102E: "ီ" MYANMAR VOWEL SIGN II
+ // U+1030: "ူ" MYANMAR VOWEL SIGN UU
+ // U+102B: "ါ" MYANMAR VOWEL SIGN TALL AA
+ // U+1032: "ဲ" MYANMAR VOWEL SIGN AI
+ // U+1036: "ံ" MYANMAR SIGN ANUSVARA
+ // U+101F: "ဟ" MYANMAR LETTER HA
+ "\u101A", "\u1039", "\u1004\u103A\u1039", "\u103E", "\u102E", "\u1030",
+ "\u102B", "\u1032", "\u1036", "\u101F")
+ .setKeysOfRow(4,
+ // U+1025: "ဥ" MYANMAR LETTER U
+ // U+1026: "ဦ" MYANMAR LETTER UU
+ // U+100C: "ဌ" MYANMAR LETTER TTHA
+ // U+100B: "ဋ" MYANMAR LETTER TTA
+ // U+100D: "ဍ" MYANMAR LETTER DDA
+ // U+1020: "ဠ" MYANMAR LETTER LLA
+ // U+100B/U+1039/U+100C: "ဋ္ဌ" MYANMAR LETTER TTA
+ // /MYANMAR SIGN VIRAMA/MYANMAR LETTER TTHA
+ "\u1025", "\u1026", "\u100C", "\u100B", "\u100D", "\u1020",
+ "\u100B\u1039\u100C",
+ // U+100F/U+1039/U+100D: "ဏ္ဍ" MYANMAR LETTER NNA
+ // /MYANMAR SIGN VIRAMA/MYANMAR LETTER DDA
+ // U+100F/U+1039/U+100C: "ဏ္ဌ" MYANMAR LETTER NNA
+ // /MYANMAR SIGN VIRAMA/MYANMAR LETTER TTHA
+ key("\u100F\u1039\u100D", moreKey("\u100F\u1039\u100C")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java
new file mode 100644
index 000000000..7933d078c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The nepali_romanized layout
+ */
+public final class NepaliRomanized extends LayoutBase {
+ private static final String LAYOUT_NAME = "nepali_romanized";
+
+ public NepaliRomanized(final LayoutCustomizer customizer) {
+ super(customizer, HindiSymbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class NepaliRomanizedCustomizer extends HindiCustomizer {
+ public NepaliRomanizedCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_NEPALI; }
+
+ @Override
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+ }
+
+ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
+ private static final ExpectedKey CURRENCY_NEPALI = key("\u0930\u0941\u002E",
+ Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN, Symbols.EURO_SIGN, Symbols.POUND_SIGN,
+ Symbols.YEN_SIGN, Symbols.PESO_SIGN);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ // U+093C: "़" DEVANAGARI SIGN NUKTA
+ key("\u091F", joinMoreKeys("\u0967", "1", key(SIGN_NUKTA, "\u093C"))),
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ key(VOWEL_SIGN_AU, "\u094C", joinMoreKeys("\u0968", "2")),
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ key(VOWEL_SIGN_E, "\u0947", joinMoreKeys("\u0969", "3")),
+ // U+0930: "र" DEVANAGARI LETTER RA
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ key("\u0930", joinMoreKeys("\u096A", "4")),
+ // U+0924: "त" DEVANAGARI LETTER TA
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ key("\u0924", joinMoreKeys("\u096B", "5")),
+ // U+092F: "य" DEVANAGARI LETTER YA
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ key("\u092F", joinMoreKeys("\u096C", "6")),
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ key(VOWEL_SIGN_U, "\u0941", joinMoreKeys("\u096D", "7")),
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ key(VOWEL_SIGN_I, "\u093F", joinMoreKeys("\u096E", "8")),
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ key(VOWEL_SIGN_O, "\u094B", joinMoreKeys("\u096F", "9")),
+ // U+092A: "प" DEVANAGARI LETTER PA
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ key("\u092A", joinMoreKeys("\u0966", "0")),
+ // U+0907: "इ" DEVANAGARI LETTER I
+ "\u0907")
+ .setKeysOfRow(2,
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ key(VOWEL_SIGN_AA, "\u093E"),
+ // U+0938: "स" DEVANAGARI LETTER SA
+ // U+0926: "द" DEVANAGARI LETTER DA
+ // U+0909: "उ" DEVANAGARI LETTER U
+ // U+0917: "ग" DEVANAGARI LETTER GA
+ // U+0939: "ह" DEVANAGARI LETTER HA
+ // U+091C: "ज" DEVANAGARI LETTER JA
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+0932: "ल" DEVANAGARI LETTER LA
+ // U+090F: "ए" DEVANAGARI LETTER E
+ // U+0950: "ॐ" DEVANAGARI OM
+ "\u0938", "\u0926", "\u0909", "\u0917", "\u0939", "\u091C", "\u0915", "\u0932",
+ "\u090F", "\u0950")
+ .setKeysOfRow(3,
+ // U+0937: "ष" DEVANAGARI LETTER SSA
+ // U+0921: "ड" DEVANAGARI LETTER DDA
+ // U+091A: "च" DEVANAGARI LETTER CA
+ // U+0935: "व" DEVANAGARI LETTER VA
+ // U+092C: "ब" DEVANAGARI LETTER BHA
+ // U+0928: "न" DEVANAGARI LETTER NA
+ // U+092E: "म" DEVANAGARI LETTER MA
+ "\u0937", "\u0921", "\u091A", "\u0935", "\u092C", "\u0928", "\u092E",
+ // U+0964: "।" DEVANAGARI DANDA
+ // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+ key("\u0964", moreKey("\u093D")),
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ key(SIGN_VIRAMA, "\u094D"))
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0920: "ठ" DEVANAGARI LETTER TTHA
+ // U+0914: "औ" DEVANAGARI LETTER AU
+ "\u0920", "\u0914",
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ key(VOWEL_SIGN_AI, "\u0948"),
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ key(VOWEL_SIGN_VOCALIC_R, "\u0943"),
+ // U+0925: "थ" DEVANAGARI LETTER THA
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ "\u0925", "\u091E",
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ key(VOWEL_SIGN_UU, "\u0942"),
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ key(VOWEL_SIGN_II, "\u0940"),
+ // U+0913: "ओ" DEVANAGARI LETTER O
+ // U+092B: "फ" DEVANAGARI LETTER PHA
+ // U+0908: "ई" DEVANAGARI LETTER II
+ "\u0913", "\u092B", "\u0908")
+ .setKeysOfRow(2,
+ // U+0906: "आ" DEVANAGARI LETTER AA
+ // U+0936: "श" DEVANAGARI LETTER SHA
+ // U+0927: "ध" DEVANAGARI LETTER DHA
+ // U+090A: "ऊ" DEVANAGARI LETTER UU
+ // U+0918: "घ" DEVANAGARI LETTER GHA
+ // U+0905: "अ" DEVANAGARI LETTER A
+ // U+091D: "झ" DEVANAGARI LETTER JHA
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ // U+0965: "॥" DEVANAGARI DOUBLE DANDA
+ // U+0910: "ऐ" DEVANAGARI LETTER AI
+ // U+0903: "ः" DEVANAGARI SIGN VISARGA
+ "\u0906", "\u0936", "\u0927", "\u090A", "\u0918", "\u0905", "\u091D", "\u0916",
+ "\u0965", "\u0910", key(SIGN_VISARGA, "\u0903"))
+ .setKeysOfRow(3,
+ // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+ // U+0922: "ढ" DEVANAGARI LETTER DDHA
+ // U+091B: "छ" DEVANAGARI LETTER CHA
+ "\u090B", "\u0922", "\u091B",
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ key(SIGN_CANDRABINDU, "\u0901"),
+ // U+092D: "भ" DEVANAGARI LETTER BHA
+ // U+0923: "ण" DEVANAGARI LETTER NNA
+ "\u092D", "\u0923",
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ key(SIGN_ANUSVARA, "\u0902"),
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ "\u0919",
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ key(SIGN_VIRAMA, "\u094D"))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java b/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java
new file mode 100644
index 000000000..4d6cdedbf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.NepaliRomanized.NepaliRomanizedCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The nepali_traditional keyboard.
+ */
+public final class NepaliTraditional extends LayoutBase {
+ private static final String LAYOUT_NAME = "nepali_traditional";
+
+ public NepaliTraditional(final LayoutCustomizer customizer) {
+ super(customizer, HindiSymbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class NepaliTraditionalCustomizer extends NepaliRomanizedCustomizer {
+ public NepaliTraditionalCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ if (isPhone) {
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ return joinKeys(key(SIGN_VIRAMA, "\u094D", PHONE_PUNCTUATION_MORE_KEYS));
+ }
+ return super.getKeysRightToSpacebar(isPhone);
+ }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(3,
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+ // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+ key(VOWEL_SIGN_E, "\u0947", joinMoreKeys(
+ moreKey(SIGN_VISARGA, "\u0903"), "\u093D")),
+ // U+0964: "।" DEVANAGARI DANDA
+ "\u0964",
+ // U+0930: "र" DEVANAGARI LETTER RA
+ // U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U
+ key("\u0930", moreKey("\u0930\u0941")));
+ } else {
+ builder.addKeysOnTheRightOfRow(3,
+ // U+0903: "ः" DEVANAGARI SIGN VISARGA
+ // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+ key(SIGN_VISARGA, "\u0903", moreKey("\u093D")),
+ // U+0947: "े" DEVANAGARI VOWEL SIGN E
+ key(VOWEL_SIGN_E, "\u0947"),
+ // U+0964: "।" DEVANAGARI DANDA
+ "\u0964",
+ // U+0930: "र" DEVANAGARI LETTER RA
+ key("\u0930", moreKey("!")),
+ // U+094D: "्" DEVANAGARI SIGN VIRAMA
+ key(SIGN_VIRAMA, "\u094D", moreKey("?")));
+ }
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ ALPHABET_SHIFTED_COMMON);
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(3,
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ key(SIGN_ANUSVARA, "\u0902"),
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ "\u0919",
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ // U+0936/U+094D/U+0930:
+ // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key(VOWEL_SIGN_AI, "\u0948", moreKey("\u0936\u094D\u0930")));
+ } else {
+ builder.addKeysOnTheRightOfRow(3,
+ // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+ key(SIGN_ANUSVARA, "\u0902"),
+ // U+0919: "ङ" DEVANAGARI LETTER NGA
+ "\u0919",
+ // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+ // U+0936/U+094D/U+0930:
+ // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+ key(VOWEL_SIGN_AI, "\u0948", moreKey("\u0936\u094D\u0930")),
+ // U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U
+ key("\u0930\u0941", moreKey("!")),
+ "?");
+ }
+ return builder.build();
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ // U+0967: "१" DEVANAGARI DIGIT ONE
+ key("\u091F", joinMoreKeys("\u0967", "1")),
+ // U+0927: "ध" DEVANAGARI LETTER DHA
+ // U+0968: "२" DEVANAGARI DIGIT TWO
+ key("\u0927", joinMoreKeys("\u0968", "2")),
+ // U+092D: "भ" DEVANAGARI LETTER BHA
+ // U+0969: "३" DEVANAGARI DIGIT THREE
+ key("\u092D", joinMoreKeys("\u0969", "3")),
+ // U+091A: "च" DEVANAGARI LETTER CA
+ // U+096A: "४" DEVANAGARI DIGIT FOUR
+ key("\u091A", joinMoreKeys("\u096A", "4")),
+ // U+0924: "त" DEVANAGARI LETTER TA
+ // U+096B: "५" DEVANAGARI DIGIT FIVE
+ key("\u0924", joinMoreKeys("\u096B", "5")),
+ // U+0925: "थ" DEVANAGARI LETTER THA
+ // U+096C: "६" DEVANAGARI DIGIT SIX
+ key("\u0925", joinMoreKeys("\u096C", "6")),
+ // U+0917: "ग" DEVANAGARI LETTER G
+ // U+096D: "७" DEVANAGARI DIGIT SEVEN
+ key("\u0917", joinMoreKeys("\u096D", "7")),
+ // U+0937: "ष" DEVANAGARI LETTER SSA
+ // U+096E: "८" DEVANAGARI DIGIT EIGHT
+ key("\u0937", joinMoreKeys("\u096E", "8")),
+ // U+092F: "य" DEVANAGARI LETTER YA
+ // U+096F: "९" DEVANAGARI DIGIT NINE
+ key("\u092F", joinMoreKeys("\u096F", "9")),
+ // U+0909: "उ" DEVANAGARI LETTER U
+ // U+0966: "०" DEVANAGARI DIGIT ZERO
+ key("\u0909", joinMoreKeys("\u0966", "0")),
+ // U+0907: "इ" DEVANAGARI LETTER I
+ // U+0914: "औ" DEVANAGARI LETTER AU
+ key("\u0907", moreKey("\u0914")))
+ .setKeysOfRow(2,
+ // U+092C: "ब" DEVANAGARI LETTER BA
+ // U+0915: "क" DEVANAGARI LETTER KA
+ // U+092E: "म" DEVANAGARI LETTER MA
+ "\u092C", "\u0915", "\u092E",
+ // U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+ key(VOWEL_SIGN_AA, "\u093E"),
+ // U+0928: "न" DEVANAGARI LETTER NA
+ // U+091C: "ज" DEVANAGARI LETTER JA
+ // U+0935: "व" DEVANAGARI LETTER VA
+ // U+092A: "प" DEVANAGARI LETTER PA
+ "\u0928", "\u091C", "\u0935", "\u092A",
+ // U+093F: "ि" DEVANAGARI VOWEL SIGN I
+ key(VOWEL_SIGN_I, "\u093F"),
+ // U+0938: "स" DEVANAGARI LETTER SA
+ "\u0938",
+ // U+0941: "ु" DEVANAGARI VOWEL SIGN U
+ key(VOWEL_SIGN_U, "\u0941"))
+ .setKeysOfRow(3,
+ // U+0936: "श" DEVANAGARI LETTER SHA
+ // U+0939: "ह" DEVANAGARI LETTER HA
+ // U+0905: "अ" DEVANAGARI LETTER A
+ // U+0916: "ख" DEVANAGARI LETTER KHA
+ // U+0926: "द" DEVANAGARI LETTER DA
+ // U+0932: "ल" DEVANAGARI LETTER LA
+ "\u0936", "\u0939", "\u0905", "\u0916", "\u0926", "\u0932")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0924/U+094D/U+0924:
+ // "त्त" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TA
+ // U+091E: "ञ" DEVANAGARI LETTER NYA
+ // U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN
+ // VIRAMA/DEVANAGARI LETTER NYA
+ // U+0965: "॥" DEVANAGARI DOUBLE DANDA
+ key("\u0924\u094D\u0924",
+ joinMoreKeys("\u091E", "\u091C\u094D\u091E", "\u0965")),
+ // U+0921/U+094D/U+0922:
+ // "ड्ढ" DEVANAGARI LETTER DDA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DDHA
+ // U+0908: "ई" DEVANAGARI LETTER II
+ key("\u0921\u094D\u0922", moreKey("\u0908")),
+ // U+0910: "ऐ" DEVANAGARI LETTER AI
+ // U+0918: "घ" DEVANAGARI LETTER GHA
+ key("\u0910", moreKey("\u0918")),
+ // U+0926/U+094D/U+0935:
+ // "द्व" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER VA
+ // U+0926/U+094D/U+0927:
+ // "द्ध" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DHA
+ key("\u0926\u094D\u0935", moreKey("\u0926\u094D\u0927")),
+ // U+091F/U+094D/U+091F:
+ // "ट्ट" DEVANAGARI LETTER TTA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTA
+ // U+091B: "छ" DEVANAGARI LETTER CHA
+ key("\u091F\u094D\u091F", moreKey("\u091B")),
+ // U+0920/U+094D/U+0920:
+ // "ठ्ठ" DEVANAGARI LETTER TTHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTHA
+ // U+091F: "ट" DEVANAGARI LETTER TTA
+ key("\u0920\u094D\u0920", moreKey("\u091F")),
+ // U+090A: "ऊ" DEVANAGARI LETTER UU
+ // U+0920: "ठ" DEVANAGARI LETTER TTHA
+ key("\u090A", moreKey("\u0920")),
+ // U+0915/U+094D/U+0937:
+ // "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+ // U+0921: "ड" DEVANAGARI LETTER DDA
+ key("\u0915\u094D\u0937", moreKey("\u0921")),
+ // U+0907: "इ" DEVANAGARI LETTER I
+ // U+0922: "ढ" DEVANAGARI LETTER DDHA
+ key("\u0907", moreKey("\u0922")),
+ // U+090F: "ए" DEVANAGARI LETTER E
+ // U+0923: "ण" DEVANAGARI LETTER NNA
+ key("\u090F", moreKey("\u0923")),
+ // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+ // U+0913: "ओ" DEVANAGARI LETTER O
+ key(VOWEL_SIGN_VOCALIC_R, "\u0943", moreKey("\u0913")))
+ .setKeysOfRow(2,
+ // U+0906: "आ" DEVANAGARI LETTER AA
+ // U+0919/U+094D: "ङ्" DEVANAGARI LETTER NGA/DEVANAGARI SIGN VIRAMA
+ // U+0921/U+094D/U+0921:
+ // "ड्ड" DEVANAGARI LETTER DDA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DDA
+ "\u0906", "\u0919\u094D", "\u0921\u094D\u0921",
+ // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+ key(SIGN_CANDRABINDU, "\u0901"),
+ // U+0926/U+094D/U+0926:
+ // "द्द" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DA
+ // U+091D: "झ" DEVANAGARI LETTER JHA
+ "\u0926\u094D\u0926", "\u091D",
+ // U+094B: "ो" DEVANAGARI VOWEL SIGN O
+ key(VOWEL_SIGN_O, "\u094B"),
+ // U+092B: "फ" DEVANAGARI LETTER PHA
+ "\u092B",
+ // U+0940: "ी" DEVANAGARI VOWEL SIGN II
+ key(VOWEL_SIGN_II, "\u0940"),
+ // U+091F/U+094D/U+0920:
+ // "ट्ठ" DEVANAGARI LETTER TTA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTHA
+ "\u091F\u094D\u0920",
+ // U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+ key(VOWEL_SIGN_UU, "\u0942"))
+ .setKeysOfRow(3,
+ // U+0915/U+094D: "क्" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA
+ // U+0939/U+094D/U+092E:
+ // "ह्म" DEVANAGARI LETTER HA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER MA
+ // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+ // U+0950: "ॐ" DEVANAGARI OM
+ "\u0915\u094D", "\u0939\u094D\u092E", "\u090B", "\u0950",
+ // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+ key(VOWEL_SIGN_AU, "\u094C"),
+ // U+0926/U+094D/U+092F:
+ // "द्य" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER YA
+ "\u0926\u094D\u092F")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java b/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java
new file mode 100644
index 000000000..c791c404d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The Nordic alphabet keyboard.
+ */
+public final class Nordic extends LayoutBase {
+ private static final String LAYOUT_NAME = "nordic";
+
+ public Nordic(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ public static final String ROW1_11 = "ROW1_11";
+ public static final String ROW2_10 = "ROW2_10";
+ public static final String ROW2_11 = "ROW2_11";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("y", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")),
+ ROW1_11)
+ .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW2_10, ROW2_11)
+ .setKeysOfRow(3, "z", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
new file mode 100644
index 000000000..9da6dcc44
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * The PC QWERTY alphabet keyboard.
+ */
+public final class PcQwerty extends LayoutBase {
+ private static final String LAYOUT_NAME = "pcqwerty";
+
+ public PcQwerty(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class PcQwertyCustomizer extends LayoutCustomizer {
+ public PcQwertyCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return joinKeys(SHIFT_KEY);
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return joinKeys(SHIFT_KEY);
+ }
+
+ @Override
+ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+ return joinKeys(SETTINGS_KEY);
+ }
+
+ @Override
+ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+ return isPhone ? joinKeys(key(ENTER_KEY, EMOJI_KEY)) : joinKeys(EMOJI_KEY);
+ }
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ customizer.setAccentedLetters(builder);
+ builder.replaceKeyOfLabel(ROW1_1, key("`", moreKey("~")))
+ .replaceKeyOfLabel(ROW2_11, key("[", moreKey("{")))
+ .replaceKeyOfLabel(ROW2_12, key("]", moreKey("}")))
+ .replaceKeyOfLabel(ROW2_13, key("\\", moreKey("|")))
+ .replaceKeyOfLabel(ROW3_10, key(";", moreKey(":")))
+ .replaceKeyOfLabel(ROW3_11, key("'", joinMoreKeys(additionalMoreKey("\""),
+ customizer.getDoubleQuoteMoreKeys(),
+ customizer.getSingleQuoteMoreKeys())))
+ .setAdditionalMoreKeysPositionOf("'", 4)
+ .replaceKeyOfLabel(ROW4_8, key(",", moreKey("<")))
+ .replaceKeyOfLabel(ROW4_9, key(".", moreKey(">")))
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ .replaceKeyOfLabel(ROW4_10, key("/", joinMoreKeys("?", "\u00BF")));
+ if (isPhone) {
+ // U+221E: "∞" INFINITY
+ // U+2260: "≠" NOT EQUAL TO
+ // U+2248: "≈" ALMOST EQUAL TO
+ builder.replaceKeyOfLabel(ROW1_13, key("=",
+ joinMoreKeys("\u221E", "\u2260", "\u2248", "+")));
+ } else {
+ // U+221E: "∞" INFINITY
+ // U+2260: "≠" NOT EQUAL TO
+ // U+2248: "≈" ALMOST EQUAL TO
+ builder.replaceKeyOfLabel(ROW1_13, key("=",
+ joinMoreKeys("+", "\u221E", "\u2260", "\u2248")));
+ }
+ return builder.build();
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
+ final ExpectedKeyboardBuilder builder;
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
+ || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+ builder = new ExpectedKeyboardBuilder(getCommonAlphabetLayout(isPhone));
+ } else {
+ builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ final LayoutCustomizer customizer = getCustomizer();
+ customizer.setAccentedLetters(builder);
+ builder.setKeysOfRow(1,
+ "~",
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ key("!", moreKey("\u00A1")),
+ "@", "#",
+ customizer.getCurrencyKey(),
+ // U+2030: "‰" PER MILLE SIGN
+ key("%", moreKey("\u2030")),
+ "^", "&",
+ // U+2020: "†" DAGGER
+ // U+2021: "‡" DOUBLE DAGGER
+ // U+2605: "★" BLACK STAR
+ key("*", joinMoreKeys("\u2020", "\u2021", "\u2605")),
+ "(", ")", "_",
+ // U+00B1: "±" PLUS-MINUS SIGN
+ // U+00D7: "×" MULTIPLICATION SIGN
+ // U+00F7: "÷" DIVISION SIGN
+ // U+221A: "√" SQUARE ROOT
+ key("+", joinMoreKeys("\u00B1", "\u00D7", "\u00F7", "\u221A")))
+ .replaceKeyOfLabel(ROW2_11, key("{"))
+ .replaceKeyOfLabel(ROW2_12, key("}"))
+ .replaceKeyOfLabel(ROW2_13, key("|"))
+ .replaceKeyOfLabel(ROW3_10, key(":"))
+ .replaceKeyOfLabel(ROW3_11, key("\"", joinMoreKeys(
+ customizer.getDoubleQuoteMoreKeys(),
+ customizer.getSingleQuoteMoreKeys())))
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .replaceKeyOfLabel(ROW4_8, key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")))
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .replaceKeyOfLabel(ROW4_9, key(">", joinMoreKeys("\u203A", "\u2265", "\u00BB")))
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ .replaceKeyOfLabel(ROW4_10, key("?", moreKey("\u00BF")));
+ }
+ builder.toUpperCase(getLocale());
+ return builder.build();
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(3, DELETE_KEY);
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(2, TAB_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_SYMBOLS
+ || elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
+ return null;
+ }
+ return super.getLayout(isPhone, elementId);
+ }
+
+ private static final String ROW1_1 = "ROW1_1";
+ private static final String ROW1_13 = "ROW1_13";
+ private static final String ROW2_11 = "ROW2_11";
+ private static final String ROW2_12 = "ROW2_12";
+ private static final String ROW2_13 = "ROW2_13";
+ private static final String ROW3_10 = "ROW3_10";
+ private static final String ROW3_11 = "ROW3_11";
+ private static final String ROW4_8 = "ROW4_8";
+ private static final String ROW4_9 = "ROW4_9";
+ private static final String ROW4_10 = "ROW4_10";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ ROW1_1,
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ key("1", joinMoreKeys(
+ "!", "\u00A1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")),
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ key("2", joinMoreKeys("@", "\u00B2", "\u2154")),
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ key("3", joinMoreKeys("#", "\u00B3", "\u00BE", "\u215C")),
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ key("4", joinMoreKeys("$", "\u2074")),
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ key("5", joinMoreKeys("%", "\u215D")),
+ key("6", moreKey("^")),
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ key("7", joinMoreKeys("&", "\u215E")),
+ key("8", moreKey("*")),
+ key("9", moreKey("(")),
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ key("0", joinMoreKeys(")", "\u207F", "\u2205")),
+ // U+2013: "–" EN DASH
+ // U+2014: "—" EM DASH
+ // U+00B7: "·" MIDDLE DOT
+ key("-", joinMoreKeys("_", "\u2013", "\u2014", "\u00B7")),
+ ROW1_13)
+ .setKeysOfRow(2, "q", "w", "e", "r", "t", "y", "u", "i", "o", "p",
+ ROW2_11, ROW2_12, ROW2_13)
+ .setKeysOfRow(3, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW3_10, ROW3_11)
+ .setKeysOfRow(4, "z", "x", "c", "v", "b", "n", "m", ROW4_8, ROW4_9, ROW4_10)
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
new file mode 100644
index 000000000..d790a1e53
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The QWERTY alphabet keyboard.
+ */
+public final class Qwerty extends LayoutBase {
+ private static final String LAYOUT_NAME = "qwerty";
+
+ public Qwerty(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("y", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")))
+ .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l")
+ .setKeysOfRow(3, "z", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java
new file mode 100644
index 000000000..26ba6cffb
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+public final class Qwertz extends LayoutBase {
+ private static final String LAYOUT_NAME = "qwertz";
+
+ public Qwertz(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("z", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")))
+ .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l")
+ .setKeysOfRow(3, "y", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java
new file mode 100644
index 000000000..5c0ffb4f9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Sinhala keyboard.
+ */
+public final class Sinhala extends LayoutBase {
+ private static final String LAYOUT_NAME = "sinhala";
+
+ public Sinhala(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class SinhalaCustomizer extends LayoutCustomizer {
+ public SinhalaCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return SINHALA_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0D85: "අ" SINHALA LETTER AYANNA
+ // U+0D86: "ආ" SINHALA LETTER AAYANNA
+ private static final ExpectedKey SINHALA_ALPHABET_KEY = key(
+ "\u0D85,\u0D86", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return ALPHABET_COMMON;
+ }
+ return ALPHABET_SHIFTED_COMMON;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0DD4: "ු" SINHALA VOWEL SIGN KETTI PAA-PILLA
+ key("\u0DD4", moreKey("1")),
+ // U+0D85: "අ" SINHALA LETTER AYANNA
+ key("\u0D85", moreKey("2")),
+ // U+0DD0: "ැ" SINHALA VOWEL SIGN KETTI AEDA-PILLA
+ key("\u0DD0", moreKey("3")),
+ // U+0DBB: "ර" SINHALA LETTER RAYANNA
+ key("\u0DBB", moreKey("4")),
+ // U+0D91: "එ" SINHALA LETTER EYANNA
+ key("\u0D91", moreKey("5")),
+ // U+0DC4: "හ" SINHALA LETTER HAYANNA
+ key("\u0DC4", moreKey("6")),
+ // U+0DB8: "ම" SINHALA LETTER MAYANNA
+ key("\u0DB8", moreKey("7")),
+ // U+0DC3: "ස" SINHALA LETTER DANTAJA SAYANNA
+ key("\u0DC3", moreKey("8")),
+ // U+0DAF: "ද" SINHALA LETTER ALPAPRAANA DAYANNA
+ // U+0DB3: "ඳ" SINHALA LETTER SANYAKA DAYANNA
+ key("\u0DAF", joinMoreKeys("9", "\u0DB3")),
+ // U+0DA0: "ච" SINHALA LETTER ALPAPRAANA CAYANNA
+ key("\u0DA0", moreKey("0")),
+ // U+0DA4: "ඤ" SINHALA LETTER TAALUJA NAASIKYAYA
+ // U+0DF4: "෴" SINHALA PUNCTUATION KUNDDALIYA
+ key("\u0DA4", moreKey("\u0DF4")))
+ .setKeysOfRow(2,
+ // U+0DCA: "්" SINHALA SIGN AL-LAKUNA
+ // U+0DD2: "ි" SINHALA VOWEL SIGN KETTI IS-PILLA
+ // U+0DCF: "ා" SINHALA VOWEL SIGN AELA-PILLA
+ // U+0DD9: "ෙ" SINHALA VOWEL SIGN KOMBUVA
+ // U+0DA7: "ට" SINHALA LETTER ALPAPRAANA TTAYANNA
+ // U+0DBA: "ය" SINHALA LETTER YAYANNA
+ // U+0DC0: "ව" SINHALA LETTER VAYANNA
+ // U+0DB1: "න" SINHALA LETTER DANTAJA NAYANNA
+ // U+0D9A: "ක" SINHALA LETTER ALPAPRAANA KAYANNA
+ // U+0DAD: "ත" SINHALA LETTER ALPAPRAANA TAYANNA
+ // U+0D8F: "ඏ" SINHALA LETTER ILUYANNA
+ "\u0DCA", "\u0DD2", "\u0DCF", "\u0DD9", "\u0DA7", "\u0DBA", "\u0DC0", "\u0DB1",
+ "\u0D9A", "\u0DAD", "\u0D8F")
+ .setKeysOfRow(3,
+ // U+0D82: "ං" SINHALA SIGN ANUSVARAYA
+ // U+0D83: "ඃ" SINHALA SIGN VISARGAYA
+ key("\u0D82", moreKey("\u0D83")),
+ // U+0DA2: "ජ" SINHALA LETTER ALPAPRAANA JAYANNA
+ // U+0DA6: "ඦ" SINHALA LETTER SANYAKA JAYANNA
+ key("\u0DA2", moreKey("\u0DA6")),
+ // U+0DA9: "ඩ" SINHALA LETTER ALPAPRAANA DDAYANNA
+ // U+0DAC: "ඬ" SINHALA LETTER SANYAKA DDAYANNA
+ key("\u0DA9", moreKey("\u0DAC")),
+ // U+0D89: "ඉ" SINHALA LETTER IYANNA
+ // U+0DB6: "බ" SINHALA LETTER ALPAPRAANA BAYANNA
+ // U+0DB4: "ප" SINHALA LETTER ALPAPRAANA PAYANNA
+ // U+0DBD: "ල" SINHALA LETTER DANTAJA LAYANNA
+ "\u0D89", "\u0DB6", "\u0DB4", "\u0DBD",
+ // U+0D9C: "ග" SINHALA LETTER ALPAPRAANA GAYANNA
+ // U+0D9F: "ඟ" SINHALA LETTER SANYAKA GAYANNA
+ key("\u0D9C", moreKey("\u0D9F")),
+ // U+0DF3: "ෳ" SINHALA VOWEL SIGN DIGA GAYANUKITTA
+ "\u0DF3")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0DD6: "ූ" SINHALA VOWEL SIGN DIGA PAA-PILLA
+ // U+0D8B: "උ" SINHALA LETTER UYANNA
+ // U+0DD1: "ෑ" SINHALA VOWEL SIGN DIGA AEDA-PILLA
+ // U+0D8D: "ඍ" SINHALA LETTER IRUYANNA
+ // U+0D94: "ඔ" SINHALA LETTER OYANNA
+ // U+0DC1: "ශ" SINHALA LETTER TAALUJA SAYANNA
+ // U+0DB9: "ඹ" SINHALA LETTER AMBA BAYANNA
+ // U+0DC2: "ෂ" SINHALA LETTER MUURDHAJA SAYANNA
+ // U+0DB0: "ධ" SINHALA LETTER MAHAAPRAANA DAYANNA
+ // U+0DA1: "ඡ" SINHALA LETTER MAHAAPRAANA CAYANNA
+ "\u0DD6", "\u0D8B", "\u0DD1", "\u0D8D", "\u0D94", "\u0DC1", "\u0DB9", "\u0DC2",
+ "\u0DB0", "\u0DA1",
+ // U+0DA5: "ඥ" SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA
+ // U+0DF4: "෴" SINHALA PUNCTUATION KUNDDALIYA
+ key("\u0DA5", moreKey("\u0DF4")))
+ .setKeysOfRow(2,
+ // U+0DDF: "ෟ" SINHALA VOWEL SIGN GAYANUKITTA
+ // U+0DD3: "ී" SINHALA VOWEL SIGN DIGA IS-PILLA
+ // U+0DD8: "ෘ" SINHALA VOWEL SIGN GAETTA-PILLA
+ // U+0DC6: "ෆ" SINHALA LETTER FAYANNA
+ // U+0DA8: "ඨ" SINHALA LETTER MAHAAPRAANA TTAYANNA
+ // U+0DCA/U+200D/U+0DBA:
+ // "්‍ය" SINHALA SIGN AL-LAKUNA/ZERO WIDTH JOINER/SINHALA LETTER YAYANNA
+ // U+0DC5/U+0DD4:
+ // "ළු" SINHALA LETTER MUURDHAJA LAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
+ // U+0DAB: "ණ" SINHALA LETTER MUURDHAJA NAYANNA
+ // U+0D9B: "ඛ" SINHALA LETTER MAHAAPRAANA KAYANNA
+ // U+0DAE: "ථ" SINHALA LETTER MAHAAPRAANA TAYANNA
+ // U+0DCA/U+200D/U+0DBB:
+ // "්‍ර" SINHALA SIGN AL-LAKUNA/ZERO WIDTH JOINER/SINHALA LETTER RAYANNA
+ "\u0DDF", "\u0DD3", "\u0DD8", "\u0DC6", "\u0DA8", "\u0DCA\u200D\u0DBA",
+ "\u0DC5\u0DD4", "\u0DAB", "\u0D9B", "\u0DAE", "\u0DCA\u200D\u0DBB")
+ .setKeysOfRow(3,
+ // U+0D9E: "ඞ" SINHALA LETTER KANTAJA NAASIKYAYA
+ // U+0DA3: "ඣ" SINHALA LETTER MAHAAPRAANA JAYANNA
+ // U+0DAA: "ඪ" SINHALA LETTER MAHAAPRAANA DDAYANNA
+ // U+0D8A: "ඊ" SINHALA LETTER IIYANNA
+ // U+0DB7: "භ" SINHALA LETTER MAHAAPRAANA BAYANNA
+ // U+0DB5: "ඵ" SINHALA LETTER MAHAAPRAANA PAYANNA
+ // U+0DC5: "ළ" SINHALA LETTER MUURDHAJA LAYANNA
+ // U+0D9D: "ඝ" SINHALA LETTER MAHAAPRAANA GAYANNA
+ // U+0DBB/U+0DCA/U+200D:
+ // "ර්‍" SINHALA LETTER RAYANNA/SINHALA SIGN AL-LAKUNA/ZERO WIDTH JOINER
+ "\u0d9E", "\u0DA3", "\u0DAA", "\u0D8A", "\u0DB7", "\u0DB5", "\u0DC5", "\u0D9D",
+ "\u0DBB\u0DCA\u200D")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java b/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java
new file mode 100644
index 000000000..be8b435d4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public final class SouthSlavic extends LayoutBase {
+ private static final String LAYOUT_NAME = "south_slavic";
+
+ public SouthSlavic(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class SouthSlavicLayoutCustomizer extends LayoutCustomizer {
+ public SouthSlavicLayoutCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public final ExpectedKey getAlphabetKey() { return SOUTH_SLAVIC_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0410: "А" CYRILLIC CAPITAL LETTER A
+ // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
+ // U+0412: "В" CYRILLIC CAPITAL LETTER VE
+ private static final ExpectedKey SOUTH_SLAVIC_ALPHABET_KEY = key(
+ "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ public static final String ROW1_6 = "ROW1_6";
+ public static final String ROW2_11 = "ROW2_11";
+ public static final String ROW3_1 = "ROW3_1";
+ public static final String ROW3_8 = "ROW3_8";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0459: "љ" CYRILLIC SMALL LETTER LJE
+ key("\u0459", additionalMoreKey("1")),
+ // U+045A: "њ" CYRILLIC SMALL LETTER NJE
+ key("\u045A", additionalMoreKey("2")),
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ key("\u0435", additionalMoreKey("3")),
+ // U+0440: "р" CYRILLIC SMALL LETTER ER
+ key("\u0440", additionalMoreKey("4")),
+ // U+0442: "т" CYRILLIC SMALL LETTER TE
+ key("\u0442", additionalMoreKey("5")),
+ key(ROW1_6, additionalMoreKey("6")),
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ key("\u0443", additionalMoreKey("7")),
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ key("\u0438", additionalMoreKey("8")),
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ key("\u043E", additionalMoreKey("9")),
+ // U+043F: "п" CYRILLIC SMALL LETTER PE
+ key("\u043F", additionalMoreKey("0")),
+ // U+0448: "ш" CYRILLIC SMALL LETTER SHA
+ "\u0448")
+ .setKeysOfRow(2,
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+0441: "с" CYRILLIC SMALL LETTER ES
+ // U+0434: "д" CYRILLIC SMALL LETTER DE
+ // U+0444: "ф" CYRILLIC SMALL LETTER EF
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ // U+0445: "х" CYRILLIC SMALL LETTER HA
+ // U+0458: "ј" CYRILLIC SMALL LETTER JE
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ // U+043B: "л" CYRILLIC SMALL LETTER EL
+ // U+0447: "ч" CYRILLIC SMALL LETTER CHE
+ "\u0430", "\u0441", "\u0434", "\u0444", "\u0433", "\u0445", "\u0458", "\u043A",
+ "\u043B", "\u0447", ROW2_11)
+ .setKeysOfRow(3,
+ // U+045F: "џ" CYRILLIC SMALL LETTER DZHE
+ // U+0446: "ц" CYRILLIC SMALL LETTER TSE
+ // U+0432: "в" CYRILLIC SMALL LETTER VE
+ // U+0431: "б" CYRILLIC SMALL LETTER BE
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ // U+043C: "м" CYRILLIC SMALL LETTER EM
+ // U+0436: "ж" CYRILLIC SMALL LETTER ZHE
+ ROW3_1, "\u045F", "\u0446", "\u0432", "\u0431", "\u043D", "\u043C", ROW3_8,
+ "\u0436")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java b/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java
new file mode 100644
index 000000000..225b9f604
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+public final class Spanish extends LayoutBase {
+ private static final String LAYOUT_NAME = "spanish";
+
+ public Spanish(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ public static final String ROW2_10 = "ROW2_10";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("y", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")))
+ .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW2_10)
+ .setKeysOfRow(3, "z", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java b/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java
new file mode 100644
index 000000000..01a602054
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+public final class Swiss extends LayoutBase {
+ private static final String LAYOUT_NAME = "swiss";
+
+ public Swiss(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+ public static final String ROW1_11 = "ROW1_11";
+ public static final String ROW2_10 = "ROW2_10";
+ public static final String ROW2_11 = "ROW2_11";
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ key("q", additionalMoreKey("1")),
+ key("w", additionalMoreKey("2")),
+ key("e", additionalMoreKey("3")),
+ key("r", additionalMoreKey("4")),
+ key("t", additionalMoreKey("5")),
+ key("z", additionalMoreKey("6")),
+ key("u", additionalMoreKey("7")),
+ key("i", additionalMoreKey("8")),
+ key("o", additionalMoreKey("9")),
+ key("p", additionalMoreKey("0")),
+ ROW1_11)
+ .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW2_10, ROW2_11)
+ .setKeysOfRow(3, "y", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
new file mode 100644
index 000000000..5f3e4b196
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The symbols keyboard layout.
+ */
+public class Symbols extends AbstractLayoutBase {
+ private final LayoutCustomizer mCustomizer;
+
+ public Symbols(final LayoutCustomizer customizer) {
+ mCustomizer = customizer;
+ }
+
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(SYMBOLS_COMMON);
+ final LayoutCustomizer customizer = mCustomizer;
+ builder.replaceKeyOfLabel(CURRENCY, customizer.getCurrencyKey());
+ builder.replaceKeyOfLabel(DOUBLE_QUOTE, key("\"", joinMoreKeys(
+ customizer.getDoubleQuoteMoreKeys(), customizer.getDoubleAngleQuoteKeys())));
+ builder.replaceKeyOfLabel(SINGLE_QUOTE, key("'", joinMoreKeys(
+ customizer.getSingleQuoteMoreKeys(), customizer.getSingleAngleQuoteKeys())));
+ if (isPhone) {
+ builder.addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone))
+ .addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ // Tablet symbols keyboard has extra two keys at the left edge of the 3rd row.
+ builder.addKeysOnTheLeftOfRow(3, (Object[])joinKeys("\\", "="));
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone))
+ .addKeysOnTheRightOfRow(3, customizer.getSymbolsShiftKey(isPhone))
+ .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY);
+ }
+ return builder.build();
+ }
+
+ // Variations of the "currency" key on the 2nd row.
+ public static final String CURRENCY = "CURRENCY";
+ // U+00A2: "¢" CENT SIGN
+ // U+00A3: "£" POUND SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+20AC: "€" EURO SIGN
+ // U+20B1: "₱" PESO SIGN
+ public static final ExpectedKey DOLLAR_SIGN = key("$");
+ public static final ExpectedKey CENT_SIGN = key("\u00A2");
+ public static final ExpectedKey POUND_SIGN = key("\u00A3");
+ public static final ExpectedKey YEN_SIGN = key("\u00A5");
+ public static final ExpectedKey EURO_SIGN = key("\u20AC");
+ public static final ExpectedKey PESO_SIGN = key("\u20B1");
+ public static final ExpectedKey CURRENCY_DOLLAR = key("$",
+ CENT_SIGN, POUND_SIGN, EURO_SIGN, YEN_SIGN, PESO_SIGN);
+ public static final ExpectedKey CURRENCY_EURO = key("\u20AC",
+ CENT_SIGN, POUND_SIGN, DOLLAR_SIGN, YEN_SIGN, PESO_SIGN);
+ public static final ExpectedKey[] CURRENCY_GENERIC_MORE_KEYS = joinMoreKeys(
+ Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN, Symbols.EURO_SIGN, Symbols.POUND_SIGN,
+ Symbols.YEN_SIGN, Symbols.PESO_SIGN);
+
+ // Variations of the "double quote" key's "more keys" on the 3rd row.
+ public static final String DOUBLE_QUOTE = "DOUBLE_QUOTE";
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
+ // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
+ private static final ExpectedKey DQUOTE_LEFT = key("\u201C");
+ private static final ExpectedKey DQUOTE_RIGHT = key("\u201D");
+ private static final ExpectedKey DQUOTE_LOW9 = key("\u201E");
+ public static ExpectedKey[] DOUBLE_QUOTES_9LR = { DQUOTE_LOW9, DQUOTE_LEFT, DQUOTE_RIGHT };
+ public static ExpectedKey[] DOUBLE_QUOTES_R9L = { DQUOTE_RIGHT, DQUOTE_LOW9, DQUOTE_LEFT };
+ public static ExpectedKey[] DOUBLE_QUOTES_L9R = { DQUOTE_LEFT, DQUOTE_LOW9, DQUOTE_RIGHT };
+ public static ExpectedKey[] DOUBLE_QUOTES_LR9 = { DQUOTE_LEFT, DQUOTE_RIGHT, DQUOTE_LOW9 };
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ private static final ExpectedKey DAQUOTE_LEFT = key("\u00AB");
+ private static final ExpectedKey DAQUOTE_RIGHT = key("\u00BB");
+ public static ExpectedKey[] DOUBLE_ANGLE_QUOTES_LR = { DAQUOTE_LEFT, DAQUOTE_RIGHT };
+ public static ExpectedKey[] DOUBLE_ANGLE_QUOTES_RL = { DAQUOTE_RIGHT, DAQUOTE_LEFT };
+
+ // Variations of the "single quote" key's "more keys" on the 3rd row.
+ public static final String SINGLE_QUOTE = "SINGLE_QUOTE";
+ // U+2018: "‘" LEFT SINGLE QUOTATION MARK
+ // U+2019: "’" RIGHT SINGLE QUOTATION MARK
+ // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
+ private static final ExpectedKey SQUOTE_LEFT = key("\u2018");
+ private static final ExpectedKey SQUOTE_RIGHT = key("\u2019");
+ private static final ExpectedKey SQUOTE_LOW9 = key("\u201A");
+ public static ExpectedKey[] SINGLE_QUOTES_9LR = { SQUOTE_LOW9, SQUOTE_LEFT, SQUOTE_RIGHT };
+ public static ExpectedKey[] SINGLE_QUOTES_R9L = { SQUOTE_RIGHT, SQUOTE_LOW9, SQUOTE_LEFT };
+ public static ExpectedKey[] SINGLE_QUOTES_L9R = { SQUOTE_LEFT, SQUOTE_LOW9, SQUOTE_RIGHT };
+ public static ExpectedKey[] SINGLE_QUOTES_LR9 = { SQUOTE_LEFT, SQUOTE_RIGHT, SQUOTE_LOW9 };
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ private static final ExpectedKey SAQUOTE_LEFT = key("\u2039");
+ private static final ExpectedKey SAQUOTE_RIGHT = key("\u203A");
+ public static ExpectedKey[] SINGLE_ANGLE_QUOTES_LR = { SAQUOTE_LEFT, SAQUOTE_RIGHT };
+ public static ExpectedKey[] SINGLE_ANGLE_QUOTES_RL = { SAQUOTE_RIGHT, SAQUOTE_LEFT };
+
+ // Common symbols keyboard layout.
+ private static final ExpectedKey[][] SYMBOLS_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ key("1", joinMoreKeys("\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")),
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ key("2", joinMoreKeys("\u00B2", "\u2154")),
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ key("3", joinMoreKeys("\u00B3", "\u00BE", "\u215C")),
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ key("4", moreKey("\u2074")),
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ key("5", moreKey("\u215D")),
+ "6",
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ key("7", moreKey("\u215E")),
+ "8", "9",
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ key("0", joinMoreKeys("\u207F", "\u2205")))
+ .setKeysOfRow(2,
+ key("@"), key("#"), key(CURRENCY),
+ // U+2030: "‰" PER MILLE SIGN
+ key("%", moreKey("\u2030")),
+ "&",
+ // U+2013: "–" EN DASH
+ // U+2014: "—" EM DASH
+ // U+00B7: "·" MIDDLE DOT
+ key("-", joinMoreKeys("_", "\u2013", "\u2014", "\u00B7")),
+ // U+00B1: "±" PLUS-MINUS SIGN
+ key("+", moreKey("\u00B1")),
+ key("(", joinMoreKeys("<", "{", "[")),
+ key(")", joinMoreKeys(">", "}", "]")))
+ .setKeysOfRow(3,
+ // U+2020: "†" DAGGER
+ // U+2021: "‡" DOUBLE DAGGER
+ // U+2605: "★" BLACK STAR
+ key("*", joinMoreKeys("\u2020", "\u2021", "\u2605")),
+ key(DOUBLE_QUOTE), key(SINGLE_QUOTE), key(":"), key(";"),
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ key("!", moreKey("\u00A1")),
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ key("?", moreKey("\u00BF")))
+ .setKeysOfRow(4,
+ key(","), key("_"), SPACE_KEY, key("/"),
+ // U+2026: "…" HORIZONTAL ELLIPSIS
+ key(".", moreKey("\u2026")))
+ .build();
+
+ public static class RtlSymbols extends Symbols {
+ public RtlSymbols(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ private static final ExpectedKey DAQUOTE_LEFT_RTL = key("\u00AB", "\u00BB");
+ private static final ExpectedKey DAQUOTE_RIGHT_RTL = key("\u00BB", "\u00AB");
+ public static ExpectedKey[] DOUBLE_ANGLE_QUOTES_LR_RTL = {
+ DAQUOTE_LEFT_RTL, DAQUOTE_RIGHT_RTL
+ };
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ private static final ExpectedKey SAQUOTE_LEFT_RTL = key("\u2039", "\u203A");
+ private static final ExpectedKey SAQUOTE_RIGHT_RTL = key("\u203A", "\u2039");
+ public static ExpectedKey[] SINGLE_ANGLE_QUOTES_LR_RTL = {
+ SAQUOTE_LEFT_RTL, SAQUOTE_RIGHT_RTL
+ };
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ .replaceKeyOfLabel("(", key("(", ")",
+ moreKey("<", ">"), moreKey("{", "}"), moreKey("[", "]")))
+ .replaceKeyOfLabel(")", key(")", "(",
+ moreKey(">", "<"), moreKey("}", "{"), moreKey("]", "[")))
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
new file mode 100644
index 000000000..3265e10e1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+
+/**
+ * The symbols shifted keyboard layout.
+ */
+public class SymbolsShifted extends AbstractLayoutBase {
+ private final LayoutCustomizer mCustomizer;
+
+ public SymbolsShifted(final LayoutCustomizer customizer) {
+ mCustomizer = customizer;
+ }
+
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(SYMBOLS_SHIFTED_COMMON);
+ final LayoutCustomizer customizer = mCustomizer;
+ builder.replaceKeyOfLabel(OTHER_CURRENCIES, (Object[])customizer.getOtherCurrencyKeys());
+ if (isPhone) {
+ builder.addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey())
+ .addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ // Tablet symbols shifted keyboard has extra two keys at the right edge of the 3rd row.
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ builder.addKeysOnTheRightOfRow(3, (Object[])joinKeys("\u00A1", "\u00BF"));
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey())
+ .addKeysOnTheRightOfRow(3, customizer.getBackToSymbolsKey())
+ .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY);
+ }
+ return builder.build();
+ }
+
+ // Variations of the "other currencies" keys on the 2rd row.
+ public static final String OTHER_CURRENCIES = "OTHER_CURRENCY";
+ public static final ExpectedKey[] CURRENCIES_OTHER_THAN_DOLLAR = {
+ Symbols.POUND_SIGN, Symbols.CENT_SIGN, Symbols.EURO_SIGN, Symbols.YEN_SIGN
+ };
+ public static final ExpectedKey[] CURRENCIES_OTHER_THAN_EURO = {
+ Symbols.POUND_SIGN, Symbols.YEN_SIGN, key(Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN),
+ Symbols.CENT_SIGN
+ };
+ public static final ExpectedKey[] CURRENCIES_OTHER_GENERIC = {
+ Symbols.POUND_SIGN, Symbols.EURO_SIGN, key(Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN),
+ Symbols.CENT_SIGN
+ };
+
+ // Common symbols shifted keyboard layout.
+ private static final ExpectedKey[][] SYMBOLS_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0060: "`" GRAVE ACCENT
+ "~", "\u0060", "|",
+ // U+2022: "•" BULLET
+ // U+266A: "♪" EIGHTH NOTE
+ // U+2665: "♥" BLACK HEART SUIT
+ // U+2660: "♠" BLACK SPADE SUIT
+ // U+2666: "♦" BLACK DIAMOND SUIT
+ // U+2663: "♣" BLACK CLUB SUIT
+ key("\u2022", joinMoreKeys("\u266A", "\u2665", "\u2660", "\u2666", "\u2663")),
+ // U+221A: "√" SQUARE ROOT
+ "\u221A",
+ // U+03C0: "π" GREEK SMALL LETTER PI
+ // U+03A0: "Π" GREEK CAPITAL LETTER PI
+ key("\u03C0", moreKey("\u03A0")),
+ // U+00F7: "÷" DIVISION SIGN
+ // U+00D7: "×" MULTIPLICATION SIGN
+ "\u00F7", "\u00D7",
+ // U+00B6: "¶" PILCROW SIGN
+ // U+00A7: "§" SECTION SIGN
+ key("\u00B6", moreKey("\u00A7")),
+ // U+2206: "∆" INCREMENT
+ "\u2206")
+ .setKeysOfRow(2,
+ OTHER_CURRENCIES,
+ // U+2191: "↑" UPWARDS ARROW
+ // U+2193: "↓" DOWNWARDS ARROW
+ // U+2190: "←" LEFTWARDS ARROW
+ // U+2192: "→" RIGHTWARDS ARROW
+ key("^", joinMoreKeys("\u2191", "\u2193", "\u2190", "\u2192")),
+ // U+00B0: "°" DEGREE SIGN
+ // U+2032: "′" PRIME
+ // U+2033: "″" DOUBLE PRIME
+ key("\u00B0", joinMoreKeys("\u2032", "\u2033")),
+ // U+2260: "≠" NOT EQUAL TO
+ // U+2248: "≈" ALMOST EQUAL TO
+ // U+221E: "∞" INFINITY
+ key("=", joinMoreKeys("\u2260", "\u2248", "\u221E")),
+ "{", "}")
+ .setKeysOfRow(3,
+ // U+00A9: "©" COPYRIGHT SIGN
+ // U+00AE: "®" REGISTERED SIGN
+ // U+2122: "™" TRADE MARK SIGN
+ // U+2105: "℅" CARE OF
+ "\\", "\u00A9", "\u00AE", "\u2122", "\u2105", "[", "]")
+ .setKeysOfRow(4,
+ ",",
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")),
+ SPACE_KEY,
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ key(">", joinMoreKeys("\u203A", "\u2265", "\u00BB")),
+ // U+2026: "…" HORIZONTAL ELLIPSIS
+ key(".", moreKey("\u2026")))
+ .build();
+
+ public static class RtlSymbolsShifted extends SymbolsShifted {
+ public RtlSymbolsShifted(final LayoutCustomizer customizer) {
+ super(customizer);
+ }
+
+ @Override
+ public ExpectedKey[][] getLayout(final boolean isPhone) {
+ return new ExpectedKeyboardBuilder(super.getLayout(isPhone))
+ .replaceKeyOfLabel("{", key("{", "}"))
+ .replaceKeyOfLabel("}", key("}", "{"))
+ .replaceKeyOfLabel("[", key("[", "]"))
+ .replaceKeyOfLabel("]", key("]", "["))
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .replaceKeyOfLabel("<", key("<", ">",
+ moreKey("\u2039", "\u203A"), moreKey("\u2264", "\u2265"),
+ moreKey("\u00AB", "\u00BB")))
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .replaceKeyOfLabel(">", key(">", "<",
+ moreKey("\u203A", "\u2039"), moreKey("\u2265", "\u2264"),
+ moreKey("\u00BB", "\u00AB")))
+ .build();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java
new file mode 100644
index 000000000..70385c7d0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Tamil keyboard.
+ */
+public final class Tamil extends LayoutBase {
+ private static final String LAYOUT_NAME = "tamil";
+
+ public Tamil(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class TamilCustomizer extends LayoutCustomizer {
+ public TamilCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return TAMIL_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ // U+0BA4: "த" TAMIL LETTER TA
+ // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
+ // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
+ private static final ExpectedKey TAMIL_ALPHABET_KEY = key(
+ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+0BF9: "௹" TAMIL RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u0BF9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0B94: "ஔ" TAMIL LETTER AU
+ key("\u0B94", moreKey("1")),
+ // U+0B90: "ஐ" TAMIL LETTER AI
+ key("\u0B90", moreKey("2")),
+ // U+0B86: "ஆ" TAMIL LETTER AA
+ key("\u0B86", moreKey("3")),
+ // U+0B88: "ஈ" TAMIL LETTER II
+ key("\u0B88", moreKey("4")),
+ // U+0B8A: "ஊ" TAMIL LETTER UU
+ key("\u0B8A", moreKey("5")),
+ // U+0BAE: "ம" TAMIL LETTER MA
+ key("\u0BAE", moreKey("6")),
+ // U+0BA9: "ன" TAMIL LETTER NNNA
+ key("\u0BA9", moreKey("7")),
+ // U+0BA8: "ந" TAMIL LETTER NA
+ key("\u0BA8", moreKey("8")),
+ // U+0B99: "ங" TAMIL LETTER NGA
+ key("\u0B99", moreKey("9")),
+ // U+0BA3: "ண" TAMIL LETTER NNA
+ key("\u0BA3", moreKey("0")),
+ // U+0B9E: "ஞ" TAMIL LETTER NYA
+ "\u0B9E")
+ .setKeysOfRow(2,
+ // U+0B93: "ஓ" TAMIL LETTER OO
+ // U+0BD0: "ௐ" TAMIL OM
+ key("\u0B93", moreKey("\u0BD0")),
+ // U+0B8F: "ஏ" TAMIL LETTER EE
+ "\u0B8F",
+ // U+0B85: "அ" TAMIL LETTER A
+ // U+0B83: "ஃ" TAMIL SIGN VISARGA
+ key("\u0B85", moreKey("\u0B83")),
+ // U+0B87: "இ" TAMIL LETTER I
+ // U+0B89: "உ" TAMIL LETTER U
+ // U+0BB1: "ற" TAMIL LETTER RRA
+ // U+0BAA: "ப" TAMIL LETTER PA
+ "\u0B87", "\u0B89", "\u0BB1", "\u0BAA",
+ // U+0B95: "க" TAMIL LETTER KA
+ // U+0BB9: "ஹ" TAMIL LETTER HA
+ // U+0B95/U+0BCD/U+0BB7:
+ // "க்ஷ" TAMIL LETTER KA/TAMIL SIGN VIRAMA/TAMIL LETTER SSA
+ key("\u0B95", joinMoreKeys("\u0BB9", "\u0B95\u0BCD\u0BB7")),
+ // U+0BA4: "த" TAMIL LETTER TA
+ "\u0BA4",
+ // U+0B9A: "ச" TAMIL LETTER CA
+ // U+0BB8: "ஸ" TAMIL LETTER SA
+ // U+0BB6/U+0BCD/U+0BB0/U+0BC0:
+ // "ஶ்ரீ" TAMIL LETTER SHA/TAMIL SIGN VIRAMA/TAMIL LETTER RA
+ // /TAMIL VOWEL SIGN II
+ key("\u0B9A", joinMoreKeys("\u0BB8", "\u0BB6\u0BCD\u0BB0\u0BC0")),
+ // U+0B9F: "ட" TAMIL LETTER TTA
+ "\u0B9F")
+ .setKeysOfRow(3,
+ // U+0B92: "ஒ" TAMIL LETTER O
+ // U+0B8E: "எ" TAMIL LETTER E
+ // U+0BCD: "்" TAMIL SIGN VIRAMA
+ // U+0BB0: "ர" TAMIL LETTER RA
+ // U+0BB5: "வ" TAMIL LETTER VA
+ // U+0BB4: "ழ TAMIL LETTER LLLA
+ // U+0BB2: "ல" TAMIL LETTER LA
+ // U+0BB3: "ள" TAMIL LETTER LLA
+ // U+0BAF: "ய" TAMIL LETTER YA
+ "\u0B92", "\u0B8E", "\u0BCD", "\u0BB0", "\u0BB5", "\u0BB4", "\u0BB2", "\u0BB3",
+ "\u0BAF",
+ // U+0BB7: "ஷ" TAMIL LETTER SSA
+ // U+0B9C: "ஜ" TAMIL LETTER JA
+ key("\u0BB7", moreKey("\u0B9C")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java
new file mode 100644
index 000000000..cc8224c54
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Telugu keyboard.
+ */
+public final class Telugu extends LayoutBase {
+ private static final String LAYOUT_NAME = "telugu";
+
+ public Telugu(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class TeluguCustomizer extends LayoutCustomizer {
+ public TeluguCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return TELUGU_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+ return EMPTY_KEYS;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+ return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+ }
+
+ @Override
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+ }
+
+ // U+0C05: "అ" TELUGU LETTER A
+ // U+0C06: "ఆ" TELUGU LETTER AA
+ // U+0C07: "ఇ" TELUGU LETTER I
+ private static final ExpectedKey TELUGU_ALPHABET_KEY = key(
+ "\u0C05\u0C06\u0C07", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+ return null;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0C4C: "ౌ" TELUGU VOWEL SIGN AU
+ // U+0C14: "ఔ" TELUGU LETTER AU
+ key("\u0C4C", joinMoreKeys("\u0C14", "1")),
+ // U+0C48: "ై" TELUGU VOWEL SIGN AI
+ // U+0C10: "ఐ" TELUGU LETTER AI
+ key("\u0C48", joinMoreKeys("\u0C10", "2")),
+ // U+0C3E: "ా" TELUGU VOWEL SIGN AA
+ // U+0C06: "ఆ" TELUGU LETTER AA
+ key("\u0C3E", joinMoreKeys("\u0C06", "3")),
+ // U+0C40: "ీ" TELUGU VOWEL SIGN II
+ // U+0C08: "ఈ" TELUGU LETTER II
+ key("\u0C40", joinMoreKeys("\u0C08", "4")),
+ // U+0C42: "ూ" TELUGU VOWEL SIGN UU
+ // U+0C0A: "ఊ" TELUGU LETTER UU
+ key("\u0C42", joinMoreKeys("\u0C0A", "5")),
+ // U+0C2C: "బ" TELUGU LETTER BA
+ // U+0C2D: "భ" TELUGU LETTER BHA
+ key("\u0C2C", joinMoreKeys("\u0C2D", "6")),
+ // U+0C39: "హ" TELUGU LETTER HA
+ // U+0C03: "ః" TELUGU SIGN VISARGA
+ key("\u0C39", joinMoreKeys("\u0C03", "7")),
+ // U+0C17: "గ" TELUGU LETTER GA
+ // U+0C18: "ఘ" TELUGU LETTER GHA
+ key("\u0C17", joinMoreKeys("\u0C18", "8")),
+ // U+0C26: "ద" TELUGU LETTER DA
+ // U+0C27: "ధ" TELUGU LETTER DHA
+ key("\u0C26", joinMoreKeys("\u0C27", "9")),
+ // U+0C1C: "జ" TELUGU LETTER JA
+ // U+0C1D: "ఝ" TELUGU LETTER JHA
+ key("\u0C1C", joinMoreKeys("\u0C1D", "0")),
+ // U+0C21: "డ" TELUGU LETTER DDA
+ // U+0C22: "ఢ" TELUGU LETTER DDHA
+ key("\u0C21", moreKey("\u0C22")))
+ .setKeysOfRow(2,
+ // U+0C4B: "ో" TELUGU VOWEL SIGN OO
+ // U+0C13: "ఓ" TELUGU LETTER OO
+ key("\u0C4B", moreKey("\u0C13")),
+ // U+0C47: "ే" TELUGU VOWEL SIGN EE
+ // U+0C0F: "ఏ" TELUGU LETTER EE
+ key("\u0C47", moreKey("\u0C0F")),
+ // U+0C4D: "్" TELUGU SIGN VIRAMA
+ // U+0C05: "అ" TELUGU LETTER A
+ key("\u0C4D", moreKey("\u0C05")),
+ // U+0C3F: "ి" TELUGU VOWEL SIGN I
+ // U+0C07: "ఇ" TELUGU LETTER I
+ key("\u0C3F", moreKey("\u0C07")),
+ // U+0C41: "ు" TELUGU VOWEL SIGN U
+ // U+0C09: "ఉ" TELUGU LETTER U
+ key("\u0C41", moreKey("\u0C09")),
+ // U+0C2A: "ప" TELUGU LETTER PA
+ // U+0C2B: "ఫ" TELUGU LETTER PHA
+ key("\u0C2A", moreKey("\u0C2B")),
+ // U+0C30: "ర" TELUGU LETTER RA
+ // U+0C31: "ఱ" TELUGU LETTER RRA
+ // U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R
+ key("\u0C30", joinMoreKeys("\u0C31", "\u0C43")),
+ // U+0C15: "క" TELUGU LETTER KA
+ // U+0C16: "ఖ" TELUGU LETTER KHA
+ key("\u0C15", moreKey("\u0C16")),
+ // U+0C24: "త" TELUGU LETTER TA
+ // U+0C25: "థ" TELUGU LETTER THA
+ key("\u0C24", moreKey("\u0C25")),
+ // U+0C1A: "చ" TELUGU LETTER CA
+ // U+0C1B: "ఛ" TELUGU LETTER CHA
+ key("\u0C1A", moreKey("\u0C1B")),
+ // U+0C1F: "ట" TELUGU LETTER TTA
+ // U+0C20: "ఠ" TELUGU LETTER TTHA
+ key("\u0C1F", moreKey("\u0C20")))
+ .setKeysOfRow(3,
+ // U+0C46: "ె" TELUGU VOWEL SIGN E
+ // U+0C12: "ఒ" TELUGU LETTER O
+ key("\u0C46", moreKey("\u0C12")),
+ // U+0C02: "ం" TELUGU SIGN ANUSVARA
+ // U+0C0E: "ఎ" TELUGU LETTER E
+ key("\u0C02", moreKey("\u0C0E")),
+ // U+0C2E: "మ" TELUGU LETTER MA
+ "\u0C2E",
+ // U+0C28: "న" TELUGU LETTER NA
+ // U+0C23: "ణ" TELUGU LETTER NNA
+ // U+0C19: "ఙ" TELUGU LETTER NGA
+ key("\u0C28", joinMoreKeys("\u0C23", "\u0C19")),
+ // U+0C35: "వ" TELUGU LETTER VA
+ "\u0C35",
+ // U+0C32: "ల" TELUGU LETTER LA
+ // U+0C33: "ళ" TELUGU LETTER LLA
+ key("\u0C32", moreKey("\u0C33")),
+ // U+0C38: "స" TELUGU LETTER SA
+ // U+0C36: "శ" TELUGU LETTER SHA
+ key("\u0C38", moreKey("\u0C36")),
+ // U+0C0B: "ఋ" TELUGU LETTER VOCALIC R
+ // U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA
+ key("\u0C0B", moreKey("\u0C4D\u0C30")),
+ // U+0C37: "ష" TELUGU LETTER SSA
+ // U+0C15/U+0C4D/U+0C37:
+ // "క్ష" TELUGU LETTER KA/TELUGU SIGN VIRAMA/TELUGU LETTER SSA
+ key("\u0C37", moreKey("\u0C15\u0C4D\u0C37")),
+ // U+0C2F: "య" TELUGU LETTER YA
+ // U+0C1C/U+0C4D/U+0C1E:
+ // "జ్ఞ" TELUGU LETTER JA/TELUGU SIGN VIRAMA/TELUGU LETTER NYA
+ key("\u0C2F", moreKey("\u0C1C\u0C4D\u0C1E")))
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java
new file mode 100644
index 000000000..af4abea93
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The Thai alphabet keyboard.
+ */
+public final class Thai extends LayoutBase {
+ private static final String LAYOUT_NAME = "thai";
+
+ public Thai(final LayoutCustomizer customizer) {
+ super(customizer, Symbols.class, SymbolsShifted.class);
+ }
+
+ @Override
+ public String getName() { return LAYOUT_NAME; }
+
+ public static class ThaiCustomizer extends LayoutCustomizer {
+ public ThaiCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getAlphabetKey() { return THAI_ALPHABET_KEY; }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_BAHT; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
+
+ // U+0E01: "ก" THAI CHARACTER KO KAI
+ // U+0E02: "ข" THAI CHARACTER KHO KHAI
+ // U+0E04: "ค" THAI CHARACTER KHO KHWAI
+ private static final ExpectedKey THAI_ALPHABET_KEY = key(
+ "\u0E01\u0E02\u0E04", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+
+ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
+ private static final ExpectedKey CURRENCY_BAHT = key("\u0E3F",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+
+ @Override
+ ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+ if (isPhone) {
+ // U+0E03: "ฃ" THAI CHARACTER KHO KHUAT
+ builder.addKeysOnTheRightOfRow(3, "\u0E03");
+ } else {
+ // U+0E03: "ฃ" THAI CHARACTER KHO KHUAT
+ builder.addKeysOnTheRightOfRow(2, "\u0E03")
+ .addKeysOnTheRightOfRow(4, (Object[])EXCLAMATION_AND_QUESTION_MARKS);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone,
+ final int elementId) {
+ if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ return getCommonAlphabetLayout(isPhone);
+ }
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
+ ALPHABET_SHIFTED_COMMON);
+ if (isPhone) {
+ // U+0E05: "ฅ" THAI CHARACTER KHO KHON
+ builder.addKeysOnTheRightOfRow(3, "\u0E05");
+ } else {
+ // U+0E05: "ฅ" THAI CHARACTER KHO KHON
+ builder.addKeysOnTheRightOfRow(2, "\u0E05");
+ }
+ return builder.build();
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ @Override
+ ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder,
+ final boolean isPhone) {
+ final LayoutCustomizer customizer = getCustomizer();
+ builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone));
+ builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone));
+ builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone));
+ if (isPhone) {
+ builder.addKeysOnTheRightOfRow(4, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY));
+ } else {
+ builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(3, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey())
+ .addKeysOnTheRightOfRow(5, EMOJI_KEY);
+ }
+ builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone))
+ .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone));
+ return builder;
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO
+ "\u0E45",
+ // U+0E51: "๑" THAI DIGIT ONE
+ key("/", joinMoreKeys("1", "\u0E51")),
+ // U+0E52: "๒" THAI DIGIT TWO
+ key("_", joinMoreKeys("2", "\u0E52")),
+ // U+0E20: "ภ" THAI CHARACTER PHO SAMPHAO
+ // U+0E53: "๓" THAI DIGIT THREE
+ key("\u0E20", joinMoreKeys("3", "\u0E53")),
+ // U+0E16: "ถ" THAI CHARACTER THO THUNG
+ // U+0E54: "๔" THAI DIGIT FOUR
+ key("\u0E16", joinMoreKeys("4", "\u0E54")),
+ // U+0E38: " ุ" THAI CHARACTER SARA U
+ key(" \u0E38", "\u0E38"),
+ // U+0E36: " ึ" THAI CHARACTER SARA UE
+ key(" \u0E36", "\u0E36"),
+ // U+0E04: "ค" THAI CHARACTER KHO KHWAI
+ // U+0E55: "๕" THAI DIGIT FIVE
+ key("\u0E04", joinMoreKeys("5", "\u0E55")),
+ // U+0E15: "ต" THAI CHARACTER TO TAO
+ // U+0E56: "๖" THAI DIGIT SIX
+ key("\u0E15", joinMoreKeys("6", "\u0E56")),
+ // U+0E08: "จ" THAI CHARACTER CHO CHAN
+ // U+0E57: "๗" THAI DIGIT SEVEN
+ key("\u0E08", joinMoreKeys("7", "\u0E57")),
+ // U+0E02: "ข" THAI CHARACTER KHO KHAI
+ // U+0E58: "๘" THAI DIGIT EIGHT
+ key("\u0E02", joinMoreKeys("8", "\u0E58")),
+ // U+0E0A: "ช" THAI CHARACTER CHO CHANG
+ // U+0E59: "๙" THAI DIGIT NINE
+ key("\u0E0A", joinMoreKeys("9", "\u0E59")))
+ .setKeysOfRow(2,
+ // U+0E46: "ๆ" THAI CHARACTER MAIYAMOK
+ // U+0E50: "๐" THAI DIGIT ZERO
+ key("\u0E46", joinMoreKeys("0", "\u0E50")),
+ // U+0E44: "ไ" THAI CHARACTER SARA AI MAIMALAI
+ // U+0E33: "ำ" THAI CHARACTER SARA AM
+ // U+0E1E: "พ" THAI CHARACTER PHO PHAN
+ // U+0E30: "ะ" THAI CHARACTER SARA A
+ "\u0E44", "\u0E33", "\u0E1E", "\u0E30",
+ // U+0E31: " ั" THAI CHARACTER MAI HAN-AKAT
+ key(" \u0E31", "\u0E31"),
+ // U+0E35: " ี" HAI CHARACTER SARA II
+ key(" \u0E35", "\u0E35"),
+ // U+0E23: "ร" THAI CHARACTER RO RUA
+ // U+0E19: "น" THAI CHARACTER NO NU
+ // U+0E22: "ย" THAI CHARACTER YO YAK
+ // U+0E1A: "บ" THAI CHARACTER BO BAIMAI
+ // U+0E25: "ล" THAI CHARACTER LO LING
+ "\u0E23", "\u0E19", "\u0E22", "\u0E1A", "\u0E25")
+ .setKeysOfRow(3,
+ // U+0E1F: "ฟ" THAI CHARACTER FO FAN
+ // U+0E2B: "ห" THAI CHARACTER HO HIP
+ // U+0E01: "ก" THAI CHARACTER KO KAI
+ // U+0E14: "ด" THAI CHARACTER DO DEK
+ // U+0E40: "เ" THAI CHARACTER SARA E
+ "\u0E1F", "\u0E2B", "\u0E01", "\u0E14", "\u0E40",
+ // U+0E49: " ้" THAI CHARACTER MAI THO
+ key(" \u0E49", "\u0E49"),
+ // U+0E48: " ่" THAI CHARACTER MAI EK
+ key(" \u0E48", "\u0E48"),
+ // U+0E32: "า" THAI CHARACTER SARA AA
+ // U+0E2A: "ส" THAI CHARACTER SO SUA
+ // U+0E27: "ว" THAI CHARACTER WO WAEN
+ // U+0E07: "ง" THAI CHARACTER NGO NGU
+ "\u0E32", "\u0E2A", "\u0E27", "\u0E07")
+ .setKeysOfRow(4,
+ // U+0E1C: "ผ" THAI CHARACTER PHO PHUNG
+ // U+0E1B: "ป" THAI CHARACTER PO PLA
+ // U+0E41: "แ" THAI CHARACTER SARA AE
+ // U+0E2D: "อ" THAI CHARACTER O ANG
+ "\u0E1C", "\u0E1B", "\u0E41", "\u0E2D",
+ // U+0E34: " ิ" THAI CHARACTER SARA I
+ key(" \u0E34", "\u0E34"),
+ // U+0E37: " ื" THAI CHARACTER SARA UEE
+ key(" \u0E37", "\u0E37"),
+ // U+0E17: "ท" THAI CHARACTER THO THAHAN
+ // U+0E21: "ม" THAI CHARACTER MO MA
+ // U+0E43: "ใ" THAI CHARACTER SARA AI MAIMUAN
+ // U+0E1D: "ฝ" THAI CHARACTER FO FA
+ "\u0E17", "\u0E21", "\u0E43", "\u0E1D")
+ .build();
+
+ private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+ .setKeysOfRow(1,
+ // U+0E51: "๑" THAI DIGIT ONE
+ // U+0E52: "๒" THAI DIGIT TWO
+ // U+0E53: "๓" THAI DIGIT THREE
+ // U+0E54: "๔" THAI DIGIT FOUR
+ // U+0E39: " ู" THAI CHARACTER SARA UU
+ "+", "\u0E51", "\u0E52", "\u0E53", "\u0E54",
+ key(" \u0E39", "\u0E39"),
+ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
+ // U+0E55: "๕" THAI DIGIT FIVE
+ // U+0E56: "๖" THAI DIGIT SIX
+ // U+0E57: "๗" THAI DIGIT SEVEN
+ // U+0E58: "๘" THAI DIGIT EIGHT
+ // U+0E59: "๙" THAI DIGIT NINE
+ "\u0E3F", "\u0E55", "\u0E56", "\u0E57", "\u0E58", "\u0E59")
+ .setKeysOfRow(2,
+ // U+0E50: "๐" THAI DIGIT ZERO
+ // U+0E0E: "ฎ" THAI CHARACTER DO CHADA
+ // U+0E11: "ฑ" THAI CHARACTER THO NANGMONTHO
+ // U+0E18: "ธ" THAI CHARACTER THO THONG
+ "\u0E50", "\"", "\u0E0E", "\u0E11", "\u0E18",
+ // U+0E4D: " ํ" THAI CHARACTER THANTHAKHAT
+ key(" \u0E4D", "\u0E4D"),
+ // U+0E4A: " ๊" THAI CHARACTER MAI TRI
+ key(" \u0E4A", "\u0E4A"),
+ // U+0E13: "ณ" THAI CHARACTER NO NEN
+ // U+0E2F: "ฯ" THAI CHARACTER PAIYANNOI
+ // U+0E0D: "ญ" THAI CHARACTER YO YING
+ // U+0E10: "ฐ" THAI CHARACTER THO THAN
+ "\u0E13", "\u0E2F", "\u0E0D", "\u0E10", ",")
+ .setKeysOfRow(3,
+ // U+0E24: "ฤ" THAI CHARACTER RU
+ // U+0E06: "ฆ" THAI CHARACTER KHO RAKHANG
+ // U+0E0F: "ฏ" THAI CHARACTER TO PATAK
+ // U+0E42: "โ" THAI CHARACTER SARA O
+ // U+0E0C: "ฌ" THAI CHARACTER CHO CHOE
+ "\u0E24", "\u0E06", "\u0E0F", "\u0E42", "\u0E0C",
+ // U+0E47: " ็" THAI CHARACTER MAITAIKHU
+ key(" \u0E47", "\u0E47"),
+ // U+0E4B: " ๋" THAI CHARACTER MAI CHATTAWA
+ key(" \u0E4B", "\u0E4B"),
+ // U+0E29: "ษ" THAI CHARACTER SO RUSI
+ // U+0E28: "ศ" THAI CHARACTER SO SALA
+ // U+0E0B: "ซ" THAI CHARACTER SO SO
+ "\u0E29", "\u0E28", "\u0E0B", ".")
+ .setKeysOfRow(4,
+ // U+0E09: "ฉ" THAI CHARACTER CHO CHING
+ // U+0E2E: "ฮ" THAI CHARACTER HO NOKHUK
+ "(", ")", "\u0E09", "\u0E2E",
+ // U+0E3A: " ฺ" THAI CHARACTER PHINTHU
+ key(" \u0E3A", "\u0E3A"),
+ // U+0E4C: " ์" THAI CHARACTER THANTHAKHAT
+ key(" \u0E4C", "\u0E4C"),
+ // U+0E12: "ฒ" THAI CHARACTER THO PHUTHAO
+ // U+0E2C: "ฬ" THAI CHARACTER LO CHULA
+ // U+0E26: "ฦ" THAI CHARACTER LU
+ "?", "\u0E12", "\u0E2C", "\u0E26")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java
new file mode 100644
index 000000000..6e721047c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import java.util.Arrays;
+
+/**
+ * This class builds a keyboard that is a two dimensional array of elements <code>E</code>.
+ *
+ * A keyboard consists of an array of rows, and a row consists of an array of elements. Each row
+ * may have different number of elements. A element of a keyboard can be specified by a row number
+ * and a column number, both numbers starts from 1.
+ *
+ * @param <E> the type of a keyboard element. A keyboard element must be an immutable object.
+ */
+abstract class AbstractKeyboardBuilder<E> {
+ // A building array of rows.
+ private E[][] mRows;
+
+ // Returns an instance of default element.
+ abstract E defaultElement();
+ // Returns an <code>E</code> array instance of the <code>size</code>.
+ abstract E[] newArray(final int size);
+ // Returns an <code>E[]</code> array instance of the <code>size</code>.
+ abstract E[][] newArrayOfArray(final int size);
+
+ /**
+ * Construct an empty builder.
+ */
+ AbstractKeyboardBuilder() {
+ mRows = newArrayOfArray(0);
+ }
+
+ /**
+ * Construct a builder from template keyboard. This builder has the same dimensions and
+ * elements of <code>rows</rows>.
+ * @param rows the template keyboard rows. The elements of the <code>rows</code> will be
+ * shared with this builder. Therefore a element must be an immutable object.
+ */
+ AbstractKeyboardBuilder(final E[][] rows) {
+ mRows = newArrayOfArray(rows.length);
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ final E[] row = rows[rowIndex];
+ mRows[rowIndex] = Arrays.copyOf(row, row.length);
+ }
+ }
+
+ /**
+ * Return current constructing keyboard.
+ * @return the array of the array of the element being constructed.
+ */
+ E[][] build() {
+ return mRows;
+ }
+
+ /**
+ * Return the number of rows.
+ * @return the number of rows being constructed.
+ */
+ int getRowCount() {
+ return mRows.length;
+ }
+
+ /**
+ * Get the current contents of the specified row.
+ * @param row the row number to get the contents.
+ * @return the array of elements at row number <code>row</code>.
+ * @throws RuntimeException if <code>row</code> is illegal.
+ */
+ E[] getRowAt(final int row) {
+ final int rowIndex = row - 1;
+ if (rowIndex < 0 || rowIndex >= mRows.length) {
+ throw new RuntimeException("Illegal row number: " + row);
+ }
+ return mRows[rowIndex];
+ }
+
+ /**
+ * Set an array of elements to the specified row.
+ * @param row the row number to set <code>elements</code>.
+ * @param elements the array of elements to set at row number <code>row</code>.
+ * @throws RuntimeException if <code>row</code> is illegal.
+ */
+ void setRowAt(final int row, final E[] elements) {
+ final int rowIndex = row - 1;
+ if (rowIndex < 0) {
+ throw new RuntimeException("Illegal row number: " + row);
+ }
+ final E[][] newRows = (rowIndex < mRows.length) ? mRows
+ : Arrays.copyOf(mRows, rowIndex + 1);
+ newRows[rowIndex] = elements;
+ mRows = newRows;
+ }
+
+ /**
+ * Set or insert an element at specified position.
+ * @param row the row number to set or insert the <code>element</code>.
+ * @param column the column number to set or insert the <code>element</code>.
+ * @param element the element to set or insert at <code>row,column</code>.
+ * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>.
+ * Otherwise the <code>element</code> replace the element at <code>row,column</code>.
+ * @throws RuntimeException if <code>row</code> or <code>column</code> is illegal.
+ */
+ void setElementAt(final int row, final int column, final E element, final boolean insert) {
+ final E[] elements = getRowAt(row);
+ final int columnIndex = column - 1;
+ if (columnIndex < 0) {
+ throw new RuntimeException("Illegal column number: " + column);
+ }
+ if (insert) {
+ if (columnIndex >= elements.length + 1) {
+ throw new RuntimeException("Illegal column number: " + column);
+ }
+ final E[] newElements = Arrays.copyOf(elements, elements.length + 1);
+ // Shift the remaining elements.
+ System.arraycopy(newElements, columnIndex, newElements, columnIndex + 1,
+ elements.length - columnIndex);
+ // Insert the element at <code>row,column</code>.
+ newElements[columnIndex] = element;
+ // Replace the current row with one.
+ setRowAt(row, newElements);
+ return;
+ }
+ final E[] newElements = (columnIndex < elements.length) ? elements
+ : Arrays.copyOf(elements, columnIndex + 1);
+ newElements[columnIndex] = element;
+ setRowAt(row, newElements);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
new file mode 100644
index 000000000..9e0039d84
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * Base class to create an expected keyboard for unit test.
+ */
+public abstract class AbstractLayoutBase {
+ // Those helper methods have a lower case name to be readable when defining expected keyboard
+ // layouts.
+
+ // Helper method to create an {@link ExpectedKey} object that has the label.
+ public static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has the label and the output text.
+ public static ExpectedKey key(final String label, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, outputText, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has the label and the output code.
+ public static ExpectedKey key(final String label, final int code,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, code, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has the icon and the output text.
+ public static ExpectedKey key(final int iconId, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(iconId, outputText, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has the icon and the output code.
+ public static ExpectedKey key(final int iconId, final int code,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(iconId, code, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has new "more keys".
+ public static ExpectedKey key(final ExpectedKey key, final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(key.getVisual(), key.getOutput(), moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedAdditionalMoreKey} object for an
+ // "additional more key" that has the label.
+ // The additional more keys can be defined independently from other more keys. The position of
+ // the additional more keys in the long press popup keyboard can be controlled by specifying
+ // special marker "%" in the usual more keys definitions.
+ public static ExpectedAdditionalMoreKey additionalMoreKey(final String label) {
+ return ExpectedAdditionalMoreKey.newInstance(label);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the label.
+ public static ExpectedKey moreKey(final String label) {
+ return ExpectedKey.newInstance(label);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the label
+ // and the output text.
+ public static ExpectedKey moreKey(final String label, final String outputText) {
+ return ExpectedKey.newInstance(label, outputText);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the label
+ // and the output code.
+ public static ExpectedKey moreKey(final String label, final int code) {
+ return ExpectedKey.newInstance(label, code);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the icon
+ // and the output text.
+ public static ExpectedKey moreKey(final int iconId, final String outputText) {
+ return ExpectedKey.newInstance(iconId, outputText);
+ }
+
+ // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey},
+ // {@link ExpectedKey} array, and {@link String}.
+ public static ExpectedKey[] joinMoreKeys(final Object ... moreKeys) {
+ return joinKeys(moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey},
+ // {@link ExpectedKey} array, and {@link String}.
+ public static ExpectedKey[] joinKeys(final Object ... keys) {
+ return ExpectedKeyboardBuilder.joinKeys(keys);
+ }
+
+ // Icon ids.
+ private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_DELETE_KEY);
+ private static final int ICON_SPACE = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_SPACE_KEY);
+ private static final int ICON_TAB = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_TAB_KEY);
+ private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_SHORTCUT_KEY);
+ private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_SETTINGS_KEY);
+ private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY);
+ private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_ENTER_KEY);
+ private static final int ICON_EMOJI = KeyboardIconsSet.getIconId(
+ KeyboardIconsSet.NAME_EMOJI_KEY);
+
+ // Functional keys.
+ public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
+ public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB);
+ public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
+ public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
+ public static final ExpectedKey LANGUAGE_SWITCH_KEY = key(
+ ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH);
+ public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
+ public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI);
+ public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java
new file mode 100644
index 000000000..56149189f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class builds an actual keyboard for unit test.
+ *
+ * An actual keyboard is an array of rows, and a row consists of an array of {@link Key}s.
+ * Each row may have different number of {@link Key}s.
+ */
+public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
+ private static ArrayList<Key> filterOutSpacer(final List<Key> keys) {
+ final ArrayList<Key> filteredKeys = new ArrayList<>();
+ for (final Key key : keys) {
+ if (key.isSpacer()) {
+ continue;
+ }
+ filteredKeys.add(key);
+ }
+ return filteredKeys;
+ }
+
+ /**
+ * Create the keyboard that consists of the array of rows of the actual keyboard's keys.
+ * @param sortedKeys keys list of the actual keyboard that is sorted from top-left to
+ * bottom-right.
+ * @return the actual keyboard grouped with rows.
+ */
+ public static Key[][] buildKeyboard(final List<Key> sortedKeys) {
+ // Filter out spacer to prepare to create rows.
+ final ArrayList<Key> filteredSortedKeys = filterOutSpacer(sortedKeys);
+
+ // Grouping keys into rows.
+ final ArrayList<ArrayList<Key>> rows = new ArrayList<>();
+ ArrayList<Key> elements = new ArrayList<>();
+ int lastY = filteredSortedKeys.get(0).getY();
+ for (final Key key : filteredSortedKeys) {
+ if (lastY != key.getY()) {
+ // A new row is starting.
+ lastY = key.getY();
+ rows.add(elements);
+ elements = new ArrayList<>();
+ }
+ elements.add(key);
+ }
+ rows.add(elements); // Add the last row.
+
+ // Calculate each dimension of rows and create a builder.
+ final int[] dimensions = new int[rows.size()];
+ for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
+ dimensions[rowIndex] = rows.get(rowIndex).size();
+ }
+ final ActualKeyboardBuilder builder = new ActualKeyboardBuilder();
+
+ for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
+ final int row = rowIndex + 1;
+ final ArrayList<Key> rowKeys = rows.get(rowIndex);
+ builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()]));
+ }
+ return builder.build();
+ }
+
+ @Override
+ Key defaultElement() { return null; }
+
+ @Override
+ Key[] newArray(final int size) { return new Key[size]; }
+
+ @Override
+ Key[][] newArrayOfArray(final int size) { return new Key[size][]; }
+
+ // Helper class to create concise representation from the key specification.
+ static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> {
+ static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer();
+
+ @Override
+ public String stringize(final MoreKeySpec spec) {
+ return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
+ }
+
+ static String toString(final String label, final int iconId, final String outputText,
+ final int code) {
+ final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
+ ? KeyboardIconsSet.getIconName(iconId) : label;
+ final String output;
+ if (code == Constants.CODE_OUTPUT_TEXT) {
+ output = outputText;
+ } else if (code < Constants.CODE_SPACE) {
+ output = Constants.printableCode(code);
+ } else {
+ output = StringUtils.newSingleCodePointString(code);
+ }
+ if (visual.equals(output)) {
+ return visual;
+ }
+ return visual + "|" + output;
+ }
+ }
+
+ // Helper class to create concise representation from the key.
+ static class KeyStringizer extends StringUtils.Stringizer<Key> {
+ static final KeyStringizer STRINGIZER = new KeyStringizer();
+
+ @Override
+ public String stringize(final Key key) {
+ if (key == null) {
+ return "NULL";
+ }
+ if (key.isSpacer()) {
+ return "SPACER";
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append(MoreKeySpecStringizer.toString(
+ key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode()));
+ final MoreKeySpec[] moreKeys = key.getMoreKeys();
+ if (moreKeys == null) {
+ return sb.toString();
+ }
+ sb.append("^");
+ sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys));
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Convert the key to human readable string.
+ * @param key the key to be converted to string.
+ * @return the human readable representation of <code>key</code>.
+ */
+ public static String toString(final Key key) {
+ return KeyStringizer.STRINGIZER.stringize(key);
+ }
+
+ /**
+ * Convert the keyboard row to human readable string.
+ * @param keys the keyboard row to be converted to string.
+ * @return the human readable representation of <code>keys</code>.
+ */
+ public static String toString(final Key[] keys) {
+ return KeyStringizer.STRINGIZER.join(keys);
+ }
+
+ // Helper class to create concise representation from the array of the key.
+ static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> {
+ static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
+
+ @Override
+ public String stringize(final Key[] keyArray) {
+ return KeyStringizer.STRINGIZER.join(keyArray);
+ }
+ }
+
+ /**
+ * Convert the keyboard to human readable string.
+ * @param rows the keyboard to be converted to string.
+ * @return the human readable representation of <code>rows</code>.
+ */
+ public static String toString(final Key[][] rows) {
+ return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java
new file mode 100644
index 000000000..0e1c71cd1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * This class represents an expected key.
+ */
+public class ExpectedKey {
+ static ExpectedKey EMPTY_KEY = newInstance("");
+
+ // A key that has a string label and may have "more keys".
+ static ExpectedKey newInstance(final String label, final ExpectedKey... moreKeys) {
+ return newInstance(label, label, moreKeys);
+ }
+
+ // A key that has a string label and a different output text and may have "more keys".
+ static ExpectedKey newInstance(final String label, final String outputText,
+ final ExpectedKey... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(label),
+ ExpectedKeyOutput.newInstance(outputText), moreKeys);
+ }
+
+ // A key that has a string label and a code point output and may have "more keys".
+ static ExpectedKey newInstance(final String label, final int code,
+ final ExpectedKey... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(label),
+ ExpectedKeyOutput.newInstance(code), moreKeys);
+ }
+
+ // A key that has an icon and an output text and may have "more keys".
+ static ExpectedKey newInstance(final int iconId, final String outputText,
+ final ExpectedKey... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(iconId),
+ ExpectedKeyOutput.newInstance(outputText), moreKeys);
+ }
+
+ // A key that has an icon and a code point output and may have "more keys".
+ static ExpectedKey newInstance(final int iconId, final int code,
+ final ExpectedKey... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(iconId),
+ ExpectedKeyOutput.newInstance(code), moreKeys);
+ }
+
+ static ExpectedKey newInstance(final ExpectedKeyVisual visual, final ExpectedKeyOutput output,
+ final ExpectedKey... moreKeys) {
+ if (moreKeys.length == 0) {
+ return new ExpectedKey(visual, output);
+ }
+ // The more keys are the extra keys that the main keyboard key may have in its long press
+ // popup keyboard.
+ // The additional more keys can be defined independently from other more keys.
+ // The position of the additional more keys in the long press popup keyboard can be
+ // controlled by specifying special marker "%" in the usual more keys definitions.
+ final ArrayList<ExpectedKey> moreKeysList = new ArrayList<>();
+ final ArrayList<ExpectedAdditionalMoreKey> additionalMoreKeys = new ArrayList<>();
+ int firstAdditionalMoreKeyIndex = -1;
+ for (int index = 0; index < moreKeys.length; index++) {
+ final ExpectedKey moreKey = moreKeys[index];
+ if (moreKey instanceof ExpectedAdditionalMoreKey) {
+ additionalMoreKeys.add((ExpectedAdditionalMoreKey) moreKey);
+ if (firstAdditionalMoreKeyIndex < 0) {
+ firstAdditionalMoreKeyIndex = index;
+ }
+ } else {
+ moreKeysList.add(moreKey);
+ }
+ }
+ if (additionalMoreKeys.isEmpty()) {
+ return new ExpectedKeyWithMoreKeys(visual, output, moreKeys);
+ }
+ final ExpectedKey[] moreKeysArray = moreKeysList.toArray(
+ new ExpectedKey[moreKeysList.size()]);
+ final ExpectedAdditionalMoreKey[] additionalMoreKeysArray = additionalMoreKeys.toArray(
+ new ExpectedAdditionalMoreKey[additionalMoreKeys.size()]);
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ visual, output, moreKeysArray, firstAdditionalMoreKeyIndex,
+ additionalMoreKeysArray);
+ }
+
+ private static final ExpectedKey[] EMPTY_KEYS = new ExpectedKey[0];
+
+ // The expected visual outlook of this key.
+ private final ExpectedKeyVisual mVisual;
+ // The expected output of this key.
+ private final ExpectedKeyOutput mOutput;
+
+ public final ExpectedKeyVisual getVisual() {
+ return mVisual;
+ }
+
+ public final ExpectedKeyOutput getOutput() {
+ return mOutput;
+ }
+
+ public ExpectedKey[] getMoreKeys() {
+ // This key has no "more keys".
+ return EMPTY_KEYS;
+ }
+
+ public ExpectedKey setMoreKeys(final ExpectedKey... moreKeys) {
+ return newInstance(mVisual, mOutput, moreKeys);
+ }
+
+ public ExpectedKey setAdditionalMoreKeys(
+ final ExpectedAdditionalMoreKey... additionalMoreKeys) {
+ if (additionalMoreKeys.length == 0) {
+ return this;
+ }
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ mVisual, mOutput, EMPTY_KEYS, 0 /* additionalMoreKeysIndex */, additionalMoreKeys);
+ }
+
+ public ExpectedKey setAdditionalMoreKeysIndex(final int additionalMoreKeysIndex) {
+ if (additionalMoreKeysIndex == 0) {
+ return this;
+ }
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ mVisual, mOutput, EMPTY_KEYS, additionalMoreKeysIndex);
+ }
+
+ protected ExpectedKey(final ExpectedKeyVisual visual, final ExpectedKeyOutput output) {
+ mVisual = visual;
+ mOutput = output;
+ }
+
+ public ExpectedKey toUpperCase(Locale locale) {
+ return newInstance(mVisual.toUpperCase(locale), mOutput.toUpperCase(locale));
+ }
+
+ public boolean equalsTo(final Key key) {
+ // This key has no "more keys".
+ return mVisual.equalsTo(key) && mOutput.equalsTo(key) && key.getMoreKeys() == null;
+ }
+
+ public boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mVisual.equalsTo(moreKeySpec) && mOutput.equalsTo(moreKeySpec);
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object instanceof ExpectedKey) {
+ final ExpectedKey key = (ExpectedKey) object;
+ return mVisual.equalsTo(key.mVisual) && mOutput.equalsTo(key.mOutput)
+ && Arrays.equals(getMoreKeys(), key.getMoreKeys());
+ }
+ return false;
+ }
+
+ private static int hashCode(final Object... objects) {
+ return Arrays.hashCode(objects);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode(mVisual, mOutput, getMoreKeys());
+ }
+
+ @Override
+ public String toString() {
+ if (mVisual.equalsTo(mOutput)) {
+ return mVisual.toString();
+ }
+ return mVisual + "|" + mOutput;
+ }
+
+ /**
+ * This class represents an expected "additional more key".
+ *
+ * The additional more keys can be defined independently from other more keys. The position of
+ * the additional more keys in the long press popup keyboard can be controlled by specifying
+ * special marker "%" in the usual more keys definitions.
+ */
+ public static class ExpectedAdditionalMoreKey extends ExpectedKey {
+ public static ExpectedAdditionalMoreKey newInstance(final String label) {
+ return new ExpectedAdditionalMoreKey(ExpectedKeyVisual.newInstance(label),
+ ExpectedKeyOutput.newInstance(label));
+ }
+
+ public static ExpectedAdditionalMoreKey newInstance(final ExpectedKey key) {
+ return new ExpectedAdditionalMoreKey(key.getVisual(), key.getOutput());
+ }
+
+ ExpectedAdditionalMoreKey(final ExpectedKeyVisual visual, final ExpectedKeyOutput output) {
+ super(visual, output);
+ }
+
+ @Override
+ public ExpectedAdditionalMoreKey toUpperCase(final Locale locale) {
+ final ExpectedKey upperCaseKey = super.toUpperCase(locale);
+ return new ExpectedAdditionalMoreKey(
+ upperCaseKey.getVisual(), upperCaseKey.getOutput());
+ }
+ }
+
+ /**
+ * This class represents an expected key that has "more keys".
+ */
+ private static class ExpectedKeyWithMoreKeys extends ExpectedKey {
+ private final ExpectedKey[] mMoreKeys;
+
+ ExpectedKeyWithMoreKeys(final ExpectedKeyVisual visual, final ExpectedKeyOutput output,
+ final ExpectedKey... moreKeys) {
+ super(visual, output);
+ mMoreKeys = moreKeys;
+ }
+
+ @Override
+ public ExpectedKey toUpperCase(final Locale locale) {
+ final ExpectedKey[] upperCaseMoreKeys = new ExpectedKey[mMoreKeys.length];
+ for (int i = 0; i < mMoreKeys.length; i++) {
+ upperCaseMoreKeys[i] = mMoreKeys[i].toUpperCase(locale);
+ }
+ return newInstance(getVisual().toUpperCase(locale), getOutput().toUpperCase(locale),
+ upperCaseMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey[] getMoreKeys() {
+ return mMoreKeys;
+ }
+
+ @Override
+ public ExpectedKey setAdditionalMoreKeys(
+ final ExpectedAdditionalMoreKey... additionalMoreKeys) {
+ if (additionalMoreKeys.length == 0) {
+ return this;
+ }
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual(), getOutput(), mMoreKeys, 0 /* additionalMoreKeysIndex */,
+ additionalMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey setAdditionalMoreKeysIndex(final int additionalMoreKeysIndex) {
+ if (additionalMoreKeysIndex == 0) {
+ return this;
+ }
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual(), getOutput(), mMoreKeys, additionalMoreKeysIndex);
+ }
+
+ @Override
+ public boolean equalsTo(final Key key) {
+ if (getVisual().equalsTo(key) && getOutput().equalsTo(key)) {
+ final MoreKeySpec[] moreKeySpecs = key.getMoreKeys();
+ final ExpectedKey[] moreKeys = getMoreKeys();
+ // This key should have at least one "more key".
+ if (moreKeySpecs == null || moreKeySpecs.length != moreKeys.length) {
+ return false;
+ }
+ for (int index = 0; index < moreKeySpecs.length; index++) {
+ if (!moreKeys[index].equalsTo(moreKeySpecs[index])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ // MoreKeySpec has no "more keys".
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "^" + Arrays.toString(getMoreKeys());
+ }
+ }
+
+ /**
+ * This class represents an expected key that has "more keys" and "additional more keys".
+ */
+ private static final class ExpectedKeyWithMoreKeysAndAdditionalMoreKeys
+ extends ExpectedKeyWithMoreKeys {
+ private final ExpectedAdditionalMoreKey[] mAdditionalMoreKeys;
+ private final int mAdditionalMoreKeysIndex;
+
+ ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(final ExpectedKeyVisual visual,
+ final ExpectedKeyOutput output, final ExpectedKey[] moreKeys,
+ final int additionalMoreKeysIndex,
+ final ExpectedAdditionalMoreKey... additionalMoreKeys) {
+ super(visual, output, moreKeys);
+ mAdditionalMoreKeysIndex = additionalMoreKeysIndex;
+ mAdditionalMoreKeys = additionalMoreKeys;
+ }
+
+ @Override
+ public ExpectedKey setMoreKeys(final ExpectedKey... moreKeys) {
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual(), getOutput(), moreKeys, mAdditionalMoreKeysIndex,
+ mAdditionalMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey setAdditionalMoreKeys(
+ final ExpectedAdditionalMoreKey... additionalMoreKeys) {
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual(), getOutput(), super.getMoreKeys(), mAdditionalMoreKeysIndex,
+ additionalMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey setAdditionalMoreKeysIndex(final int additionalMoreKeysIndex) {
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual(), getOutput(), super.getMoreKeys(), additionalMoreKeysIndex,
+ mAdditionalMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey toUpperCase(final Locale locale) {
+ final ExpectedKey[] moreKeys = super.getMoreKeys();
+ final ExpectedKey[] upperCaseMoreKeys = new ExpectedKey[moreKeys.length];
+ for (int i = 0; i < moreKeys.length; i++) {
+ upperCaseMoreKeys[i] = moreKeys[i].toUpperCase(locale);
+ }
+ final ExpectedAdditionalMoreKey[] upperCaseAdditionalMoreKeys =
+ new ExpectedAdditionalMoreKey[mAdditionalMoreKeys.length];
+ for (int i = 0; i < mAdditionalMoreKeys.length; i++) {
+ upperCaseAdditionalMoreKeys[i] = mAdditionalMoreKeys[i].toUpperCase(locale);
+ }
+ return new ExpectedKeyWithMoreKeysAndAdditionalMoreKeys(
+ getVisual().toUpperCase(locale), getOutput().toUpperCase(locale),
+ upperCaseMoreKeys, mAdditionalMoreKeysIndex, upperCaseAdditionalMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey[] getMoreKeys() {
+ final ExpectedKey[] moreKeys = super.getMoreKeys();
+ final ExpectedKey[] edittedMoreKeys = Arrays.copyOf(
+ moreKeys, moreKeys.length + mAdditionalMoreKeys.length);
+ System.arraycopy(edittedMoreKeys, mAdditionalMoreKeysIndex,
+ edittedMoreKeys, mAdditionalMoreKeysIndex + mAdditionalMoreKeys.length,
+ moreKeys.length - mAdditionalMoreKeysIndex);
+ System.arraycopy(mAdditionalMoreKeys, 0, edittedMoreKeys, mAdditionalMoreKeysIndex,
+ mAdditionalMoreKeys.length);
+ return edittedMoreKeys;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
new file mode 100644
index 000000000..1be51e60b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.Locale;
+
+/**
+ * This class represents an expected output of a key.
+ *
+ * There are two types of expected output, an integer code point and a string output text.
+ */
+abstract class ExpectedKeyOutput {
+ static ExpectedKeyOutput newInstance(final int code) {
+ return new Code(code);
+ }
+
+ static ExpectedKeyOutput newInstance(final String outputText) {
+ // If the <code>outputText</code> is one code point string, use {@link CodePoint} object.
+ if (StringUtils.codePointCount(outputText) == 1) {
+ return new Code(outputText.codePointAt(0));
+ }
+ return new Text(outputText);
+ }
+
+ abstract ExpectedKeyOutput toUpperCase(final Locale locale);
+ abstract boolean equalsTo(final String text);
+ abstract boolean equalsTo(final Key key);
+ abstract boolean equalsTo(final MoreKeySpec moreKeySpec);
+ abstract boolean equalsTo(final ExpectedKeyOutput output);
+
+ /**
+ * This class represents an integer code point.
+ */
+ private static class Code extends ExpectedKeyOutput {
+ // UNICODE code point or a special negative value defined in {@link Constants}.
+ private final int mCode;
+
+ Code(final int code) { mCode = code; }
+
+ @Override
+ ExpectedKeyOutput toUpperCase(final Locale locale) {
+ if (Constants.isLetterCode(mCode)) {
+ final String codeString = StringUtils.newSingleCodePointString(mCode);
+ // A letter may have an upper case counterpart that consists of multiple code
+ // points, for instance the upper case of "ß" is "SS".
+ return newInstance(codeString.toUpperCase(locale));
+ }
+ // A special negative value has no upper case.
+ return this;
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return StringUtils.codePointCount(text) == 1 && text.codePointAt(0) == mCode;
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mCode == key.getCode();
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mCode == moreKeySpec.mCode;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return (output instanceof Code) && mCode == ((Code)output).mCode;
+ }
+
+ @Override
+ public String toString() {
+ return Constants.isLetterCode(mCode) ? StringUtils.newSingleCodePointString(mCode)
+ : Constants.printableCode(mCode);
+ }
+ }
+
+ /**
+ * This class represents a string output text.
+ */
+ private static class Text extends ExpectedKeyOutput {
+ private final String mText;
+
+ Text(final String text) { mText = text; }
+
+ @Override
+ ExpectedKeyOutput toUpperCase(final Locale locale) {
+ return newInstance(mText.toUpperCase(locale));
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return text.equals(text);
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return key.getCode() == Constants.CODE_OUTPUT_TEXT
+ && mText.equals(key.getOutputText());
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return moreKeySpec.mCode == Constants.CODE_OUTPUT_TEXT
+ && mText.equals(moreKeySpec.mOutputText);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return (output instanceof Text) && mText == ((Text)output).mText;
+ }
+
+ @Override
+ public String toString() {
+ return mText;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
new file mode 100644
index 000000000..0a0da32b6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+
+import java.util.Locale;
+
+/**
+ * This class represents an expected visual outlook of a key.
+ *
+ * There are two types of expected visual, an integer icon id and a string label.
+ */
+abstract class ExpectedKeyVisual {
+ static ExpectedKeyVisual newInstance(final String label) {
+ return new Label(label);
+ }
+
+ static ExpectedKeyVisual newInstance(final int iconId) {
+ return new Icon(iconId);
+ }
+
+ abstract ExpectedKeyVisual toUpperCase(final Locale locale);
+ abstract boolean equalsTo(final String text);
+ abstract boolean equalsTo(final Key key);
+ abstract boolean equalsTo(final MoreKeySpec moreKeySpec);
+ abstract boolean equalsTo(final ExpectedKeyOutput output);
+ abstract boolean equalsTo(final ExpectedKeyVisual visual);
+
+ /**
+ * This class represents an integer icon id.
+ */
+ private static class Icon extends ExpectedKeyVisual {
+ private final int mIconId;
+
+ Icon(final int iconId) {
+ mIconId = iconId;
+ }
+
+ @Override
+ ExpectedKeyVisual toUpperCase(final Locale locale) {
+ return this;
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return false;
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mIconId == key.getIconId();
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mIconId == moreKeySpec.mIconId;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return false;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyVisual visual) {
+ return (visual instanceof Icon) && mIconId == ((Icon)visual).mIconId;
+ }
+
+ @Override
+ public String toString() {
+ return KeyboardIconsSet.getIconName(mIconId);
+ }
+ }
+
+ /**
+ * This class represents a string label.
+ */
+ private static class Label extends ExpectedKeyVisual {
+ private final String mLabel;
+
+ Label(final String label) { mLabel = label; }
+
+ @Override
+ ExpectedKeyVisual toUpperCase(final Locale locale) {
+ return new Label(mLabel.toUpperCase(locale));
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return mLabel.equals(text);
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mLabel.equals(key.getLabel());
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mLabel.equals(moreKeySpec.mLabel);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return output.equalsTo(mLabel);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyVisual visual) {
+ return (visual instanceof Label) && mLabel.equals(((Label)visual).mLabel);
+ }
+
+ @Override
+ public String toString() {
+ return mLabel;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java
new file mode 100644
index 000000000..9b7de88ea
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * This class builds an expected keyboard for unit test.
+ *
+ * An expected keyboard is an array of rows, and a row consists of an array of {@link ExpectedKey}s.
+ * Each row may have different number of {@link ExpectedKey}s. While building an expected keyboard,
+ * an {@link ExpectedKey} can be specified by a row number and a column number, both numbers starts
+ * from 1.
+ */
+public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<ExpectedKey> {
+ public ExpectedKeyboardBuilder() {
+ super();
+ }
+
+ public ExpectedKeyboardBuilder(final ExpectedKey[][] rows) {
+ super(rows);
+ }
+
+ @Override
+ protected ExpectedKey defaultElement() {
+ return ExpectedKey.EMPTY_KEY;
+ }
+
+ @Override
+ ExpectedKey[] newArray(final int size) {
+ return new ExpectedKey[size];
+ }
+
+ @Override
+ ExpectedKey[][] newArrayOfArray(final int size) {
+ return new ExpectedKey[size][];
+ }
+
+ @Override
+ public ExpectedKey[][] build() {
+ return super.build();
+ }
+
+ // A replacement job to be performed.
+ private interface ReplaceJob {
+ // Returns a {@link ExpectedKey} objects to replace.
+ ExpectedKey[] replacingKeys(final ExpectedKey oldKey);
+ // Return true if replacing should be stopped at first occurrence.
+ boolean stopAtFirstOccurrence();
+ }
+
+ private static ExpectedKey[] replaceKeyAt(final ExpectedKey[] keys, final int columnIndex,
+ final ExpectedKey[] replacingKeys) {
+ // Optimization for replacing a key with another key.
+ if (replacingKeys.length == 1) {
+ keys[columnIndex] = replacingKeys[0];
+ return keys;
+ }
+ final int newLength = keys.length - 1 + replacingKeys.length;
+ // Remove the key at columnIndex.
+ final ExpectedKey[] newKeys = Arrays.copyOf(keys, newLength);
+ System.arraycopy(keys, columnIndex + 1, newKeys, columnIndex + replacingKeys.length,
+ keys.length - 1 - columnIndex);
+ // Insert replacing keys at columnIndex.
+ System.arraycopy(replacingKeys, 0, newKeys, columnIndex, replacingKeys.length);
+ return newKeys;
+
+ }
+
+ // Replace key(s) that has the specified visual.
+ private void replaceKeyOf(final ExpectedKeyVisual visual, final ReplaceJob job) {
+ int replacedCount = 0;
+ final int rowCount = getRowCount();
+ for (int row = 1; row <= rowCount; row++) {
+ ExpectedKey[] keys = getRowAt(row);
+ for (int columnIndex = 0; columnIndex < keys.length; /* nothing */) {
+ final ExpectedKey currentKey = keys[columnIndex];
+ if (!currentKey.getVisual().equalsTo(visual)) {
+ columnIndex++;
+ continue;
+ }
+ final ExpectedKey[] replacingKeys = job.replacingKeys(currentKey);
+ keys = replaceKeyAt(keys, columnIndex, replacingKeys);
+ columnIndex += replacingKeys.length;
+ setRowAt(row, keys);
+ replacedCount++;
+ if (job.stopAtFirstOccurrence()) {
+ return;
+ }
+ }
+ }
+ if (replacedCount == 0) {
+ throw new RuntimeException(
+ "Can't find key that has visual: " + visual + " in\n" + this);
+ }
+ }
+
+ // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey},
+ // {@link ExpectedKey} array, and {@link String}.
+ static ExpectedKey[] joinKeys(final Object ... keys) {
+ final ArrayList<ExpectedKey> list = new ArrayList<>();
+ for (final Object key : keys) {
+ if (key instanceof ExpectedKey) {
+ list.add((ExpectedKey)key);
+ } else if (key instanceof ExpectedKey[]) {
+ list.addAll(Arrays.asList((ExpectedKey[])key));
+ } else if (key instanceof String) {
+ list.add(ExpectedKey.newInstance((String)key));
+ } else {
+ throw new RuntimeException("Unknown expected key type: " + key);
+ }
+ }
+ return list.toArray(new ExpectedKey[list.size()]);
+ }
+
+ /**
+ * Set the row with specified keys.
+ * @param row the row number to set keys.
+ * @param keys the keys to be set at <code>row</code>. Each key can be {@link ExpectedKey},
+ * {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setKeysOfRow(final int row, final Object ... keys) {
+ setRowAt(row, joinKeys(keys));
+ return this;
+ }
+
+ /**
+ * Set the "more keys" of the key that has the specified label.
+ * @param label the label of the key to set the "more keys".
+ * @param moreKeys the array of "more key" to be set. Each "more key" can be
+ * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setMoreKeysOf(final String label, final Object ... moreKeys) {
+ setMoreKeysOf(ExpectedKeyVisual.newInstance(label), joinKeys(moreKeys));
+ return this;
+ }
+
+ /**
+ * Set the "more keys" of the key that has the specified icon.
+ * @param iconId the icon id of the key to set the "more keys".
+ * @param moreKeys the array of "more key" to be set. Each "more key" can be
+ * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setMoreKeysOf(final int iconId, final Object ... moreKeys) {
+ setMoreKeysOf(ExpectedKeyVisual.newInstance(iconId), joinKeys(moreKeys));
+ return this;
+ }
+
+ private void setMoreKeysOf(final ExpectedKeyVisual visual, final ExpectedKey[] moreKeys) {
+ replaceKeyOf(visual, new ReplaceJob() {
+ @Override
+ public ExpectedKey[] replacingKeys(final ExpectedKey oldKey) {
+ return new ExpectedKey[] { oldKey.setMoreKeys(moreKeys) };
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Set the "additional more keys position" of the key that has the specified label.
+ * @param label the label of the key to set the "additional more keys".
+ * @param additionalMoreKeysPosition the position in the "more keys" where
+ * "additional more keys" will be merged. The position starts from 1.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setAdditionalMoreKeysPositionOf(final String label,
+ final int additionalMoreKeysPosition) {
+ final int additionalMoreKeysIndex = additionalMoreKeysPosition - 1;
+ if (additionalMoreKeysIndex < 0) {
+ throw new RuntimeException("Illegal additional more keys position: "
+ + additionalMoreKeysPosition);
+ }
+ final ExpectedKeyVisual visual = ExpectedKeyVisual.newInstance(label);
+ replaceKeyOf(visual, new ReplaceJob() {
+ @Override
+ public ExpectedKey[] replacingKeys(final ExpectedKey oldKey) {
+ return new ExpectedKey[] {
+ oldKey.setAdditionalMoreKeysIndex(additionalMoreKeysIndex)
+ };
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return true;
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Insert the keys at specified position.
+ * @param row the row number to insert the <code>keys</code>.
+ * @param column the column number to insert the <code>keys</code>.
+ * @param keys the array of keys to insert at <code>row,column</code>. Each key can be
+ * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ * @throws RuntimeException if <code>row</code> or <code>column</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder insertKeysAtRow(final int row, final int column,
+ final Object ... keys) {
+ final ExpectedKey[] expectedKeys = joinKeys(keys);
+ for (int index = 0; index < keys.length; index++) {
+ setElementAt(row, column + index, expectedKeys[index], true /* insert */);
+ }
+ return this;
+ }
+
+ /**
+ * Add the keys on the left most of the row.
+ * @param row the row number to add the <code>keys</code>.
+ * @param keys the array of keys to add on the left most of the row. Each key can be
+ * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ * @throws RuntimeException if <code>row</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder addKeysOnTheLeftOfRow(final int row,
+ final Object ... keys) {
+ final ExpectedKey[] expectedKeys = joinKeys(keys);
+ // Keys should be inserted from the last to preserve the order.
+ for (int index = keys.length - 1; index >= 0; index--) {
+ setElementAt(row, 1, expectedKeys[index], true /* insert */);
+ }
+ return this;
+ }
+
+ /**
+ * Add the keys on the right most of the row.
+ * @param row the row number to add the <code>keys</code>.
+ * @param keys the array of keys to add on the right most of the row. Each key can be
+ * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}.
+ * @return this builder.
+ * @throws RuntimeException if <code>row</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder addKeysOnTheRightOfRow(final int row,
+ final Object ... keys) {
+ final int rightEnd = getRowAt(row).length + 1;
+ insertKeysAtRow(row, rightEnd, keys);
+ return this;
+ }
+
+ /**
+ * Replace the most top-left key that has the specified label with the new keys.
+ * @param label the label of the key to set <code>newKeys</code>.
+ * @param newKeys the keys to be set. Each key can be {@link ExpectedKey}, {@link ExpectedKey}
+ * array, and {@link String}.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder replaceKeyOfLabel(final String label,
+ final Object ... newKeys) {
+ final ExpectedKeyVisual visual = ExpectedKeyVisual.newInstance(label);
+ replaceKeyOf(visual, new ReplaceJob() {
+ @Override
+ public ExpectedKey[] replacingKeys(final ExpectedKey oldKey) {
+ return joinKeys(newKeys);
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return true;
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Replace the all specified keys with the new keys.
+ * @param key the key to be replaced by <code>newKeys</code>.
+ * @param newKeys the keys to be set. Each key can be {@link ExpectedKey}, {@link ExpectedKey}
+ * array, and {@link String}.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder replaceKeysOfAll(final ExpectedKey key,
+ final Object ... newKeys) {
+ replaceKeyOf(key.getVisual(), new ReplaceJob() {
+ @Override
+ public ExpectedKey[] replacingKeys(final ExpectedKey oldKey) {
+ return joinKeys(newKeys);
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return false;
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Convert all keys of this keyboard builder to upper case keys.
+ * @param locale the locale used to convert cases.
+ * @return this builder
+ */
+ public ExpectedKeyboardBuilder toUpperCase(final Locale locale) {
+ final int rowCount = getRowCount();
+ for (int row = 1; row <= rowCount; row++) {
+ final ExpectedKey[] lowerCaseKeys = getRowAt(row);
+ final ExpectedKey[] upperCaseKeys = new ExpectedKey[lowerCaseKeys.length];
+ for (int columnIndex = 0; columnIndex < lowerCaseKeys.length; columnIndex++) {
+ upperCaseKeys[columnIndex] = lowerCaseKeys[columnIndex].toUpperCase(locale);
+ }
+ setRowAt(row, upperCaseKeys);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return toString(build());
+ }
+
+ /**
+ * Convert the keyboard to human readable string.
+ * @param rows the keyboard to be converted to string.
+ * @return the human readable representation of <code>rows</code>.
+ */
+ public static String toString(final ExpectedKey[][] rows) {
+ final StringBuilder sb = new StringBuilder();
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ if (rowIndex > 0) {
+ sb.append("\n");
+ }
+ sb.append(Arrays.toString(rows[rowIndex]));
+ }
+ return sb.toString();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java
new file mode 100644
index 000000000..3e82f65bf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class EnglishCustomizer extends LayoutCustomizer {
+ EnglishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e", "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0113")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FB", "\u00FC", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ .setMoreKeysOf("i", "\u00ED", "\u00EE", "\u00EF", "\u012B", "\u00EC")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F4", "\u00F6", "\u00F2", "\u0153", "\u00F8", "\u014D",
+ "\u00F5")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ .setMoreKeysOf("s", "\u00DF")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ .setMoreKeysOf("c", "\u00E7")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ .setMoreKeysOf("n", "\u00F1");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
new file mode 100644
index 000000000..ab90267d0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class FrenchCustomizer extends LayoutCustomizer {
+ FrenchCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setAdditionalMoreKeysPositionOf("a", 3)
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E2", "\u00E6", "\u00E1", "\u00E4", "\u00E3", "\u00E5",
+ "\u0101", "\u00AA")
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setAdditionalMoreKeysPositionOf("e", 5)
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FF")
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setAdditionalMoreKeysPositionOf("u", 3)
+ .setMoreKeysOf("u", "\u00F9", "\u00FB", "\u00FC", "\u00FA", "\u016B")
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setAdditionalMoreKeysPositionOf("i", 2)
+ .setMoreKeysOf("i", "\u00EE", "\u00EF", "\u00EC", "\u00ED", "\u012F", "\u012B")
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setAdditionalMoreKeysPositionOf("o", 3)
+ .setMoreKeysOf("o",
+ "\u00F4", "\u0153", "\u00F6", "\u00F2", "\u00F3", "\u00F5", "\u00F8",
+ "\u014D", "\u00BA")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+ .setAdditionalMoreKeysPositionOf("c", 2);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java
new file mode 100644
index 000000000..6d38937aa
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class GermanCustomizer extends LayoutCustomizer {
+ public GermanCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ .setMoreKeysOf("e", "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0117")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B")
+ .setAdditionalMoreKeysPositionOf("u", 2)
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F6", "\u00F4", "\u00F2", "\u00F3", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ .setAdditionalMoreKeysPositionOf("o", 2)
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E4", "\u00E2", "\u00E0", "\u00E1", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ .setAdditionalMoreKeysPositionOf("a", 2)
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u00DF", "\u015B", "\u0161")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
new file mode 100644
index 000000000..735070946
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class ItalianCustomizer extends LayoutCustomizer {
+ public ItalianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u016B")
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u012F", "\u012B")
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F2", "\u00F3", "\u00F4", "\u00F6", "\u00F5", "\u0153", "\u00F8",
+ "\u014D", "\u00BA")
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101", "\u00AA");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java
new file mode 100644
index 000000000..a22ed60ac
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.util.Log;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
+import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
+import com.android.inputmethod.keyboard.KeyboardTheme;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ActualKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.Arrays;
+
+/**
+ * Base class for keyboard layout unit test.
+ */
+abstract class LayoutTestsBase extends KeyboardLayoutSetTestsBase {
+ private LayoutBase mLayout;
+ private InputMethodSubtype mSubtype;
+ private String mLogTag;
+ private KeyboardLayoutSet mKeyboardLayoutSet;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mLayout = getLayout();
+ mSubtype = getSubtype(mLayout.getLocale(), mLayout.getName());
+ mLogTag = SubtypeLocaleUtils.getSubtypeNameForLogging(mSubtype) + "/"
+ + (isPhone() ? "phone" : "tablet");
+ // TODO: Test with language switch key enabled and disabled.
+ mKeyboardLayoutSet = createKeyboardLayoutSet(mSubtype, null /* editorInfo */,
+ true /* voiceInputKeyEnabled */, true /* languageSwitchKeyEnabled */);
+ }
+
+ @Override
+ protected int getKeyboardThemeForTests() {
+ return KeyboardTheme.THEME_ID_KLP;
+ }
+
+ // Those helper methods have a lower case name to be readable when defining expected keyboard
+ // layouts.
+
+ // Helper method to create an {@link ExpectedKey} object that has the label.
+ static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) {
+ return AbstractLayoutBase.key(label, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has the label and the output text.
+ static ExpectedKey key(final String label, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return AbstractLayoutBase.key(label, outputText, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object that has new "more keys".
+ static ExpectedKey key(final ExpectedKey key, final ExpectedKey ... moreKeys) {
+ return AbstractLayoutBase.key(key, moreKeys);
+ }
+
+ // Helper method to create an {@link ExpectedAdditionalMoreKey} object for an
+ // "additional more key" that has the label.
+ public static ExpectedAdditionalMoreKey additionalMoreKey(final String label) {
+ return AbstractLayoutBase.additionalMoreKey(label);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the label.
+ static ExpectedKey moreKey(final String label) {
+ return AbstractLayoutBase.moreKey(label);
+ }
+
+ // Helper method to create an {@link ExpectedKey} object for a "more key" that has the label
+ // and the output text.
+ static ExpectedKey moreKey(final String label, final String outputText) {
+ return AbstractLayoutBase.moreKey(label, outputText);
+ }
+
+ // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey},
+ // {@link ExpectedKey} array, and {@link String}.
+ static ExpectedKey[] joinMoreKeys(final Object ... moreKeys) {
+ return AbstractLayoutBase.joinKeys(moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey},
+ // {@link ExpectedKey} array, and {@link String}.
+ static ExpectedKey[] joinKeys(final Object ... keys) {
+ return AbstractLayoutBase.joinKeys(keys);
+ }
+
+ // Keyboard layout for testing subtype.
+ abstract LayoutBase getLayout();
+
+ ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder;
+ }
+
+ // TODO: Add phone, phone symbols, number, number password layout tests.
+
+ public final void testAlphabet() {
+ doKeyboardTests(KeyboardId.ELEMENT_ALPHABET);
+ }
+
+ public final void testAlphabetAutomaticShifted() {
+ doKeyboardTests(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
+ }
+
+ public final void testAlphabetManualShifted() {
+ doKeyboardTests(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED);
+ }
+
+ public final void testAlphabetShiftLocked() {
+ doKeyboardTests(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED);
+ }
+
+ public final void testAlphabetShiftLockShifted() {
+ doKeyboardTests(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED);
+ }
+
+ public final void testSymbols() {
+ doKeyboardTests(KeyboardId.ELEMENT_SYMBOLS);
+ }
+
+ public final void testSymbolsShifted() {
+ doKeyboardTests(KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
+ }
+
+ // Comparing expected keyboard and actual keyboard.
+ private void doKeyboardTests(final int elementId) {
+ final ExpectedKey[][] expectedKeyboard = mLayout.getLayout(isPhone(), elementId);
+ // Skip test if no keyboard is defined.
+ if (expectedKeyboard == null) {
+ return;
+ }
+ final String tag = mLogTag + "/" + KeyboardId.elementIdToName(elementId);
+ // Create actual keyboard object.
+ final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(elementId);
+ // Create actual keyboard to be compared with the expected keyboard.
+ final Key[][] actualKeyboard = ActualKeyboardBuilder.buildKeyboard(
+ keyboard.getSortedKeys());
+
+ // Dump human readable definition of expected/actual keyboards.
+ Log.d(tag, "expected=\n" + ExpectedKeyboardBuilder.toString(expectedKeyboard));
+ Log.d(tag, "actual =\n" + ActualKeyboardBuilder.toString(actualKeyboard));
+ // Test both keyboards have the same number of rows.
+ assertEquals(tag + " labels"
+ + "\nexpected=" + ExpectedKeyboardBuilder.toString(expectedKeyboard)
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard),
+ expectedKeyboard.length, actualKeyboard.length);
+ for (int r = 0; r < actualKeyboard.length; r++) {
+ final int row = r + 1;
+ // Test both keyboards' rows have the same number of columns.
+ assertEquals(tag + " labels row=" + row
+ + "\nexpected=" + Arrays.toString(expectedKeyboard[r])
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard[r]),
+ expectedKeyboard[r].length, actualKeyboard[r].length);
+ for (int c = 0; c < actualKeyboard[r].length; c++) {
+ final int column = c + 1;
+ final Key actualKey = actualKeyboard[r][c];
+ final ExpectedKey expectedKey = expectedKeyboard[r][c];
+ // Test both keyboards' keys have the same visual outlook and key output.
+ assertTrue(tag + " labels row,column=" + row + "," + column
+ + "\nexpected=" + expectedKey
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKey),
+ expectedKey.equalsTo(actualKey));
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
new file mode 100644
index 000000000..9edbcab69
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class NoLanguageCustomizer extends LayoutCustomizer {
+ NoLanguageCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+ .setMoreKeysOf("w", "\u0175")
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+0115: "ĕ" LATIN SMALL LETTER E WITH BREVE
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ .setMoreKeysOf("e",
+ "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113", "\u0115", "\u0117",
+ "\u0119", "\u011B")
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ .setMoreKeysOf("r", "\u0155", "\u0157", "\u0159")
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
+ .setMoreKeysOf("t", "\u00FE", "\u0163", "\u0165", "\u0167")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("y", "\u00FD", "\u0177", "\u00FF", "\u0133")
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ .setMoreKeysOf("u",
+ "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u0169", "\u016B", "\u016D",
+ "\u016F", "\u0171", "\u0173")
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+012D: "ĭ" LATIN SMALL LETTER I WITH BREVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("i",
+ "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u0129", "\u012B", "\u012D",
+ "\u012F", "\u0131", "\u0133")
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+014F: "ŏ" LATIN SMALL LETTER O WITH BREVE
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F2", "\u00F3", "\u00F4", "\u00F5", "\u00F6", "\u00F8", "\u014D",
+ "\u014F", "\u0151", "\u0153", "\u00BA")
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u00E4", "\u00E5", "\u00E6",
+ "\u0101", "\u0103", "\u0105", "\u00AA")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+017F: "ſ" LATIN SMALL LETTER LONG S
+ .setMoreKeysOf("s", "\u00DF", "\u015B", "\u015D", "\u015F", "\u0161", "\u017F")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ .setMoreKeysOf("d", "\u010F", "\u0111", "\u00F0")
+ // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ .setMoreKeysOf("g", "\u011D", "\u011F", "\u0121", "\u0123")
+ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
+ .setMoreKeysOf("h", "\u0125")
+ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
+ .setMoreKeysOf("j", "\u0135")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ // U+0138: "ĸ" LATIN SMALL LETTER KRA
+ .setMoreKeysOf("k", "\u0137", "\u0138")
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u013A", "\u013C", "\u013E", "\u0140", "\u0142")
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ .setMoreKeysOf("z", "\u017A", "\u017C", "\u017E")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
+ // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u0109", "\u010B", "\u010D")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ // U+014B: "ŋ" LATIN SMALL LETTER ENG
+ .setMoreKeysOf("n", "\u00F1", "\u0144", "\u0146", "\u0148", "\u0149", "\u014B");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java
new file mode 100644
index 000000000..629e8cb8b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class PortugueseCustomizer extends LayoutCustomizer {
+ PortugueseCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00EA", "\u00E8", "\u0119", "\u0117", "\u0113", "\u00EB")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00F9", "\u00FB", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EE", "\u00EC", "\u00EF", "\u012F", "\u012B")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F5", "\u00F4", "\u00F2", "\u00F6", "\u0153", "\u00F8",
+ "\u014D", "\u00BA")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E3", "\u00E0", "\u00E2", "\u00E4", "\u00E5", "\u00E6",
+ "\u00AA")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u00E7", "\u010D", "\u0107");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java
new file mode 100644
index 000000000..8974ad6ec
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+class SpanishCustomizer extends LayoutCustomizer {
+ SpanishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? PHONE_PUNCTUATION_MORE_KEYS
+ : LayoutBase.TABLET_PUNCTUATION_MORE_KEYS;
+ }
+
+ // Punctuation more keys for phone form factor.
+ private static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = AbstractLayoutBase.joinKeys(
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ ",", "?", "!", "#", ")", "(", "/", ";", "\u00A1",
+ "'", "@", ":", "-", "\"", "+", "%", "&", "\u00BF");
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00E8", "\u00EB", "\u00EA", "\u0119", "\u0117", "\u0113")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00F9", "\u00FB", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EF", "\u00EC", "\u00EE", "\u012F", "\u012B")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F2", "\u00F6", "\u00F4", "\u00F5", "\u00F8", "\u0153",
+ "\u014D", "\u00BA")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E0", "\u00E4", "\u00E2", "\u00E3", "\u00E5", "\u0105",
+ "\u00E6", "\u0101", "\u00AA")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ .replaceKeyOfLabel(Spanish.ROW2_10, "\u00F1")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java
new file mode 100644
index 000000000..cd2259888
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * af: TestsAfrikaans/qwerty
+ */
+@SmallTest
+public final class TestsAfrikaans extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("af");
+ private static final LayoutBase LAYOUT = new Qwerty(new AfrikaansCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class AfrikaansCustomizer extends LayoutCustomizer {
+ AfrikaansCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FB", "\u00FC", "\u00F9", "\u016B")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("y", "\u00FD", "\u0133")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("i",
+ "\u00ED", "\u00EC", "\u00EF", "\u00EE", "\u012F", "\u012B", "\u0133")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F4", "\u00F6", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E2", "\u00E4", "\u00E0", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java
new file mode 100644
index 000000000..fd7670827
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Arabic;
+import com.android.inputmethod.keyboard.layout.Arabic.ArabicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * ar: Arabic/arabic
+ */
+@SmallTest
+public class TestsArabic extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ar");
+ private static final LayoutBase LAYOUT = new Arabic(new ArabicCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java
new file mode 100644
index 000000000..327e9438f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.ArmenianPhonetic;
+import com.android.inputmethod.keyboard.layout.ArmenianPhonetic.ArmenianPhoneticCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic
+ */
+@SmallTest
+public final class TestsArmenianAMPhonetic extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("hy", "AM");
+ private static final LayoutBase LAYOUT = new ArmenianPhonetic(
+ new ArmenianPhoneticCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java
new file mode 100644
index 000000000..f5317e269
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * az_AZ: Azerbaijani (Azerbaijan)/qwerty
+ */
+@SmallTest
+public final class TestsAzerbaijaniAZ extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("az", "AZ");
+ private static final LayoutBase LAYOUT = new Qwerty(new AzerbaijaniAZCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static final class AzerbaijaniAZCustomizer extends LayoutCustomizer {
+ public AzerbaijaniAZCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0259: "ə" LATIN SMALL LETTER SCHWA
+ .setMoreKeysOf("e", "\u0259")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B")
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i",
+ "\u0131", "\u00EE", "\u00EF", "\u00EC", "\u00ED", "\u012F", "\u012B")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F6", "\u00F4", "\u0153", "\u00F2", "\u00F3", "\u00F5", "\u00F8",
+ "\u014D")
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ .setMoreKeysOf("a", "\u00E2")
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u015F", "\u00DF", "\u015B", "\u0161")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u011F");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java
new file mode 100644
index 000000000..bef18c5d5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * eu_ES: Basque (Spain)/spanish
+ */
+@SmallTest
+public class TestsBasqueES extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("eu", "ES");
+ private static final LayoutBase LAYOUT = new Spanish(new BasqueESCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class BasqueESCustomizer extends EuroCustomizer {
+ private final SpanishCustomizer mSpanishCustomizer;
+
+ public BasqueESCustomizer(final Locale locale) {
+ super(locale);
+ mSpanishCustomizer = new SpanishCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mSpanishCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java
new file mode 100644
index 000000000..c5238d54f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic;
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * be_BY: Belarusian (Belarus)/east_slavic
+ */
+@SmallTest
+public final class TestsBelarusianBY extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("be", "BY");
+ private static final LayoutBase LAYOUT = new EastSlavic(new BelarusianBYCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class BelarusianBYCustomizer extends EastSlavicCustomizer {
+ public BelarusianBYCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() {
+ return Symbols.DOUBLE_QUOTES_R9L;
+ }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() {
+ return Symbols.SINGLE_QUOTES_R9L;
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ .setMoreKeysOf("\u0435", "\u0451")
+ // U+045E: "ў" CYRILLIC SMALL LETTER SHORT U
+ .replaceKeyOfLabel(EastSlavic.ROW1_9, key("\u045E", additionalMoreKey("9")))
+ // U+044B: "ы" CYRILLIC SMALL LETTER YERU
+ .replaceKeyOfLabel(EastSlavic.ROW2_2, "\u044B")
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ .replaceKeyOfLabel(EastSlavic.ROW2_11, "\u044D")
+ // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ .replaceKeyOfLabel(EastSlavic.ROW3_5, "\u0456")
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ .setMoreKeysOf("\u044C", "\u044A");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java
new file mode 100644
index 000000000..d64263207
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Bengali;
+import com.android.inputmethod.keyboard.layout.Bengali.BengaliCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * bn_IN: Bengali (India)/bengali
+ */
+@SmallTest
+public final class TestsBengaliIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("bn", "IN");
+ private static final LayoutBase LAYOUT = new Bengali(new BengaliINCustomzier(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class BengaliINCustomzier extends BengaliCustomizer {
+ public BengaliINCustomzier(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java
new file mode 100644
index 000000000..ded8d7243
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Bulgarian;
+import com.android.inputmethod.keyboard.layout.Bulgarian.BulgarianCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * bg: TestsBulgarian/bulgarian
+ */
+@SmallTest
+public final class TestsBulgarian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("bg");
+ private static final LayoutBase LAYOUT = new Bulgarian(new BulgarianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java
new file mode 100644
index 000000000..22b2011ee
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.BulgarianBds;
+import com.android.inputmethod.keyboard.layout.BulgarianBds.BulgarianBdsCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * bg: Bulgarian/bulgarian_bds
+ */
+@SmallTest
+public final class TestsBulgarianBds extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("bg");
+ private static final LayoutBase LAYOUT = new BulgarianBds(new BulgarianBdsCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java
new file mode 100644
index 000000000..151a0a627
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * ca: Catalan/spanish
+ */
+@SmallTest
+public class TestsCatalan extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ca");
+ private static final LayoutBase LAYOUT = new Spanish(new CatalanCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class CatalanCustomizer extends EuroCustomizer {
+ public CatalanCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? PHONE_PUNCTUATION_MORE_KEYS
+ : TABLET_PUNCTUATION_MORE_KEYS;
+ }
+
+ // U+00B7: "·" MIDDLE DOT
+ private static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "?", "!", "\u00B7", "#", ")", "(", "/", ";",
+ "'", "@", ":", "-", "\"", "+", "%", "&");
+
+ private static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+ ",", "'", "\u00B7", "#", ")", "(", "/", ";",
+ "@", ":", "-", "\"", "+", "%", "&");
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E8", "\u00E9", "\u00EB", "\u00EA", "\u0119", "\u0117", "\u0113")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00F9", "\u00FB", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EF", "\u00EC", "\u00EE", "\u012F", "\u012B")
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F2", "\u00F3", "\u00F6", "\u00F4", "\u00F5", "\u00F8", "\u0153",
+ "\u014D", "\u00BA")
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E4", "\u00E2", "\u00E3", "\u00E5", "\u0105",
+ "\u00E6", "\u0101", "\u00AA")
+ // U+00B7: "·" MIDDLE DOT
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "l\u00B7l", "\u0142")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ .replaceKeyOfLabel(Spanish.ROW2_10, "\u00E7")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java
new file mode 100644
index 000000000..8575ef219
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * hr: Croatian/qwertz
+ */
+@SmallTest
+public final class TestsCroatian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("hr");
+ private static final LayoutBase LAYOUT = new Qwertz(new CroatianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class CroatianCustomizer extends LayoutCustomizer {
+ public CroatianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ .setMoreKeysOf("z", "\u017E", "\u017A", "\u017C")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ .setMoreKeysOf("s", "\u0161", "\u015B", "\u00DF")
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ .setMoreKeysOf("d", "\u0111")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ .setMoreKeysOf("c", "\u010D", "\u0107", "\u00E7")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java
new file mode 100644
index 000000000..f4794707f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * cs: Czech/qwertz
+ */
+@SmallTest
+public final class TestsCzech extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("cs");
+ private static final LayoutBase LAYOUT = new Qwertz(new CzechCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class CzechCustomizer extends LayoutCustomizer {
+ public CzechCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u011B", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117",
+ "\u0113")
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ .setMoreKeysOf("r", "\u0159")
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ .setMoreKeysOf("t", "\u0165")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ .setMoreKeysOf("z", "\u017E", "\u017A", "\u017C")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u016F", "\u00FB", "\u00FC", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EE", "\u00EF", "\u00EC", "\u012F", "\u012B")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E0", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u010F")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u00E7", "\u0107")
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u0148", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java
new file mode 100644
index 000000000..85c63a128
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * da: Danish/nordic
+ */
+@SmallTest
+public final class TestsDanish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("da");
+ private static final LayoutBase LAYOUT = new Nordic(new DanishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class DanishCustomizer extends EuroCustomizer {
+ public DanishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ .setMoreKeysOf("e", "\u00E9", "\u00EB")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00FB", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ .setMoreKeysOf("i", "\u00ED", "\u00EF")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o", "\u00F3", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u014D")
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ .replaceKeyOfLabel(Nordic.ROW1_11, "\u00E5")
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW2_10, key("\u00E6", moreKey("\u00E4")))
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW2_11, key("\u00F8", moreKey("\u00F6")))
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a", "\u00E1", "\u00E4", "\u00E0", "\u00E2", "\u00E3", "\u0101")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u00DF", "\u015B", "\u0161")
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ .setMoreKeysOf("d", "\u00F0")
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u0142")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java
new file mode 100644
index 000000000..1730f66be
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * nl: Dutch/qwerty
+ */
+@SmallTest
+public final class TestsDutch extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("nl");
+ private static final LayoutBase LAYOUT = new Qwerty(new DutchCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ static class DutchCustomizer extends EuroCustomizer {
+ public DutchCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E4", "\u00E2", "\u00E0", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00EB", "\u00EA", "\u00E8", "\u0119", "\u0117", "\u0113")
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("y", "\u0133")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00FB", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("i",
+ "\u00ED", "\u00EF", "\u00EC", "\u00EE", "\u012F", "\u012B", "\u0133")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java
new file mode 100644
index 000000000..31adf7a8d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Azerty;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.tests.TestsDutch.DutchCustomizer;
+
+import java.util.Locale;
+
+/**
+ * nl_BE: Dutch (Belgium)/azerty
+ */
+@SmallTest
+public final class TestsDutchBE extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("nl", "BE");
+ private static final LayoutBase LAYOUT = new Azerty(new DutchCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
new file mode 100644
index 000000000..a05269312
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * en_US: English (United States)/dvorak
+ */
+@SmallTest
+public class TestsEnglishDvorak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("en", "US");
+ private static final LayoutBase LAYOUT = new Dvorak(new EnglishDvorakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class EnglishDvorakCustomizer extends DvorakCustomizer {
+ private final EnglishCustomizer mEnglishCustomizer;
+
+ EnglishDvorakCustomizer(final Locale locale) {
+ super(locale);
+ mEnglishCustomizer = new EnglishCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mEnglishCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java
new file mode 100644
index 000000000..c80b25024
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/*
+ * en_IN: English (India)/qwerty
+ */
+@SmallTest
+public final class TestsEnglishIN extends TestsEnglishUS {
+ private static final Locale LOCALE = new Locale("en", "IN");
+ private static final LayoutBase LAYOUT = new Qwerty(new EnglishINCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class EnglishINCustomizer extends EnglishCustomizer {
+ public EnglishINCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java
new file mode 100644
index 000000000..c0dcbdc06
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/*
+ * en_GB: English (Great Britain)/qwerty
+ */
+@SmallTest
+public final class TestsEnglishUK extends TestsEnglishUS {
+ private static final Locale LOCALE = new Locale("en", "GB");
+ private static final LayoutBase LAYOUT = new Qwerty(new EnglishUKCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class EnglishUKCustomizer extends EnglishCustomizer {
+ public EnglishUKCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_POUND; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() { return CURRENCIES_OTHER_THAN_POUND; }
+
+ private static final ExpectedKey CURRENCY_POUND = key(Symbols.POUND_SIGN,
+ Symbols.CENT_SIGN, Symbols.DOLLAR_SIGN, Symbols.EURO_SIGN, Symbols.YEN_SIGN,
+ Symbols.PESO_SIGN);
+
+ private static final ExpectedKey[] CURRENCIES_OTHER_THAN_POUND = {
+ Symbols.EURO_SIGN, Symbols.YEN_SIGN, key(Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN),
+ Symbols.CENT_SIGN
+ };
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
new file mode 100644
index 000000000..6ea8f6000
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * en_US: English (United States)/qwerty
+ */
+@SmallTest
+public class TestsEnglishUS extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("en", "US");
+ private static final LayoutBase LAYOUT = new Qwerty(new EnglishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java
new file mode 100644
index 000000000..6a44187c9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * eo: Esperanto/spanish
+ */
+@SmallTest
+public class TestsEsperanto extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("eo");
+ private static final LayoutBase LAYOUT = new Spanish(new EsperantoCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class EsperantoCustomizer extends LayoutCustomizer {
+ public EsperantoCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
+ .replaceKeyOfLabel("q", key("\u015D", joinMoreKeys(
+ additionalMoreKey("1"), "q")))
+ // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
+ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+ .replaceKeyOfLabel("w", key("\u011D", joinMoreKeys(
+ additionalMoreKey("2"), "w", "\u0175")))
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u011B", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117",
+ "\u0113")
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ .setMoreKeysOf("r", "\u0159", "\u0155", "\u0157")
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
+ .setMoreKeysOf("t", "\u0165", "\u021B", "\u0163", "\u0167")
+ // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ .replaceKeyOfLabel("y", key("\u016D", joinMoreKeys(
+ additionalMoreKey("6"), "y", "\u00FD", "\u0177", "\u00FF", "\u00FE")))
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00B5: "µ" MICRO SIGN
+ .setMoreKeysOf("u",
+ "\u00FA", "\u016F", "\u00FB", "\u00FC", "\u00F9", "\u016B", "\u0169",
+ "\u0171", "\u0173", "\u00B5")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ .setMoreKeysOf("i",
+ "\u00ED", "\u00EE", "\u00EF", "\u0129", "\u00EC", "\u012F", "\u012B",
+ "\u0131", "\u0133")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D", "\u0151", "\u00BA")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E0", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101", "\u0103", "\u0105", "\u00AA")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ .setMoreKeysOf("s", "\u00DF", "\u0161", "\u015B", "\u0219", "\u015F")
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ .setMoreKeysOf("d", "\u00F0", "\u010F", "\u0111")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ // U+014B: "ŋ" LATIN SMALL LETTER ENG
+ .setMoreKeysOf("n", "\u00F1", "\u0144", "\u0146", "\u0148", "\u0149", "\u014B")
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ .setMoreKeysOf("g", "\u011F", "\u0121", "\u0123")
+ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
+ // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE
+ .setMoreKeysOf("h", "\u0125", "\u0127")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ // U+0138: "ĸ" LATIN SMALL LETTER KRA
+ .setMoreKeysOf("k", "\u0137", "\u0138")
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u013A", "\u013C", "\u013E", "\u0140", "\u0142")
+ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
+ .replaceKeyOfLabel(Spanish.ROW2_10, "\u0135")
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ .setMoreKeysOf("z", "\u017A", "\u017C", "\u017E")
+ // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
+ .replaceKeyOfLabel("x", key("\u0109", moreKey("x")))
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
+ .setMoreKeysOf("c", "\u0107", "\u010D", "\u00E7", "\u010B")
+ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+ .setMoreKeysOf("v", "w", "\u0175");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java
new file mode 100644
index 000000000..865e9ea17
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * et_EE: Estonian (Estonia)/nordic
+ */
+@SmallTest
+public final class TestsEstonianEE extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("et", "EE");
+ private static final LayoutBase LAYOUT = new Nordic(new EstonianEECustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class EstonianEECustomizer extends EuroCustomizer {
+ public EstonianEECustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ .setMoreKeysOf("e",
+ "\u0113", "\u00E8", "\u0117", "\u00E9", "\u00EA", "\u00EB", "\u0119",
+ "\u011B")
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ .setMoreKeysOf("r", "\u0157", "\u0159", "\u0155")
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ .setMoreKeysOf("t", "\u0163", "\u0165")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ .setMoreKeysOf("u",
+ "\u00FC", "\u016B", "\u0173", "\u00F9", "\u00FA", "\u00FB", "\u016F",
+ "\u0171")
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ .setMoreKeysOf("i",
+ "\u012B", "\u00EC", "\u012F", "\u00ED", "\u00EE", "\u00EF", "\u0131")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ .setMoreKeysOf("o",
+ "\u00F6", "\u00F5", "\u00F2", "\u00F3", "\u00F4", "\u0153", "\u0151",
+ "\u00F8")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW1_11, "\u00FC")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ .replaceKeyOfLabel(Nordic.ROW2_10, key("\u00F6", moreKey("\u00F5")))
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW2_11, "\u00E4")
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ .setMoreKeysOf("a",
+ "\u00E4", "\u0101", "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u00E5",
+ "\u00E6", "\u0105")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B", "\u015F")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u010F")
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u0123", "\u011F")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ .setMoreKeysOf("k", "\u0137")
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ .setMoreKeysOf("l", "\u013C", "\u0142", "\u013A", "\u013E")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ .setMoreKeysOf("z", "\u017E", "\u017C", "\u017A")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u00E7", "\u0107")
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u0146", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java
new file mode 100644
index 000000000..ff32da117
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * fi: Finnish/nordic
+ */
+@SmallTest
+public final class TestsFinnish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fi");
+ private static final LayoutBase LAYOUT = new Nordic(new FinnishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class FinnishCustomizer extends EuroCustomizer {
+ public FinnishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ .setMoreKeysOf("u", "\u00FC")
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F8", "\u00F4", "\u00F2", "\u00F3", "\u00F5", "\u0153", "\u014D")
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ .replaceKeyOfLabel(Nordic.ROW1_11, "\u00E5")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ .replaceKeyOfLabel(Nordic.ROW2_10, key("\u00F6", moreKey("\u00F8")))
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ .replaceKeyOfLabel(Nordic.ROW2_11, key("\u00E4", moreKey("\u00E6")))
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a", "\u00E6", "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u0101")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ .setMoreKeysOf("z", "\u017E", "\u017A", "\u017C");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java
new file mode 100644
index 000000000..7ced1fb7b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Azerty;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * fr: French/azerty
+ */
+@SmallTest
+public final class TestsFrench extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fr");
+ private static final LayoutBase LAYOUT = new Azerty(new FrenchEuroCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ static final class FrenchEuroCustomizer extends FrenchCustomizer {
+ private final EuroCustomizer mEuroCustomizer;
+
+ public FrenchEuroCustomizer(final Locale locale) {
+ super(locale);
+ mEuroCustomizer = new EuroCustomizer(locale);
+ }
+
+ @Override
+ public final ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+ @Override
+ public final ExpectedKey[] getOtherCurrencyKeys() {
+ return mEuroCustomizer.getOtherCurrencyKeys();
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java
new file mode 100644
index 000000000..9b3cd1ee2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * fr_CA: French (Canada)/qwerty
+ */
+@SmallTest
+public final class TestsFrenchCA extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fr", "CA");
+ private static final LayoutBase LAYOUT = new Qwerty(new FrenchCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java
new file mode 100644
index 000000000..2598aa3bf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * fr_CH: French (Switzerland)/swiss
+ */
+@SmallTest
+public final class TestsFrenchCH extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fr", "CH");
+ private static final LayoutBase LAYOUT = new Swiss(new FrenchCHCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class FrenchCHCustomizer extends FrenchCustomizer {
+ public FrenchCHCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ super.setAccentedLetters(builder);
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ .replaceKeyOfLabel(Swiss.ROW1_11, key("\u00E8", moreKey("\u00FC")))
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ .replaceKeyOfLabel(Swiss.ROW2_10, key("\u00E9", moreKey("\u00F6")))
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ .replaceKeyOfLabel(Swiss.ROW2_11, key("\u00E0", moreKey("\u00E4")));
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
new file mode 100644
index 000000000..33d1445a4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.tests.TestsFrench.FrenchEuroCustomizer;
+
+import java.util.Locale;
+
+/**
+ * fr: French/dvorak
+ */
+@SmallTest
+public final class TestsFrenchDvorak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fr");
+ private static final LayoutBase LAYOUT = new Dvorak(new FrenchDvorakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class FrenchDvorakCustomizer extends DvorakCustomizer {
+ private final FrenchEuroCustomizer mFrenchEuroCustomizer;
+
+ public FrenchDvorakCustomizer(final Locale locale) {
+ super(locale);
+ mFrenchEuroCustomizer = new FrenchEuroCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return mFrenchEuroCustomizer.getCurrencyKey(); }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return mFrenchEuroCustomizer.getOtherCurrencyKeys();
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mFrenchEuroCustomizer.setAccentedLetters(builder);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java
new file mode 100644
index 000000000..6ab28704a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.tests.TestsFrench.FrenchEuroCustomizer;
+
+import java.util.Locale;
+
+/**
+ * fr: French/qwertz
+ */
+@SmallTest
+public final class TestsFrenchQwertz extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fr");
+ private static final LayoutBase LAYOUT = new Qwertz(new FrenchEuroCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java
new file mode 100644
index 000000000..1472828a4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * gl_ES: Galician (Spain)/spanish
+ */
+@SmallTest
+public class TestsGalicianES extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("gl", "ES");
+ private static final LayoutBase LAYOUT = new Spanish(new GalicianESCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class GalicianESCustomizer extends EuroCustomizer {
+ private final SpanishCustomizer mSpanishCustomizer;
+
+ public GalicianESCustomizer(final Locale locale) {
+ super(locale);
+ mSpanishCustomizer = new SpanishCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mSpanishCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java
new file mode 100644
index 000000000..f25942fb5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Georgian;
+import com.android.inputmethod.keyboard.layout.Georgian.GeorgianCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * ka_GE: Georgian (Georgia)/georgian
+ */
+@SmallTest
+public final class TestsGeorgianGE extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ka", "GE");
+ private static final LayoutBase LAYOUT = new Georgian(new GeorgianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java
new file mode 100644
index 000000000..6f7571197
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * de: German/qwertz
+ */
+@SmallTest
+public final class TestsGerman extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("de");
+ private static final LayoutBase LAYOUT = new Qwertz(new GermanEuroCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ static class GermanEuroCustomizer extends GermanCustomizer {
+ final EuroCustomizer mEuroCustomizer;
+
+ public GermanEuroCustomizer(final Locale locale) {
+ super(locale);
+ mEuroCustomizer = new EuroCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return mEuroCustomizer.getOtherCurrencyKeys();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java
new file mode 100644
index 000000000..7deb00bb4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * de_CH: German (Switzerland)/swiss
+ */
+@SmallTest
+public final class TestsGermanCH extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("de", "CH");
+ private static final LayoutBase LAYOUT = new Swiss(new GermanCHCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class GermanCHCustomizer extends GermanCustomizer {
+ public GermanCHCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ super.setAccentedLetters(builder);
+ return builder
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ .replaceKeyOfLabel(Swiss.ROW1_11, key("\u00FC", moreKey("\u00E8")))
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ .replaceKeyOfLabel(Swiss.ROW2_10, key("\u00F6", moreKey("\u00E9")))
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ .replaceKeyOfLabel(Swiss.ROW2_11, key("\u00E4", moreKey("\u00E0")));
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
new file mode 100644
index 000000000..b28d5cfcf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * de: German/dvorak
+ */
+@SmallTest
+public final class TestsGermanDvorak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("de");
+ private static final LayoutBase LAYOUT = new Dvorak(new GermanDvorakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ static class GermanDvorakCustomizer extends DvorakCustomizer {
+ final GermanCustomizer mGermanCustomizer;
+
+ public GermanDvorakCustomizer(final Locale locale) {
+ super(locale);
+ mGermanCustomizer = new GermanCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
+ }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mGermanCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java
new file mode 100644
index 000000000..19ae5a3f5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.tests.TestsGerman.GermanEuroCustomizer;
+
+import java.util.Locale;
+
+/**
+ * de: German/qwerty
+ */
+@SmallTest
+public final class TestsGermanQwerty extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("de");
+ private static final LayoutBase LAYOUT = new Qwerty(new GermanEuroCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java
new file mode 100644
index 000000000..4acb119ac
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Greek;
+import com.android.inputmethod.keyboard.layout.Greek.GreekCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * el: Greek/greek
+ */
+@SmallTest
+public class TestsGreek extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("el");
+ private static final LayoutBase LAYOUT = new Greek(new GreekCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java
new file mode 100644
index 000000000..c0243a870
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Hebrew;
+import com.android.inputmethod.keyboard.layout.Hebrew.HebrewCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * iw: Hebrew/hebrew
+ */
+@SmallTest
+public class TestsHebrew extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("iw");
+ private static final LayoutBase LAYOUT = new Hebrew(new HebrewCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java
new file mode 100644
index 000000000..84053b5be
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Hindi;
+import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * hi: Hindi/hindi
+ */
+@SmallTest
+public final class TestsHindi extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("hi");
+ private static final LayoutBase LAYOUT = new Hindi(new HindiCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java
new file mode 100644
index 000000000..2e676df26
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.HindiCompact;
+import com.android.inputmethod.keyboard.layout.HindiCompact.HindiCompactCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * hi: Hindi/hindi_compact
+ */
+@SmallTest
+public final class TestsHindiCompact extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("hi");
+ private static final LayoutBase LAYOUT = new HindiCompact(new HindiCompactCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java
new file mode 100644
index 000000000..efc95dcf9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * hu: Hungarian/qwertz
+ */
+@SmallTest
+public final class TestsHungarian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("hu");
+ private static final LayoutBase LAYOUT = new Qwertz(new HungarianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class HungarianCustomizer extends LayoutCustomizer {
+ public HungarianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u0171", "\u00FB", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EE", "\u00EF", "\u00EC", "\u012F", "\u012B")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u0151", "\u00F4", "\u00F2", "\u00F5", "\u0153",
+ "\u00F8", "\u014D")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E0", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java
new file mode 100644
index 000000000..62b111e6a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * is: Icelandic/qwerty
+ */
+@SmallTest
+public final class TestsIcelandic extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("is");
+ private static final LayoutBase LAYOUT = new Qwerty(new IcelandicCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class IcelandicCustomizer extends LayoutCustomizer {
+ public IcelandicCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00EB", "\u00E8", "\u00EA", "\u0119", "\u0117", "\u0113")
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ .setMoreKeysOf("t", "\u00FE")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00FB", "\u00F9", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00ED", "\u00EF", "\u00EE", "\u00EC", "\u012F", "\u012B")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E4", "\u00E6", "\u00E5", "\u00E0", "\u00E2", "\u00E3",
+ "\u0101")
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ .setMoreKeysOf("d", "\u00F0");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java
new file mode 100644
index 000000000..9b23bfe2b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * in: Indonesian/qwerty # "id" is the official language code of Indonesian.
+ */
+@SmallTest
+public final class TestsIndonesian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("in");
+ private static final LayoutBase LAYOUT = new Qwerty(new LayoutCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java
new file mode 100644
index 000000000..f3c610c8b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * it: Italian/qwerty
+ */
+@SmallTest
+public final class TestsItalian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("it");
+ private static final LayoutBase LAYOUT = new Qwerty(new ItalianITCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class ItalianITCustomizer extends EuroCustomizer {
+ private final ItalianCustomizer mItalianCustomizer;
+
+ public ItalianITCustomizer(final Locale locale) {
+ super(locale);
+ mItalianCustomizer = new ItalianCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mItalianCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java
new file mode 100644
index 000000000..d32f9e957
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * it_CH: Italian (Switzerland)/swiss
+ */
+@SmallTest
+public final class TestsItalianCH extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("it", "CH");
+ private static final LayoutBase LAYOUT = new Swiss(new ItalianCHCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class ItalianCHCustomizer extends ItalianCustomizer {
+ public ItalianCHCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ super.setAccentedLetters(builder);
+ return builder
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ .replaceKeyOfLabel(Swiss.ROW1_11, key("\u00FC", moreKey("\u00E8")))
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ .replaceKeyOfLabel(Swiss.ROW2_10, key("\u00F6", moreKey("\u00E9")))
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ .replaceKeyOfLabel(Swiss.ROW2_11, key("\u00E4", moreKey("\u00E0")));
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java
new file mode 100644
index 000000000..d1866e803
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Kannada;
+import com.android.inputmethod.keyboard.layout.Kannada.KannadaCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * kn_IN: Kannada (India)/kannada
+ */
+@SmallTest
+public final class TestsKannadaIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("kn", "IN");
+ private static final LayoutBase LAYOUT = new Kannada(new KannadaCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java
new file mode 100644
index 000000000..d255a0fa9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic;
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * kk: Kazakh/east_slavic
+ */
+@SmallTest
+public final class TestsKazakh extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("kk");
+ private static final LayoutBase LAYOUT = new EastSlavic(new KazakhCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class KazakhCustomizer extends EastSlavicCustomizer {
+ public KazakhCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
+ // U+04B1: "ұ" CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+ .setMoreKeysOf("\u0443", "\u04AF", "\u04B1")
+ // U+043A: "к" CYRILLIC SMALL LETTER KA
+ // U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER
+ .setMoreKeysOf("\u043A", "\u049B")
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ .setMoreKeysOf("\u0435", "\u0451")
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
+ .setMoreKeysOf("\u043D", "\u04A3")
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ // U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE
+ .setMoreKeysOf("\u0433", "\u0493")
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ .replaceKeyOfLabel(EastSlavic.ROW1_9, key("\u0449", additionalMoreKey("9")))
+ // U+044B: "ы" CYRILLIC SMALL LETTER YERU
+ // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ .replaceKeyOfLabel(EastSlavic.ROW2_2, key("\u044B", moreKey("\u0456")))
+ // U+0430: "а" CYRILLIC SMALL LETTER A
+ // U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA
+ .setMoreKeysOf("\u0430", "\u04D9")
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
+ .setMoreKeysOf("\u043E", "\u04E9")
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA
+ .replaceKeyOfLabel(EastSlavic.ROW2_11, key("\u044D", moreKey("\u04BB")))
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ .replaceKeyOfLabel(EastSlavic.ROW3_5, "\u0438")
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ .setMoreKeysOf("\u044C", "\u044A");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java
new file mode 100644
index 000000000..df2f40d86
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Khmer;
+import com.android.inputmethod.keyboard.layout.Khmer.KhmerCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * km_KH: Khmer (Cambodia)/khmer
+ */
+@SmallTest
+public final class TestsKhmerKH extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("km", "KH");
+ private static final LayoutBase LAYOUT = new Khmer(new KhmerCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java
new file mode 100644
index 000000000..9797b4ba9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic;
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * ky: Kyrgyz/east_slavic
+ */
+@SmallTest
+public final class TestsKyrgyz extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ky");
+ private static final LayoutBase LAYOUT = new EastSlavic(new KyrgyzCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class KyrgyzCustomizer extends EastSlavicCustomizer {
+ public KyrgyzCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0443: "у" CYRILLIC SMALL LETTER U
+ // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
+ .setMoreKeysOf("\u0443", "\u04AF")
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ .setMoreKeysOf("\u0435", "\u0451")
+ // U+043D: "н" CYRILLIC SMALL LETTER EN
+ // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
+ .setMoreKeysOf("\u043D", "\u04A3")
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ .replaceKeyOfLabel(EastSlavic.ROW1_9, key("\u0449", additionalMoreKey("9")))
+ // U+044B: "ы" CYRILLIC SMALL LETTER YERU
+ .replaceKeyOfLabel(EastSlavic.ROW2_2, "\u044B")
+ // U+043E: "о" CYRILLIC SMALL LETTER O
+ // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
+ .setMoreKeysOf("\u043E", "\u04E9")
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ .replaceKeyOfLabel(EastSlavic.ROW2_11, "\u044D")
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ .replaceKeyOfLabel(EastSlavic.ROW3_5, "\u0438")
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ .setMoreKeysOf("\u044C", "\u044A");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java
new file mode 100644
index 000000000..34ad1fb7f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Lao;
+import com.android.inputmethod.keyboard.layout.Lao.LaoCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * lo_LA: Lao (Laos)/lao
+ */
+@SmallTest
+public final class TestsLaoLA extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("lo", "LA");
+ private static final LayoutBase LAYOUT = new Lao(new LaoCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java
new file mode 100644
index 000000000..dc1736c6d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * lv: Latvian/qwerty
+ */
+@SmallTest
+public final class TestsLatvian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("lv");
+ private static final LayoutBase LAYOUT = new Qwerty(new LatvianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class LatvianCustomizer extends LayoutCustomizer {
+ public LatvianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ .setMoreKeysOf("e",
+ "\u0113", "\u0117", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0119",
+ "\u011B")
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ .setMoreKeysOf("r", "\u0157", "\u0159", "\u0155")
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ .setMoreKeysOf("t", "\u0163", "\u0165")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ .setMoreKeysOf("u",
+ "\u016B", "\u0173", "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u016F",
+ "\u0171")
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ .setMoreKeysOf("i",
+ "\u012B", "\u012F", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u0131")
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ .setMoreKeysOf("o",
+ "\u00F2", "\u00F3", "\u00F4", "\u00F5", "\u00F6", "\u0153", "\u0151",
+ "\u00F8")
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ .setMoreKeysOf("a",
+ "\u0101", "\u00E0", "\u00E1", "\u00E2", "\u00E3", "\u00E4", "\u00E5",
+ "\u00E6", "\u0105")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B", "\u015F")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u010F")
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u0123", "\u011F")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ .setMoreKeysOf("k", "\u0137")
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ .setMoreKeysOf("l", "\u013C", "\u0142", "\u013A", "\u013E")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ .setMoreKeysOf("z", "\u017E", "\u017C", "\u017A")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u00E7", "\u0107")
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u0146", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java
new file mode 100644
index 000000000..55ac37a37
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * lt: Lithuanian/qwerty
+ */
+@SmallTest
+public final class TestsLithuanian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("lt");
+ private static final LayoutBase LAYOUT = new Qwerty(new LithuanianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class LithuanianCustomizer extends LayoutCustomizer {
+ public LithuanianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ .setMoreKeysOf("e",
+ "\u0117", "\u0119", "\u0113", "\u00E8", "\u00E9", "\u00EA", "\u00EB",
+ "\u011B")
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ .setMoreKeysOf("r", "\u0157", "\u0159", "\u0155")
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ .setMoreKeysOf("t", "\u0163", "\u0165")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ .setMoreKeysOf("u",
+ "\u016B", "\u0173", "\u00FC", "\u016B", "\u00F9", "\u00FA", "\u00FB",
+ "\u016F", "\u0171")
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ .setMoreKeysOf("i",
+ "\u012F", "\u012B", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u0131")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ .setMoreKeysOf("o",
+ "\u00F6", "\u00F5", "\u00F2", "\u00F3", "\u00F4", "\u0153", "\u0151",
+ "\u00F8")
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ .setMoreKeysOf("a",
+ "\u0105", "\u00E4", "\u0101", "\u00E0", "\u00E1", "\u00E2", "\u00E3",
+ "\u00E5", "\u00E6")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B", "\u015F")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u010F")
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u0123", "\u011F")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ .setMoreKeysOf("k", "\u0137")
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ .setMoreKeysOf("l", "\u013C", "\u0142", "\u013A", "\u013E")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ .setMoreKeysOf("z", "\u017E", "\u017C", "\u017A")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u00E7", "\u0107")
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u0146", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java
new file mode 100644
index 000000000..1d7d85650
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.SouthSlavic;
+import com.android.inputmethod.keyboard.layout.SouthSlavic.SouthSlavicLayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * mk: Macedonian/south_slavic
+ */
+@SmallTest
+public final class TestsMacedonian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("mk");
+ private static final LayoutBase LAYOUT = new SouthSlavic(new MacedonianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class MacedonianCustomizer extends SouthSlavicLayoutCustomizer {
+ public MacedonianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
+ .setMoreKeysOf("\u0435", "\u0450")
+ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
+ .replaceKeyOfLabel(SouthSlavic.ROW1_6, key("\u0455", additionalMoreKey("6")))
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
+ .setMoreKeysOf("\u0438", "\u045D")
+ // U+045C: "ќ" CYRILLIC SMALL LETTER KJE
+ .replaceKeyOfLabel(SouthSlavic.ROW2_11, "\u045C")
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ .replaceKeyOfLabel(SouthSlavic.ROW3_1, "\u0437")
+ // U+0453: "ѓ" CYRILLIC SMALL LETTER GJE
+ .replaceKeyOfLabel(SouthSlavic.ROW3_8, "\u0453");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java
new file mode 100644
index 000000000..9792af9d0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * ms_MY: Malay (Malaysia)/qwerty
+ */
+@SmallTest
+public final class TestsMalayMY extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ms", "MY");
+ private static final LayoutBase LAYOUT = new Qwerty(new LayoutCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java
new file mode 100644
index 000000000..f937de89a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Malayalam;
+import com.android.inputmethod.keyboard.layout.Malayalam.MalayalamCustomizer;
+
+import java.util.Locale;
+
+/**
+ * ta_IN: Malayalam (India)/malayalam
+ */
+@SmallTest
+public final class TestsMalayalamIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ml", "IN");
+ private static final LayoutBase LAYOUT = new Malayalam(new MalayalamCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java
new file mode 100644
index 000000000..b937629b0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Marathi;
+import com.android.inputmethod.keyboard.layout.Marathi.MarathiCustomizer;
+
+import java.util.Locale;
+
+/**
+ * mr_IN: Marathi (India)/marathi
+ */
+@SmallTest
+public final class TestsMarathiIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("mr", "IN");
+ private static final LayoutBase LAYOUT = new Marathi(new MarathiCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java
new file mode 100644
index 000000000..e28e962f9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Mongolian;
+import com.android.inputmethod.keyboard.layout.Mongolian.MongolianMNCustomizer;
+
+import java.util.Locale;
+
+/**
+ * mn_MN: Mongolian (Mongolia)/mongolian
+ */
+@SmallTest
+public final class TestsMongolianMN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("mn", "MN");
+ private static final LayoutBase LAYOUT = new Mongolian(new MongolianMNCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java
new file mode 100644
index 000000000..e6d3b3b92
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Myanmar;
+import com.android.inputmethod.keyboard.layout.Myanmar.MyanmarCustomizer;
+
+import java.util.Locale;
+
+/**
+ * my_MM: Myanmar (Myanmar)/myanmar
+ */
+@SmallTest
+public final class TestsMyanmarMM extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("my", "MM");
+ private static final LayoutBase LAYOUT = new Myanmar(new MyanmarCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java
new file mode 100644
index 000000000..971976aec
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.NepaliRomanized;
+import com.android.inputmethod.keyboard.layout.NepaliRomanized.NepaliRomanizedCustomizer;
+
+import java.util.Locale;
+
+/**
+ * ne_NP: Nepali (Nepal) Romanized/nepali_romanized
+ */
+@SmallTest
+public final class TestsNepaliRomanized extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ne", "NP");
+ private static final LayoutBase LAYOUT = new NepaliRomanized(
+ new NepaliRomanizedCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java
new file mode 100644
index 000000000..724c4304f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.NepaliTraditional;
+import com.android.inputmethod.keyboard.layout.NepaliTraditional.NepaliTraditionalCustomizer;
+
+import java.util.Locale;
+
+/**
+ * ne_NP: Nepali (Nepal) Traditional/nepali_traditional
+ */
+@SmallTest
+public final class TestsNepaliTraditional extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ne", "NP");
+ private static final LayoutBase LAYOUT = new NepaliTraditional(
+ new NepaliTraditionalCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
new file mode 100644
index 000000000..3ed63153a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/qwerty
+ */
+@SmallTest
+public final class TestsNoLanguage extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("zz");
+ private static final LayoutBase LAYOUT = new Qwerty(new NoLanguageCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
new file mode 100644
index 000000000..8d627e3b4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Colemak;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/colemak
+ */
+@SmallTest
+public final class TestsNoLanguageColemak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("zz");
+ private static final LayoutBase LAYOUT = new Colemak(new NoLanguageColemakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class NoLanguageColemakCustomizer extends LayoutCustomizer {
+ private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+ public NoLanguageColemakCustomizer(final Locale locale) {
+ super(locale);
+ mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mNoLanguageCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
new file mode 100644
index 000000000..9bf47ed42
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Dvorak;
+import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/dvorak
+ */
+@SmallTest
+public final class TestsNoLanguageDvorak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("zz");
+ private static final LayoutBase LAYOUT = new Dvorak(new NoLanguageDvorakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class NoLanguageDvorakCustomizer extends DvorakCustomizer {
+ private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+ public NoLanguageDvorakCustomizer(final Locale locale) {
+ super(locale);
+ mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mNoLanguageCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
new file mode 100644
index 000000000..cd8d43ca8
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.PcQwerty;
+import com.android.inputmethod.keyboard.layout.PcQwerty.PcQwertyCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * zz: Alphabet/pcqwerty
+ */
+@SmallTest
+public final class TestsNoLanguagePcQwerty extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("zz");
+ private static final LayoutBase LAYOUT = new PcQwerty(new NoLanguagePcQwertyCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class NoLanguagePcQwertyCustomizer extends PcQwertyCustomizer {
+ private final NoLanguageCustomizer mNoLanguageCustomizer;
+
+ public NoLanguagePcQwertyCustomizer(final Locale locale) {
+ super(locale);
+ mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return mNoLanguageCustomizer.setAccentedLetters(builder);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java
new file mode 100644
index 000000000..5d220dfa1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * nb: Norwegian Bokmål/nordic
+ */
+@SmallTest
+public final class TestsNorwegian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("nb");
+ private static final LayoutBase LAYOUT = new Nordic(new NorwegianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class NorwegianCustomizer extends LayoutCustomizer {
+ public NorwegianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B")
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F4", "\u00F2", "\u00F3", "\u00F6", "\u00F5", "\u0153", "\u014D")
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ .replaceKeyOfLabel(Nordic.ROW1_11, "\u00E5")
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW2_10, key("\u00F8", moreKey("\u00F6")))
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ .replaceKeyOfLabel(Nordic.ROW2_11, key("\u00E6", moreKey("\u00E4")))
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a", "\u00E0", "\u00E4", "\u00E1", "\u00E2", "\u00E3", "\u0101");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java
new file mode 100644
index 000000000..b7d75c9f0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Farsi;
+import com.android.inputmethod.keyboard.layout.Farsi.FarsiCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+
+import java.util.Locale;
+
+/**
+ * fa: Persian/farsi
+ */
+@SmallTest
+public class TestsPersian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("fa");
+ private static final LayoutBase LAYOUT = new Farsi(new FarsiCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java
new file mode 100644
index 000000000..04f88c3fc
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * pl: Polish/qwerty
+ */
+@SmallTest
+public final class TestsPolish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("pl");
+ private static final LayoutBase LAYOUT = new Qwerty(new PolishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class PolishCustomizer extends LayoutCustomizer {
+ public PolishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e",
+ "\u0119", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0117", "\u0113")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+ "\u014D")
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u0105", "\u00E1", "\u00E0", "\u00E2", "\u00E4", "\u00E6", "\u00E3",
+ "\u00E5", "\u0101")
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u015B", "\u00DF", "\u0161")
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u0142")
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ .setMoreKeysOf("z", "\u017C", "\u017A", "\u017E")
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u0107", "\u00E7", "\u010D")
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ .setMoreKeysOf("n", "\u0144", "\u00F1");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java
new file mode 100644
index 000000000..8a984a765
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * pt_BR: Portuguese (Brazil)/qwerty
+ */
+@SmallTest
+public class TestsPortugueseBR extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("pt", "BR");
+ private static final LayoutBase LAYOUT = new Qwerty(new PortugueseCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java
new file mode 100644
index 000000000..e15e811db
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * pt_PT: Portuguese (Portugal)/qwerty
+ */
+@SmallTest
+public final class TestsPortuguesePT extends TestsPortugueseBR {
+ private static final Locale LOCALE = new Locale("pt", "PT");
+ private static final LayoutBase LAYOUT = new Qwerty(new PortuguesePTCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class PortuguesePTCustomizer extends PortugueseCustomizer {
+ private final EuroCustomizer mEuroCustomizer;
+
+ public PortuguesePTCustomizer(final Locale locale) {
+ super(locale);
+ mEuroCustomizer = new EuroCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return mEuroCustomizer.getOtherCurrencyKeys();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java
new file mode 100644
index 000000000..0207f1c22
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * ro: Romanian/qwerty
+ */
+@SmallTest
+public final class TestsRomanian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ro");
+ private static final LayoutBase LAYOUT = new Qwerty(new RomanianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class RomanianCustomizer extends LayoutCustomizer {
+ public RomanianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
+ .setMoreKeysOf("t", "\u021B")
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i", "\u00EE", "\u00EF", "\u00EC", "\u00ED", "\u012F", "\u012B")
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E2", "\u00E3", "\u0103", "\u00E0", "\u00E1", "\u00E4", "\u00E6",
+ "\u00E5", "\u0101")
+ // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u0219", "\u00DF", "\u015B", "\u0161");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java
new file mode 100644
index 000000000..9919207ed
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic;
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * ru: Russian/east_slavic
+ */
+@SmallTest
+public final class TestsRussian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ru");
+ private static final LayoutBase LAYOUT = new EastSlavic(new RussianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class RussianCustomizer extends EastSlavicCustomizer {
+ public RussianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ .setMoreKeysOf("\u0435", "\u0451")
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ .replaceKeyOfLabel(EastSlavic.ROW1_9, key("\u0449", additionalMoreKey("9")))
+ // U+044B: "ы" CYRILLIC SMALL LETTER YERU
+ .replaceKeyOfLabel(EastSlavic.ROW2_2, "\u044B")
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ .replaceKeyOfLabel(EastSlavic.ROW2_11, "\u044D")
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ .replaceKeyOfLabel(EastSlavic.ROW3_5, "\u0438")
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ .setMoreKeysOf("\u044C", "\u044A");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java
new file mode 100644
index 000000000..41f1690f3
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.SouthSlavic;
+import com.android.inputmethod.keyboard.layout.SouthSlavic.SouthSlavicLayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * sr: Serbian/south_slavic
+ */
+@SmallTest
+public final class TestsSerbian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("sr");
+ private static final LayoutBase LAYOUT = new SouthSlavic(new SerbianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SerbianCustomizer extends SouthSlavicLayoutCustomizer {
+ public SerbianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0435: "е" CYRILLIC SMALL LETTER IE
+ // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
+ .setMoreKeysOf("\u0435", "\u0450")
+ // U+0437: "з" CYRILLIC SMALL LETTER ZE
+ .replaceKeyOfLabel(SouthSlavic.ROW1_6, key("\u0437", additionalMoreKey("6")))
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
+ .setMoreKeysOf("\u0438", "\u045D")
+ // U+045B: "ћ" CYRILLIC SMALL LETTER TSHE
+ .replaceKeyOfLabel(SouthSlavic.ROW2_11, "\u045B")
+ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
+ .replaceKeyOfLabel(SouthSlavic.ROW3_1, "\u0455")
+ // U+0452: "ђ" CYRILLIC SMALL LETTER DJE
+ .replaceKeyOfLabel(SouthSlavic.ROW3_8, "\u0452");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java
new file mode 100644
index 000000000..1cea49760
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Sinhala;
+import com.android.inputmethod.keyboard.layout.Sinhala.SinhalaCustomizer;
+
+import java.util.Locale;
+
+/**
+ * si_LK: Sinhala (Sri Lanka)/sinhala
+ */
+@SmallTest
+public final class TestsSinhalaLK extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("si", "LK");
+ private static final LayoutBase LAYOUT = new Sinhala(new SinhalaCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java
new file mode 100644
index 000000000..bdaf0cad1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * sk: Slovak/qwerty
+ */
+@SmallTest
+public final class TestsSlovak extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("sk");
+ private static final LayoutBase LAYOUT = new Qwerty(new SlovakCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SlovakCustomizer extends EuroCustomizer {
+ public SlovakCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ .setMoreKeysOf("e",
+ "\u00E9", "\u011B", "\u0113", "\u0117", "\u00E8", "\u00EA", "\u00EB",
+ "\u0119")
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ .setMoreKeysOf("r", "\u0155", "\u0159", "\u0157")
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ .setMoreKeysOf("t", "\u0165", "\u0163")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ .setMoreKeysOf("u",
+ "\u00FA", "\u016F", "\u00FC", "\u016B", "\u0173", "\u00F9", "\u00FB",
+ "\u0171")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ .setMoreKeysOf("i",
+ "\u00ED", "\u012B", "\u012F", "\u00EC", "\u00EE", "\u00EF", "\u0131")
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ .setMoreKeysOf("o",
+ "\u00F4", "\u00F3", "\u00F6", "\u00F2", "\u00F5", "\u0153", "\u0151",
+ "\u00F8")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ .setMoreKeysOf("a",
+ "\u00E1", "\u00E4", "\u0101", "\u00E0", "\u00E2", "\u00E3", "\u00E5",
+ "\u00E6", "\u0105")
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ .setMoreKeysOf("s", "\u0161", "\u00DF", "\u015B", "\u015F")
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u010F")
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u0123", "\u011F")
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ .setMoreKeysOf("k", "\u0137")
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u013E", "\u013A", "\u013C", "\u0142")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ .setMoreKeysOf("z", "\u017E", "\u017C", "\u017A")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u00E7", "\u0107")
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ .setMoreKeysOf("n", "\u0148", "\u0146", "\u00F1", "\u0144");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java
new file mode 100644
index 000000000..cdb1beeba
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * sl: Slovenian/qwerty
+ */
+@SmallTest
+public final class TestsSlovenian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("sl");
+ private static final LayoutBase LAYOUT = new Qwerty(new SlovenianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SlovenianCustomizer extends EuroCustomizer {
+ public SlovenianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u0161")
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ .setMoreKeysOf("d", "\u0111")
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ .setMoreKeysOf("z", "\u017E")
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ .setMoreKeysOf("c", "\u010D", "\u0107");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java
new file mode 100644
index 000000000..12e8676ae
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * es: Spanish/spanish
+ */
+@SmallTest
+public class TestsSpanish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("es");
+ private static final LayoutBase LAYOUT = new Spanish(new SpanishESCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SpanishESCustomizer extends SpanishCustomizer {
+ private final EuroCustomizer mEuroCustomizer;
+
+ public SpanishESCustomizer(final Locale locale) {
+ super(locale);
+ mEuroCustomizer = new EuroCustomizer(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return mEuroCustomizer.getOtherCurrencyKeys();
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
new file mode 100644
index 000000000..75aad136f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Spanish;
+
+import java.util.Locale;
+
+/**
+ * es_419: Spanish (Latin America)/spanish
+ */
+@SmallTest
+public class TestsSpanish419 extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("es", "419");
+ private static final LayoutBase LAYOUT = new Spanish(new SpanishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java
new file mode 100644
index 000000000..c3ac0a0c0
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Spanish;
+
+import java.util.Locale;
+
+/**
+ * es_US: Spanish (United States)/spanish
+ */
+@SmallTest
+public class TestsSpanishUS extends TestsSpanish {
+ private static final Locale LOCALE = new Locale("es", "US");
+ private static final LayoutBase LAYOUT = new Spanish(new SpanishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java
new file mode 100644
index 000000000..13b974194
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * sw: Swahili/qwerty
+ */
+@SmallTest
+public final class TestsSwahili extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("sw");
+ private static final LayoutBase LAYOUT = new Qwerty(new SwahiliCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SwahiliCustomizer extends LayoutCustomizer {
+ public SwahiliCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113")
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FB", "\u00FC", "\u00F9", "\u00FA", "\u016B")
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ .setMoreKeysOf("i", "\u00EE", "\u00EF", "\u00ED", "\u012B", "\u00EC")
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ .setMoreKeysOf("o",
+ "\u00F4", "\u00F6", "\u00F2", "\u00F3", "\u0153", "\u00F8", "\u014D",
+ "\u00F5")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ .setMoreKeysOf("s", "\u00DF")
+ .setMoreKeysOf("g", "g'")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ .setMoreKeysOf("c", "\u00E7")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ .setMoreKeysOf("n", "\u00F1");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java
new file mode 100644
index 000000000..9b58914a2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * sv: Swedish/nordic
+ */
+@SmallTest
+public final class TestsSwedish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("sv");
+ private static final LayoutBase LAYOUT = new Nordic(new SwedishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class SwedishCustomizer extends EuroCustomizer {
+ public SwedishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_RL; }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ .setMoreKeysOf("e", "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0119")
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ .setMoreKeysOf("r", "\u0159")
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ .setMoreKeysOf("t", "\u0165", "\u00FE")
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ .setMoreKeysOf("y", "\u00FD", "\u00FF")
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FC", "\u00FA", "\u00F9", "\u00FB", "\u016B")
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ .setMoreKeysOf("i", "\u00ED", "\u00EC", "\u00EE", "\u00EF")
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o", "\u00F3", "\u00F2", "\u00F4", "\u00F5", "\u014D")
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ .replaceKeyOfLabel(Nordic.ROW1_11, "\u00E5")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ .replaceKeyOfLabel(Nordic.ROW2_10,
+ key("\u00F6", joinMoreKeys("\u00F8", "\u0153")))
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ .replaceKeyOfLabel(Nordic.ROW2_11, key("\u00E4", moreKey("\u00E6")))
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ .setMoreKeysOf("a", "\u00E1", "\u00E0", "\u00E2", "\u0105", "\u00E3")
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ .setMoreKeysOf("s", "\u015B", "\u0161", "\u015F", "\u00DF")
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ .setMoreKeysOf("d", "\u00F0", "\u010F")
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ .setMoreKeysOf("l", "\u0142")
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ .setMoreKeysOf("z", "\u017A", "\u017E", "\u017C")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ .setMoreKeysOf("n", "\u0144", "\u00F1", "\u0148");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java
new file mode 100644
index 000000000..38d5364e5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * tl: Tagalog/spanish
+ */
+@SmallTest
+public class TestsTagalog extends TestsSpanish {
+ private static final Locale LOCALE = new Locale("tl");
+ private static final LayoutBase LAYOUT = new Spanish(new TagalogCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class TagalogCustomizer extends SpanishCustomizer {
+
+ public TagalogCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+ return isPhone ? LayoutBase.PHONE_PUNCTUATION_MORE_KEYS
+ : LayoutBase.TABLET_PUNCTUATION_MORE_KEYS;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java
new file mode 100644
index 000000000..5b3649d2b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Tamil;
+import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer;
+
+import java.util.Locale;
+
+/**
+ * ta_IN: Tamil (India)/tamil
+ */
+@SmallTest
+public final class TestsTamilIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("ta", "IN");
+ private static final LayoutBase LAYOUT = new Tamil(new TamilCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java
new file mode 100644
index 000000000..04996d9aa
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Telugu;
+import com.android.inputmethod.keyboard.layout.Telugu.TeluguCustomizer;
+
+import java.util.Locale;
+
+/**
+ * te_IN: Telugu (India)/telugu
+ */
+@SmallTest
+public final class TestsTeluguIN extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("te", "IN");
+ private static final LayoutBase LAYOUT = new Telugu(new TeluguCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java
new file mode 100644
index 000000000..3c8727290
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Thai;
+import com.android.inputmethod.keyboard.layout.Thai.ThaiCustomizer;
+
+import java.util.Locale;
+
+/**
+ * th: Thai/thai
+ */
+@SmallTest
+public final class TestsThai extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("th");
+ private static final LayoutBase LAYOUT = new Thai(new ThaiCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java
new file mode 100644
index 000000000..b35f8850a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * tr: Turkish/qwerty
+ */
+@SmallTest
+public final class TestsTurkish extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("tr");
+ private static final LayoutBase LAYOUT = new Qwerty(new TurkishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class TurkishCustomizer extends EuroCustomizer {
+ public TurkishCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B")
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ .setMoreKeysOf("i",
+ "\u0131", "\u00EE", "\u00EF", "\u00EC", "\u00ED", "\u012F", "\u012B")
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ .setMoreKeysOf("o",
+ "\u00F6", "\u00F4", "\u0153", "\u00F2", "\u00F3", "\u00F5", "\u00F8",
+ "\u014D")
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ .setMoreKeysOf("a", "\u00E2")
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ .setMoreKeysOf("s", "\u015F", "\u00DF", "\u015B", "\u0161")
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ .setMoreKeysOf("g", "\u011F")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java
new file mode 100644
index 000000000..a6bcacc9e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.EastSlavic;
+import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * uk: Ukrainian/east_slavic
+ */
+@SmallTest
+public final class TestsUkrainian extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("uk");
+ private static final LayoutBase LAYOUT = new EastSlavic(new UkrainianCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class UkrainianCustomizer extends EastSlavicCustomizer {
+ public UkrainianCustomizer(final Locale locale) { super(locale); }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_HRYVNIA; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ @Override
+ public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
+
+ @Override
+ public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_R9L; }
+
+ // U+20B4: "₴" HRYVNIA SIGN
+ private static final ExpectedKey CURRENCY_HRYVNIA = key("\u20B4",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+0433: "г" CYRILLIC SMALL LETTER GHE
+ // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN
+ .setMoreKeysOf("\u0433", "\u0491")
+ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
+ .replaceKeyOfLabel(EastSlavic.ROW1_9, key("\u0449", additionalMoreKey("9")))
+ // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ // U+0457: "ї" CYRILLIC SMALL LETTER YI
+ .replaceKeyOfLabel(EastSlavic.ROW2_2, key("\u0456", moreKey("\u0457")))
+ // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE
+ .replaceKeyOfLabel(EastSlavic.ROW2_11, "\u0454")
+ // U+0438: "и" CYRILLIC SMALL LETTER I
+ .replaceKeyOfLabel(EastSlavic.ROW3_5, "\u0438")
+ // U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ .setMoreKeysOf("\u044C", "\u044A");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java
new file mode 100644
index 000000000..83d86ac4d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * vi: Vietnamese/qwerty
+ */
+@SmallTest
+public final class TestsVietnamese extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("vi");
+ private static final LayoutBase LAYOUT = new Qwerty(new VietnameseCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+
+ private static class VietnameseCustomizer extends LayoutCustomizer {
+ public VietnameseCustomizer(final Locale locale) {
+ super(locale);
+ }
+
+ @Override
+ public ExpectedKey getCurrencyKey() { return CURRENCY_DONG; }
+
+ @Override
+ public ExpectedKey[] getOtherCurrencyKeys() {
+ return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+ }
+
+ // U+20AB: "₫" DONG SIGN
+ private static final ExpectedKey CURRENCY_DONG = key("\u20AB",
+ Symbols.CURRENCY_GENERIC_MORE_KEYS);
+
+ @Override
+ public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+1EBB: "ẻ" LATIN SMALL LETTER E WITH HOOK ABOVE
+ // U+1EBD: "ẽ" LATIN SMALL LETTER E WITH TILDE
+ // U+1EB9: "ẹ" LATIN SMALL LETTER E WITH DOT BELOW
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+1EC1: "ề" LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
+ // U+1EBF: "ế" LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
+ // U+1EC3: "ể" LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+ // U+1EC5: "ễ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
+ // U+1EC7: "ệ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+ .setMoreKeysOf("e",
+ "\u00E8", "\u00E9", "\u1EBB", "\u1EBD", "\u1EB9", "\u00EA", "\u1EC1",
+ "\u1EBF", "\u1EC3", "\u1EC5", "\u1EC7")
+ // U+1EF3: "ỳ" LATIN SMALL LETTER Y WITH GRAVE
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+1EF7: "ỷ" LATIN SMALL LETTER Y WITH HOOK ABOVE
+ // U+1EF9: "ỹ" LATIN SMALL LETTER Y WITH TILDE
+ // U+1EF5: "ỵ" LATIN SMALL LETTER Y WITH DOT BELOW
+ .setMoreKeysOf("y", "\u1EF3", "\u00FD", "\u1EF7", "\u1EF9", "\u1EF5")
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+1EE7: "ủ" LATIN SMALL LETTER U WITH HOOK ABOVE
+ // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
+ // U+1EE5: "ụ" LATIN SMALL LETTER U WITH DOT BELOW
+ // U+01B0: "ư" LATIN SMALL LETTER U WITH HORN
+ // U+1EEB: "ừ" LATIN SMALL LETTER U WITH HORN AND GRAVE
+ // U+1EE9: "ứ" LATIN SMALL LETTER U WITH HORN AND ACUTE
+ // U+1EED: "ử" LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
+ // U+1EEF: "ữ" LATIN SMALL LETTER U WITH HORN AND TILDE
+ // U+1EF1: "ự" LATIN SMALL LETTER U WITH HORN AND DOT BELOW
+ .setMoreKeysOf("u",
+ "\u00F9", "\u00FA", "\u1EE7", "\u0169", "\u1EE5", "\u01B0", "\u1EEB",
+ "\u1EE9", "\u1EED", "\u1EEF", "\u1EF1")
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+1EC9: "ỉ" LATIN SMALL LETTER I WITH HOOK ABOVE
+ // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
+ // U+1ECB: "ị" LATIN SMALL LETTER I WITH DOT BELOW
+ .setMoreKeysOf("i", "\u00EC", "\u00ED", "\u1EC9", "\u0129", "\u1ECB")
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+1ECF: "ỏ" LATIN SMALL LETTER O WITH HOOK ABOVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+1ECD: "ọ" LATIN SMALL LETTER O WITH DOT BELOW
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+1ED3: "ồ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
+ // U+1ED1: "ố" LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
+ // U+1ED5: "ổ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+ // U+1ED7: "ỗ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
+ // U+1ED9: "ộ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+ // U+01A1: "ơ" LATIN SMALL LETTER O WITH HORN
+ // U+1EDD: "ờ" LATIN SMALL LETTER O WITH HORN AND GRAVE
+ // U+1EDB: "ớ" LATIN SMALL LETTER O WITH HORN AND ACUTE
+ // U+1EDF: "ở" LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
+ // U+1EE1: "ỡ" LATIN SMALL LETTER O WITH HORN AND TILDE
+ // U+1EE3: "ợ" LATIN SMALL LETTER O WITH HORN AND DOT BELOW
+ .setMoreKeysOf("o",
+ "\u00F2", "\u00F3", "\u1ECF", "\u00F5", "\u1ECD", "\u00F4", "\u1ED3",
+ "\u1ED1", "\u1ED5", "\u1ED7", "\u1ED9", "\u01A1", "\u1EDD", "\u1EDB",
+ "\u1EDF", "\u1EE1", "\u1EE3")
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+1EA3: "ả" LATIN SMALL LETTER A WITH HOOK ABOVE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+1EA1: "ạ" LATIN SMALL LETTER A WITH DOT BELOW
+ // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+ // U+1EB1: "ằ" LATIN SMALL LETTER A WITH BREVE AND GRAVE
+ // U+1EAF: "ắ" LATIN SMALL LETTER A WITH BREVE AND ACUTE
+ // U+1EB3: "ẳ" LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
+ // U+1EB5: "ẵ" LATIN SMALL LETTER A WITH BREVE AND TILDE
+ // U+1EB7: "ặ" LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+1EA7: "ầ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
+ // U+1EA5: "ấ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
+ // U+1EA9: "ẩ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+ // U+1EAB: "ẫ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
+ // U+1EAD: "ậ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u1EA3", "\u00E3", "\u1EA1", "\u0103", "\u1EB1",
+ "\u1EAF", "\u1EB3", "\u1EB5", "\u1EB7", "\u00E2", "\u1EA7", "\u1EA5",
+ "\u1EA9", "\u1EAB", "\u1EAD")
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ .setMoreKeysOf("d", "\u0111");
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java
new file mode 100644
index 000000000..e048e92c2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+
+import java.util.Locale;
+
+/**
+ * zu: Zulu/qwerty
+ */
+@SmallTest
+public final class TestsZulu extends TestsEnglishUS {
+ private static final Locale LOCALE = new Locale("zu");
+ private static final LayoutBase LAYOUT = new Qwerty(new EnglishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java b/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java
new file mode 100644
index 000000000..c29257d34
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/AppWorkaroundsTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.settings.Settings;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION_CODES;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.inputmethod.EditorInfo;
+
+@LargeTest
+public class AppWorkaroundsTests extends InputTestsBase {
+ String packageNameOfAppBeforeJellyBean;
+ String packageNameOfAppAfterJellyBean;
+
+ @Override
+ protected void setUp() throws Exception {
+ // NOTE: this will fail if there is no app installed that targets an SDK
+ // before Jelly Bean. For the moment, it's fine.
+ final PackageManager pm = getContext().getPackageManager();
+ for (ApplicationInfo ai : pm.getInstalledApplications(0 /* flags */)) {
+ if (ai.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
+ packageNameOfAppBeforeJellyBean = ai.packageName;
+ } else {
+ packageNameOfAppAfterJellyBean = ai.packageName;
+ }
+ }
+ super.setUp();
+ }
+
+ // We want to test if the app package info is correctly retrieved by LatinIME. Since it
+ // asks this information to the package manager from the package name, and that it takes
+ // the package name from the EditorInfo, all we have to do it put the correct package
+ // name in the editor info.
+ // To this end, our base class InputTestsBase offers a hook for us to touch the EditorInfo.
+ // We override this hook to write the package name that we need.
+ @Override
+ protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
+ if ("testBeforeJellyBeanTrue".equals(getName())) {
+ ei.packageName = packageNameOfAppBeforeJellyBean;
+ } else if ("testBeforeJellyBeanFalse".equals(getName())) {
+ ei.packageName = packageNameOfAppAfterJellyBean;
+ }
+ return ei;
+ }
+
+ public void testBeforeJellyBeanTrue() {
+ assertTrue("Couldn't successfully detect this app targets < Jelly Bean (package is "
+ + packageNameOfAppBeforeJellyBean + ")",
+ Settings.getInstance().getCurrent().isBeforeJellyBean());
+ }
+
+ public void testBeforeJellyBeanFalse() {
+ assertFalse("Couldn't successfully detect this app targets >= Jelly Bean (package is "
+ + packageNameOfAppAfterJellyBean + ")",
+ Settings.getInstance().getCurrent().isBeforeJellyBean());
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index cd5384ea4..ae184268c 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -20,8 +20,18 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Pair;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.CodePointUtils;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FusionDictionary;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
+import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.utils.LocaleUtils;
import java.io.File;
import java.io.IOException;
@@ -30,68 +40,183 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.TimeUnit;
@LargeTest
public class BinaryDictionaryDecayingTests extends AndroidTestCase {
private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
private static final String TEST_LOCALE = "test";
-
- // Note that these are corresponding definitions in native code in
- // latinime::DynamicPatriciaTriePolicy.
- private static final String SET_NEEDS_TO_DECAY_FOR_TESTING_KEY =
- "SET_NEEDS_TO_DECAY_FOR_TESTING";
-
private static final int DUMMY_PROBABILITY = 0;
+ private static final int[] DICT_FORMAT_VERSIONS =
+ new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+
+ private int mCurrentTime = 0;
@Override
protected void setUp() throws Exception {
super.setUp();
+ mCurrentTime = 0;
}
@Override
protected void tearDown() throws Exception {
+ stopTestModeInNativeCode();
super.tearDown();
}
+ private static boolean supportsBeginningOfSentence(final int formatVersion) {
+ return formatVersion > FormatSpec.VERSION401;
+ }
+
+ private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word,
+ final int probability) {
+ binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */,
+ BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */,
+ false /* isBeginningOfSentence */, false /* isNotAWord */,
+ false /* isBlacklisted */, mCurrentTime /* timestamp */);
+ }
+
+ private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0,
+ final String word1, final int probability) {
+ binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability,
+ mCurrentTime /* timestamp */);
+ }
+
+ private static boolean isValidBigram(final BinaryDictionary binaryDictionary,
+ final String word0, final String word1) {
+ return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1);
+ }
+
private void forcePassingShortTime(final BinaryDictionary binaryDictionary) {
- // Entries having low probability would be suppressed once in 3 GCs.
- final int count = 3;
- for (int i = 0; i < count; i++) {
- binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY);
- binaryDictionary.flushWithGC();
- }
+ // 30 days.
+ final int timeToElapse = (int)TimeUnit.SECONDS.convert(30, TimeUnit.DAYS);
+ mCurrentTime += timeToElapse;
+ setCurrentTimeForTestMode(mCurrentTime);
+ binaryDictionary.flushWithGC();
}
private void forcePassingLongTime(final BinaryDictionary binaryDictionary) {
- // Currently, probabilities are decayed when GC is run. All entries that have never been
- // typed in 128 GCs would be removed.
- final int count = 128;
- for (int i = 0; i < count; i++) {
- binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY);
- binaryDictionary.flushWithGC();
+ // 365 days.
+ final int timeToElapse = (int)TimeUnit.SECONDS.convert(365, TimeUnit.DAYS);
+ mCurrentTime += timeToElapse;
+ setCurrentTimeForTestMode(mCurrentTime);
+ binaryDictionary.flushWithGC();
+ }
+
+ private File createEmptyDictionaryAndGetFile(final String dictId,
+ final int formatVersion) throws IOException {
+ if (formatVersion == FormatSpec.VERSION4
+ || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
+ || formatVersion == FormatSpec.VERSION4_DEV) {
+ return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
+ } else {
+ throw new IOException("Dictionary format version " + formatVersion
+ + " is not supported.");
}
}
- private File createEmptyDictionaryAndGetFile(final String filename) throws IOException {
- final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION,
+ private File createEmptyVer4DictionaryAndGetFile(final String dictId, final int formatVersion)
+ throws IOException {
+ final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION,
getContext().getCacheDir());
- Map<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
- 3 /* dictVersion */, attributeMap)) {
+ FileUtils.deleteRecursively(file);
+ Map<String, String> attributeMap = new HashMap<>();
+ attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId);
+ attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
+ String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+ attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion,
+ LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) {
return file;
} else {
- throw new IOException("Empty dictionary cannot be created.");
+ throw new IOException("Empty dictionary " + file.getAbsolutePath()
+ + " cannot be created. Foramt version: " + formatVersion);
+ }
+ }
+
+ private static int setCurrentTimeForTestMode(final int currentTime) {
+ return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime);
+ }
+
+ private static int stopTestModeInNativeCode() {
+ return BinaryDictionaryUtils.setCurrentTimeForTest(-1);
+ }
+
+ public void testReadDictInJavaSide() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testReadDictInJavaSide(formatVersion);
}
}
+ private void testReadDictInJavaSide(final int formatVersion) {
+ setCurrentTimeForTestMode(mCurrentTime);
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "ab", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "aaa", DUMMY_PROBABILITY);
+ binaryDictionary.flushWithGC();
+ binaryDictionary.close();
+
+ final DictDecoder dictDecoder =
+ BinaryDictIOUtils.getDictDecoder(dictFile, 0, dictFile.length());
+ try {
+ final FusionDictionary dict =
+ dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
+ PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "a");
+ assertNotNull(ptNode);
+ assertTrue(ptNode.isTerminal());
+ assertNotNull(ptNode.getBigram("aaa"));
+ ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "ab");
+ assertNotNull(ptNode);
+ assertTrue(ptNode.isTerminal());
+ ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa");
+ assertNotNull(ptNode);
+ assertTrue(ptNode.isTerminal());
+ } catch (IOException e) {
+ fail("IOException while reading dictionary: " + e);
+ } catch (UnsupportedFormatException e) {
+ fail("Unsupported format: " + e);
+ }
+ dictFile.delete();
+ }
+
+ public void testControlCurrentTime() {
+ final int TEST_COUNT = 1000;
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+ final int startTime = stopTestModeInNativeCode();
+ for (int i = 0; i < TEST_COUNT; i++) {
+ final int currentTime = random.nextInt(Integer.MAX_VALUE);
+ final int currentTimeInNativeCode = setCurrentTimeForTestMode(currentTime);
+ assertEquals(currentTime, currentTimeInNativeCode);
+ }
+ final int endTime = stopTestModeInNativeCode();
+ final int MAX_ALLOWED_ELAPSED_TIME = 10;
+ assertTrue(startTime <= endTime && endTime <= startTime + MAX_ALLOWED_ELAPSED_TIME);
+ }
+
public void testAddValidAndInvalidWords() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddValidAndInvalidWords(formatVersion);
+ }
+ }
+
+ private void testAddValidAndInvalidWords(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -99,46 +224,44 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
assertFalse(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
assertTrue(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
assertTrue(binaryDictionary.isValidWord("b"));
- final int unigramProbability = binaryDictionary.getFrequency("a");
- binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidBigram("a", "b"));
- binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidBigram("a", "b"));
- binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidBigram("a", "b"));
- binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY);
- assertTrue(binaryDictionary.isValidBigram("a", "b"));
+ addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY);
+ assertFalse(isValidBigram(binaryDictionary, "a", "b"));
+ addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "a", "b"));
- binaryDictionary.addUnigramWord("c", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "c", DUMMY_PROBABILITY);
- assertTrue(binaryDictionary.isValidBigram("a", "c"));
+ addUnigramWord(binaryDictionary, "c", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "c", DUMMY_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "a", "c"));
// Add bigrams of not valid unigrams.
- binaryDictionary.addBigramWords("x", "y", Dictionary.NOT_A_PROBABILITY);
- assertFalse(binaryDictionary.isValidBigram("x", "y"));
- binaryDictionary.addBigramWords("x", "y", DUMMY_PROBABILITY);
- assertFalse(binaryDictionary.isValidBigram("x", "y"));
+ addBigramWords(binaryDictionary, "x", "y", Dictionary.NOT_A_PROBABILITY);
+ assertFalse(isValidBigram(binaryDictionary, "x", "y"));
+ addBigramWords(binaryDictionary, "x", "y", DUMMY_PROBABILITY);
+ assertFalse(isValidBigram(binaryDictionary, "x", "y"));
binaryDictionary.close();
dictFile.delete();
}
public void testDecayingProbability() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testDecayingProbability(formatVersion);
+ }
+ }
+
+ private void testDecayingProbability(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -146,50 +269,53 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
assertTrue(binaryDictionary.isValidWord("a"));
forcePassingShortTime(binaryDictionary);
assertFalse(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ assertTrue(binaryDictionary.isValidWord("a"));
forcePassingShortTime(binaryDictionary);
assertTrue(binaryDictionary.isValidWord("a"));
forcePassingLongTime(binaryDictionary);
assertFalse(binaryDictionary.isValidWord("a"));
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY);
- assertTrue(binaryDictionary.isValidBigram("a", "b"));
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "a", "b"));
forcePassingShortTime(binaryDictionary);
- assertFalse(binaryDictionary.isValidBigram("a", "b"));
-
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY);
- assertTrue(binaryDictionary.isValidBigram("a", "b"));
+ assertFalse(isValidBigram(binaryDictionary, "a", "b"));
+
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "a", "b"));
forcePassingShortTime(binaryDictionary);
- assertTrue(binaryDictionary.isValidBigram("a", "b"));
+ assertTrue(isValidBigram(binaryDictionary, "a", "b"));
forcePassingLongTime(binaryDictionary);
- assertFalse(binaryDictionary.isValidBigram("a", "b"));
+ assertFalse(isValidBigram(binaryDictionary, "a", "b"));
binaryDictionary.close();
dictFile.delete();
}
public void testAddManyUnigramsToDecayingDict() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddManyUnigramsToDecayingDict(formatVersion);
+ }
+ }
+
+ private void testAddManyUnigramsToDecayingDict(final int formatVersion) {
final int unigramCount = 30000;
final int unigramTypedCount = 100000;
final int codePointSetSize = 50;
@@ -198,16 +324,17 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ setCurrentTimeForTestMode(mCurrentTime);
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final ArrayList<String> words = new ArrayList<String>();
+ final ArrayList<String> words = new ArrayList<>();
for (int i = 0; i < unigramCount; i++) {
final String word = CodePointUtils.generateWord(random, codePointSet);
@@ -215,32 +342,102 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
}
final int maxUnigramCount = Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY));
+ binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY));
for (int i = 0; i < unigramTypedCount; i++) {
final String word = words.get(random.nextInt(words.size()));
- binaryDictionary.addUnigramWord(word, DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
final int unigramCountBeforeGC =
- Integer.parseInt(binaryDictionary.getPropertyForTests(
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.UNIGRAM_COUNT_QUERY));
while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
- binaryDictionary.flushWithGC();
+ forcePassingShortTime(binaryDictionary);
}
final int unigramCountAfterGC =
- Integer.parseInt(binaryDictionary.getPropertyForTests(
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.UNIGRAM_COUNT_QUERY));
assertTrue(unigramCountBeforeGC > unigramCountAfterGC);
}
}
- assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+ assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.UNIGRAM_COUNT_QUERY)) > 0);
- assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+ assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount);
+ forcePassingLongTime(binaryDictionary);
+ assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.UNIGRAM_COUNT_QUERY)));
+ }
+
+ public void testOverflowUnigrams() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testOverflowUnigrams(formatVersion);
+ }
+ }
+
+ private void testOverflowUnigrams(final int formatVersion) {
+ final int unigramCount = 20000;
+ final int eachUnigramTypedCount = 2;
+ final int strongUnigramTypedCount = 20;
+ final int weakUnigramTypedCount = 1;
+ final int codePointSetSize = 50;
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ setCurrentTimeForTestMode(mCurrentTime);
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+ final String strong = "strong";
+ final String weak = "weak";
+ for (int j = 0; j < strongUnigramTypedCount; j++) {
+ addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY);
+ }
+ for (int j = 0; j < weakUnigramTypedCount; j++) {
+ addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY);
+ }
+ assertTrue(binaryDictionary.isValidWord(strong));
+ assertTrue(binaryDictionary.isValidWord(weak));
+
+ for (int i = 0; i < unigramCount; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ for (int j = 0; j < eachUnigramTypedCount; j++) {
+ addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
+ }
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ final int unigramCountBeforeGC =
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.UNIGRAM_COUNT_QUERY));
+ assertTrue(binaryDictionary.isValidWord(strong));
+ assertTrue(binaryDictionary.isValidWord(weak));
+ binaryDictionary.flushWithGC();
+ final int unigramCountAfterGC =
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.UNIGRAM_COUNT_QUERY));
+ assertTrue(unigramCountBeforeGC > unigramCountAfterGC);
+ assertFalse(binaryDictionary.isValidWord(weak));
+ assertTrue(binaryDictionary.isValidWord(strong));
+ break;
+ }
+ }
}
public void testAddManyBigramsToDecayingDict() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddManyBigramsToDecayingDict(formatVersion);
+ }
+ }
+
+ private void testAddManyBigramsToDecayingDict(final int formatVersion) {
final int unigramCount = 5000;
final int bigramCount = 30000;
final int bigramTypedCount = 100000;
@@ -250,17 +447,18 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ setCurrentTimeForTestMode(mCurrentTime);
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final ArrayList<String> words = new ArrayList<String>();
- final ArrayList<Pair<String, String>> bigrams = new ArrayList<Pair<String, String>>();
+ final ArrayList<String> words = new ArrayList<>();
+ final ArrayList<Pair<String, String>> bigrams = new ArrayList<>();
for (int i = 0; i < unigramCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
@@ -274,35 +472,221 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
}
final String word0 = words.get(word0Index);
final String word1 = words.get(word1Index);
- final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
bigrams.add(bigram);
}
final int maxBigramCount = Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
+ binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
for (int i = 0; i < bigramTypedCount; ++i) {
final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size()));
- binaryDictionary.addUnigramWord(bigram.first, DUMMY_PROBABILITY);
- binaryDictionary.addUnigramWord(bigram.second, DUMMY_PROBABILITY);
- binaryDictionary.addBigramWords(bigram.first, bigram.second, DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, bigram.first, DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, bigram.second, DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, bigram.first, bigram.second, DUMMY_PROBABILITY);
if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
final int bigramCountBeforeGC =
- Integer.parseInt(binaryDictionary.getPropertyForTests(
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.BIGRAM_COUNT_QUERY));
while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
- binaryDictionary.flushWithGC();
+ forcePassingShortTime(binaryDictionary);
}
final int bigramCountAfterGC =
- Integer.parseInt(binaryDictionary.getPropertyForTests(
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.BIGRAM_COUNT_QUERY));
assertTrue(bigramCountBeforeGC > bigramCountAfterGC);
}
}
- assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+ assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0);
- assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+ assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest(
BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount);
+ forcePassingLongTime(binaryDictionary);
+ assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.BIGRAM_COUNT_QUERY)));
+ }
+
+ public void testOverflowBigrams() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testOverflowBigrams(formatVersion);
+ }
+ }
+
+ private void testOverflowBigrams(final int formatVersion) {
+ final int bigramCount = 20000;
+ final int unigramCount = 1000;
+ final int unigramTypedCount = 20;
+ final int eachBigramTypedCount = 2;
+ final int strongBigramTypedCount = 20;
+ final int weakBigramTypedCount = 1;
+ final int codePointSetSize = 50;
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ setCurrentTimeForTestMode(mCurrentTime);
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+ final ArrayList<String> words = new ArrayList<>();
+ for (int i = 0; i < unigramCount; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ words.add(word);
+ for (int j = 0; j < unigramTypedCount; j++) {
+ addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
+ }
+ }
+ final String strong = "strong";
+ final String weak = "weak";
+ final String target = "target";
+ for (int j = 0; j < unigramTypedCount; j++) {
+ addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, target, DUMMY_PROBABILITY);
+ }
+ binaryDictionary.flushWithGC();
+ for (int j = 0; j < strongBigramTypedCount; j++) {
+ addBigramWords(binaryDictionary, strong, target, DUMMY_PROBABILITY);
+ }
+ for (int j = 0; j < weakBigramTypedCount; j++) {
+ addBigramWords(binaryDictionary, weak, target, DUMMY_PROBABILITY);
+ }
+ assertTrue(isValidBigram(binaryDictionary, strong, target));
+ assertTrue(isValidBigram(binaryDictionary, weak, target));
+
+ for (int i = 0; i < bigramCount; i++) {
+ final int word0Index = random.nextInt(words.size());
+ final String word0 = words.get(word0Index);
+ final int index = random.nextInt(words.size() - 1);
+ final int word1Index = (index >= word0Index) ? index + 1 : index;
+ final String word1 = words.get(word1Index);
+
+ for (int j = 0; j < eachBigramTypedCount; j++) {
+ addBigramWords(binaryDictionary, word0, word1, DUMMY_PROBABILITY);
+ }
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ final int bigramCountBeforeGC =
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.BIGRAM_COUNT_QUERY));
+ binaryDictionary.flushWithGC();
+ final int bigramCountAfterGC =
+ Integer.parseInt(binaryDictionary.getPropertyForTest(
+ BinaryDictionary.BIGRAM_COUNT_QUERY));
+ assertTrue(bigramCountBeforeGC > bigramCountAfterGC);
+ assertTrue(isValidBigram(binaryDictionary, strong, target));
+ assertFalse(isValidBigram(binaryDictionary, weak, target));
+ break;
+ }
+ }
+ }
+
+ public void testDictMigration() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+ }
+ }
+
+ private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
+ setCurrentTimeForTestMode(mCurrentTime);
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
+ assertTrue(binaryDictionary.isValidWord("aaa"));
+ addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY);
+ assertFalse(binaryDictionary.isValidWord("bbb"));
+ addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
+ addUnigramWord(binaryDictionary, "abc", DUMMY_PROBABILITY);
+ addBigramWords(binaryDictionary, "aaa", "abc", DUMMY_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "abc"));
+ addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY);
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb"));
+
+ assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion());
+ assertTrue(binaryDictionary.migrateTo(toFormatVersion));
+ assertTrue(binaryDictionary.isValidDictionary());
+ assertEquals(toFormatVersion, binaryDictionary.getFormatVersion());
+ assertTrue(binaryDictionary.isValidWord("aaa"));
+ assertFalse(binaryDictionary.isValidWord("bbb"));
+ assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc"));
+ addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY);
+ assertTrue(binaryDictionary.isValidWord("bbb"));
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "abc"));
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb"));
+ addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY);
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
+ binaryDictionary.close();
+ dictFile.delete();
+ }
+
+ public void testBeginningOfSentence() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ if (supportsBeginningOfSentence(formatVersion)) {
+ testBeginningOfSentence(formatVersion);
+ }
+ }
+ }
+
+ private void testBeginningOfSentence(final int formatVersion) {
+ setCurrentTimeForTestMode(mCurrentTime);
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ binaryDictionary.addUnigramEntry("", DUMMY_PROBABILITY, "" /* shortcutTarget */,
+ BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */,
+ true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */,
+ mCurrentTime);
+ final PrevWordsInfo prevWordsInfoStartOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE;
+ addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
+ binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY,
+ mCurrentTime);
+ assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa"));
+ binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY,
+ mCurrentTime);
+ addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY);
+ binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY,
+ mCurrentTime);
+ assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa"));
+ assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb"));
+
+ forcePassingLongTime(binaryDictionary);
+ assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa"));
+ assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb"));
+
+ addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
+ binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY,
+ mCurrentTime);
+ addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY);
+ binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY,
+ mCurrentTime);
+ assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa"));
+ assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb"));
+ binaryDictionary.close();
+ dictFile.delete();
}
}
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 5b8f0e977..6ba18d665 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -21,8 +21,14 @@ import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.util.Pair;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
import com.android.inputmethod.latin.makedict.CodePointUtils;
import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.WeightedString;
+import com.android.inputmethod.latin.makedict.WordProperty;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
+import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.utils.LanguageModelParam;
import java.io.File;
import java.io.IOException;
@@ -33,39 +39,60 @@ import java.util.Locale;
import java.util.Map;
import java.util.Random;
+// TODO Use the seed passed as an argument for makedict test.
@LargeTest
public class BinaryDictionaryTests extends AndroidTestCase {
private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
private static final String TEST_LOCALE = "test";
+ private static final int[] DICT_FORMAT_VERSIONS =
+ new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ private static boolean canCheckBigramProbability(final int formatVersion) {
+ return formatVersion > FormatSpec.VERSION401;
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ private static boolean supportsBeginningOfSentence(final int formatVersion) {
+ return formatVersion > FormatSpec.VERSION401;
}
- private File createEmptyDictionaryAndGetFile(final String filename) throws IOException {
- final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION,
+ private File createEmptyDictionaryAndGetFile(final String dictId,
+ final int formatVersion) throws IOException {
+ if (formatVersion == FormatSpec.VERSION4
+ || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
+ || formatVersion == FormatSpec.VERSION4_DEV) {
+ return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
+ } else {
+ throw new IOException("Dictionary format version " + formatVersion
+ + " is not supported.");
+ }
+ }
+
+ private File createEmptyVer4DictionaryAndGetFile(final String dictId,
+ final int formatVersion) throws IOException {
+ final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION,
getContext().getCacheDir());
- Map<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
- 3 /* dictVersion */, attributeMap)) {
+ file.delete();
+ file.mkdir();
+ Map<String, String> attributeMap = new HashMap<>();
+ if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion,
+ Locale.ENGLISH, attributeMap)) {
return file;
} else {
- throw new IOException("Empty dictionary cannot be created.");
+ throw new IOException("Empty dictionary " + file.getAbsolutePath()
+ + " cannot be created. Format version: " + formatVersion);
}
}
public void testIsValidDictionary() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testIsValidDictionary(formatVersion);
+ }
+ }
+
+ private void testIsValidDictionary(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -77,7 +104,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.close();
assertFalse("binaryDictionary must be invalid after closing.",
binaryDictionary.isValidDictionary());
- dictFile.delete();
+ FileUtils.deleteRecursively(dictFile);
binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */,
dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(),
TEST_LOCALE, true /* isUpdatable */);
@@ -86,10 +113,126 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.close();
}
+ public void testConstructingDictionaryOnMemory() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testConstructingDictionaryOnMemory(formatVersion);
+ }
+ }
+
+ private void testConstructingDictionaryOnMemory(final int formatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ FileUtils.deleteRecursively(dictFile);
+ assertFalse(dictFile.exists());
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, formatVersion,
+ new HashMap<String, String>());
+ assertTrue(binaryDictionary.isValidDictionary());
+ assertEquals(formatVersion, binaryDictionary.getFormatVersion());
+ final int probability = 100;
+ addUnigramWord(binaryDictionary, "word", probability);
+ assertEquals(probability, binaryDictionary.getFrequency("word"));
+ assertFalse(dictFile.exists());
+ binaryDictionary.flush();
+ assertTrue(dictFile.exists());
+ assertTrue(binaryDictionary.isValidDictionary());
+ assertEquals(formatVersion, binaryDictionary.getFormatVersion());
+ assertEquals(probability, binaryDictionary.getFrequency("word"));
+ binaryDictionary.close();
+ dictFile.delete();
+ }
+
+ public void testAddTooLongWord() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddTooLongWord(formatVersion);
+ }
+ }
+
+ private void testAddTooLongWord(final int formatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final StringBuffer stringBuilder = new StringBuffer();
+ for (int i = 0; i < Constants.DICTIONARY_MAX_WORD_LENGTH; i++) {
+ stringBuilder.append('a');
+ }
+ final String validLongWord = stringBuilder.toString();
+ stringBuilder.append('a');
+ final String invalidLongWord = stringBuilder.toString();
+ final int probability = 100;
+ addUnigramWord(binaryDictionary, "aaa", probability);
+ addUnigramWord(binaryDictionary, validLongWord, probability);
+ addUnigramWord(binaryDictionary, invalidLongWord, probability);
+ // Too long short cut.
+ binaryDictionary.addUnigramEntry("a", probability, invalidLongWord,
+ 10 /* shortcutProbability */, false /* isBeginningOfSentence */,
+ false /* isNotAWord */, false /* isBlacklisted */,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+ addUnigramWord(binaryDictionary, "abc", probability);
+ final int updatedProbability = 200;
+ // Update.
+ addUnigramWord(binaryDictionary, validLongWord, updatedProbability);
+ addUnigramWord(binaryDictionary, invalidLongWord, updatedProbability);
+ addUnigramWord(binaryDictionary, "abc", updatedProbability);
+
+ assertEquals(probability, binaryDictionary.getFrequency("aaa"));
+ assertEquals(updatedProbability, binaryDictionary.getFrequency(validLongWord));
+ assertEquals(BinaryDictionary.NOT_A_PROBABILITY,
+ binaryDictionary.getFrequency(invalidLongWord));
+ assertEquals(updatedProbability, binaryDictionary.getFrequency("abc"));
+ dictFile.delete();
+ }
+
+ private static void addUnigramWord(final BinaryDictionary binaryDictionary, final String word,
+ final int probability) {
+ binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */,
+ BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */,
+ false /* isBeginningOfSentence */, false /* isNotAWord */,
+ false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
+ }
+
+ private static void addBigramWords(final BinaryDictionary binaryDictionary, final String word0,
+ final String word1, final int probability) {
+ binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
+ }
+
+ private static boolean isValidBigram(final BinaryDictionary binaryDictionary,
+ final String word0, final String word1) {
+ return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1);
+ }
+
+ private static void removeBigramEntry(final BinaryDictionary binaryDictionary,
+ final String word0, final String word1) {
+ binaryDictionary.removeNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1);
+ }
+
+ private static int getBigramProbability(final BinaryDictionary binaryDictionary,
+ final String word0, final String word1) {
+ return binaryDictionary.getNgramProbability(new PrevWordsInfo(new WordInfo(word0)), word1);
+ }
+
public void testAddUnigramWord() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddUnigramWord(formatVersion);
+ }
+ }
+
+ private void testAddUnigramWord(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -98,21 +241,21 @@ public class BinaryDictionaryTests extends AndroidTestCase {
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
final int probability = 100;
- binaryDictionary.addUnigramWord("aaa", probability);
+ addUnigramWord(binaryDictionary, "aaa", probability);
// Reallocate and create.
- binaryDictionary.addUnigramWord("aab", probability);
+ addUnigramWord(binaryDictionary, "aab", probability);
// Insert into children.
- binaryDictionary.addUnigramWord("aac", probability);
+ addUnigramWord(binaryDictionary, "aac", probability);
// Make terminal.
- binaryDictionary.addUnigramWord("aa", probability);
+ addUnigramWord(binaryDictionary, "aa", probability);
// Create children.
- binaryDictionary.addUnigramWord("aaaa", probability);
+ addUnigramWord(binaryDictionary, "aaaa", probability);
// Reallocate and make termianl.
- binaryDictionary.addUnigramWord("a", probability);
+ addUnigramWord(binaryDictionary, "a", probability);
final int updatedProbability = 200;
// Update.
- binaryDictionary.addUnigramWord("aaa", updatedProbability);
+ addUnigramWord(binaryDictionary, "aaa", updatedProbability);
assertEquals(probability, binaryDictionary.getFrequency("aab"));
assertEquals(probability, binaryDictionary.getFrequency("aac"));
@@ -125,13 +268,19 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testRandomlyAddUnigramWord() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testRandomlyAddUnigramWord(formatVersion);
+ }
+ }
+
+ private void testRandomlyAddUnigramWord(final int formatVersion) {
final int wordCount = 1000;
final int codePointSetSize = 50;
final long seed = System.currentTimeMillis();
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -139,7 +288,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final HashMap<String, Integer> probabilityMap = new HashMap<String, Integer>();
+ final HashMap<String, Integer> probabilityMap = new HashMap<>();
// Test a word that isn't contained within the dictionary.
final Random random = new Random(seed);
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
@@ -148,7 +297,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
probabilityMap.put(word, random.nextInt(0xFF));
}
for (String word : probabilityMap.keySet()) {
- binaryDictionary.addUnigramWord(word, probabilityMap.get(word));
+ addUnigramWord(binaryDictionary, word, probabilityMap.get(word));
}
for (String word : probabilityMap.keySet()) {
assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word));
@@ -157,9 +306,15 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testAddBigramWords() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddBigramWords(formatVersion);
+ }
+ }
+
+ private void testAddBigramWords(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -168,59 +323,73 @@ public class BinaryDictionaryTests extends AndroidTestCase {
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
final int unigramProbability = 100;
- final int bigramProbability = 10;
- final int updatedBigramProbability = 15;
- binaryDictionary.addUnigramWord("aaa", unigramProbability);
- binaryDictionary.addUnigramWord("abb", unigramProbability);
- binaryDictionary.addUnigramWord("bcc", unigramProbability);
- binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
- binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
- binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
- binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
-
- final int probability = binaryDictionary.calculateProbability(unigramProbability,
- bigramProbability);
- assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
- assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
- assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
- assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
- assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
- assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
- assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
- assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
-
- binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability);
- final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability,
- updatedBigramProbability);
- assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
-
- assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
- assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
- assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+ final int bigramProbability = 150;
+ final int updatedBigramProbability = 200;
+ addUnigramWord(binaryDictionary, "aaa", unigramProbability);
+ addUnigramWord(binaryDictionary, "abb", unigramProbability);
+ addUnigramWord(binaryDictionary, "bcc", unigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability);
+
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "abb"));
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc"));
+ assertTrue(isValidBigram(binaryDictionary, "abb", "aaa"));
+ assertTrue(isValidBigram(binaryDictionary, "abb", "bcc"));
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
+ }
+
+ addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability);
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(updatedBigramProbability,
+ getBigramProbability(binaryDictionary, "aaa", "abb"));
+ }
+
+ assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa"));
+ assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc"));
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa"));
assertEquals(Dictionary.NOT_A_PROBABILITY,
- binaryDictionary.getBigramProbability("bcc", "aaa"));
+ getBigramProbability(binaryDictionary, "bcc", "aaa"));
assertEquals(Dictionary.NOT_A_PROBABILITY,
- binaryDictionary.getBigramProbability("bcc", "bbc"));
+ getBigramProbability(binaryDictionary, "bcc", "bbc"));
assertEquals(Dictionary.NOT_A_PROBABILITY,
- binaryDictionary.getBigramProbability("aaa", "aaa"));
+ getBigramProbability(binaryDictionary, "aaa", "aaa"));
// Testing bigram link.
- binaryDictionary.addUnigramWord("abcde", unigramProbability);
- binaryDictionary.addUnigramWord("fghij", unigramProbability);
- binaryDictionary.addBigramWords("abcde", "fghij", bigramProbability);
- binaryDictionary.addUnigramWord("fgh", unigramProbability);
- binaryDictionary.addUnigramWord("abc", unigramProbability);
- binaryDictionary.addUnigramWord("f", unigramProbability);
- assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+ addUnigramWord(binaryDictionary, "abcde", unigramProbability);
+ addUnigramWord(binaryDictionary, "fghij", unigramProbability);
+ addBigramWords(binaryDictionary, "abcde", "fghij", bigramProbability);
+ addUnigramWord(binaryDictionary, "fgh", unigramProbability);
+ addUnigramWord(binaryDictionary, "abc", unigramProbability);
+ addUnigramWord(binaryDictionary, "f", unigramProbability);
+
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, "abcde", "fghij"));
+ }
assertEquals(Dictionary.NOT_A_PROBABILITY,
- binaryDictionary.getBigramProbability("abcde", "fgh"));
- binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability);
- assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+ getBigramProbability(binaryDictionary, "abcde", "fgh"));
+ addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability);
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(updatedBigramProbability,
+ getBigramProbability(binaryDictionary, "abcde", "fghij"));
+ }
dictFile.delete();
}
public void testRandomlyAddBigramWords() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testRandomlyAddBigramWords(formatVersion);
+ }
+ }
+
+ private void testRandomlyAddBigramWords(final int formatVersion) {
final int wordCount = 100;
final int bigramCount = 1000;
final int codePointSetSize = 50;
@@ -229,7 +398,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -237,19 +406,18 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final ArrayList<String> words = new ArrayList<String>();
- final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
+ final ArrayList<String> words = new ArrayList<>();
+ final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
- final HashMap<Pair<String, String>, Integer> bigramProbabilities =
- new HashMap<Pair<String, String>, Integer>();
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
for (int i = 0; i < wordCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
unigramProbabilities.put(word, unigramProbability);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
for (int i = 0; i < bigramCount; i++) {
@@ -258,29 +426,38 @@ public class BinaryDictionaryTests extends AndroidTestCase {
if (TextUtils.equals(word0, word1)) {
continue;
}
- final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
bigramWords.add(bigram);
- final int bigramProbability = random.nextInt(0xF);
+ final int unigramProbability = unigramProbabilities.get(word1);
+ final int bigramProbability =
+ unigramProbability + random.nextInt(0xFF - unigramProbability);
bigramProbabilities.put(bigram, bigramProbability);
- binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
}
for (final Pair<String, String> bigram : bigramWords) {
- final int unigramProbability = unigramProbabilities.get(bigram.second);
final int bigramProbability = bigramProbabilities.get(bigram);
- final int probability = binaryDictionary.calculateProbability(unigramProbability,
- bigramProbability);
- assertEquals(probability,
- binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+ assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+ isValidBigram(binaryDictionary, bigram.first, bigram.second));
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
+ }
}
dictFile.delete();
}
public void testRemoveBigramWords() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testRemoveBigramWords(formatVersion);
+ }
+ }
+
+ private void testRemoveBigramWords(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -288,45 +465,51 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
final int unigramProbability = 100;
- final int bigramProbability = 10;
- binaryDictionary.addUnigramWord("aaa", unigramProbability);
- binaryDictionary.addUnigramWord("abb", unigramProbability);
- binaryDictionary.addUnigramWord("bcc", unigramProbability);
- binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
- binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
- binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
- binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
-
- assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
- assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
- assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
- assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
-
- binaryDictionary.removeBigramWords("aaa", "abb");
- assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb"));
- binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
- assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
-
-
- binaryDictionary.removeBigramWords("aaa", "bcc");
- assertEquals(false, binaryDictionary.isValidBigram("aaa", "bcc"));
- binaryDictionary.removeBigramWords("abb", "aaa");
- assertEquals(false, binaryDictionary.isValidBigram("abb", "aaa"));
- binaryDictionary.removeBigramWords("abb", "bcc");
- assertEquals(false, binaryDictionary.isValidBigram("abb", "bcc"));
-
- binaryDictionary.removeBigramWords("aaa", "abb");
+ final int bigramProbability = 150;
+ addUnigramWord(binaryDictionary, "aaa", unigramProbability);
+ addUnigramWord(binaryDictionary, "abb", unigramProbability);
+ addUnigramWord(binaryDictionary, "bcc", unigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability);
+
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "abb"));
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc"));
+ assertTrue(isValidBigram(binaryDictionary, "abb", "aaa"));
+ assertTrue(isValidBigram(binaryDictionary, "abb", "bcc"));
+
+ removeBigramEntry(binaryDictionary, "aaa", "abb");
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "abb"));
+ addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability);
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "abb"));
+
+
+ removeBigramEntry(binaryDictionary, "aaa", "bcc");
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "bcc"));
+ removeBigramEntry(binaryDictionary, "abb", "aaa");
+ assertFalse(isValidBigram(binaryDictionary, "abb", "aaa"));
+ removeBigramEntry(binaryDictionary, "abb", "bcc");
+ assertFalse(isValidBigram(binaryDictionary, "abb", "bcc"));
+
+ removeBigramEntry(binaryDictionary, "aaa", "abb");
// Test remove non-existing bigram operation.
- binaryDictionary.removeBigramWords("aaa", "abb");
- binaryDictionary.removeBigramWords("bcc", "aaa");
+ removeBigramEntry(binaryDictionary, "aaa", "abb");
+ removeBigramEntry(binaryDictionary, "bcc", "aaa");
dictFile.delete();
}
public void testFlushDictionary() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testFlushDictionary(formatVersion);
+ }
+ }
+
+ private void testFlushDictionary(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -335,8 +518,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
final int probability = 100;
- binaryDictionary.addUnigramWord("aaa", probability);
- binaryDictionary.addUnigramWord("abcd", probability);
+ addUnigramWord(binaryDictionary, "aaa", probability);
+ addUnigramWord(binaryDictionary, "abcd", probability);
// Close without flushing.
binaryDictionary.close();
@@ -347,8 +530,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa"));
assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd"));
- binaryDictionary.addUnigramWord("aaa", probability);
- binaryDictionary.addUnigramWord("abcd", probability);
+ addUnigramWord(binaryDictionary, "aaa", probability);
+ addUnigramWord(binaryDictionary, "abcd", probability);
binaryDictionary.flush();
binaryDictionary.close();
@@ -358,7 +541,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
assertEquals(probability, binaryDictionary.getFrequency("aaa"));
assertEquals(probability, binaryDictionary.getFrequency("abcd"));
- binaryDictionary.addUnigramWord("bcde", probability);
+ addUnigramWord(binaryDictionary, "bcde", probability);
binaryDictionary.flush();
binaryDictionary.close();
@@ -372,9 +555,15 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testFlushWithGCDictionary() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testFlushWithGCDictionary(formatVersion);
+ }
+ }
+
+ private void testFlushWithGCDictionary(final int formatVersion) {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -383,40 +572,46 @@ public class BinaryDictionaryTests extends AndroidTestCase {
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
final int unigramProbability = 100;
- final int bigramProbability = 10;
- binaryDictionary.addUnigramWord("aaa", unigramProbability);
- binaryDictionary.addUnigramWord("abb", unigramProbability);
- binaryDictionary.addUnigramWord("bcc", unigramProbability);
- binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
- binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
- binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
- binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
+ final int bigramProbability = 150;
+ addUnigramWord(binaryDictionary, "aaa", unigramProbability);
+ addUnigramWord(binaryDictionary, "abb", unigramProbability);
+ addUnigramWord(binaryDictionary, "bcc", unigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability);
+ addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability);
+ addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability);
binaryDictionary.flushWithGC();
binaryDictionary.close();
binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final int probability = binaryDictionary.calculateProbability(unigramProbability,
- bigramProbability);
assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
assertEquals(unigramProbability, binaryDictionary.getFrequency("abb"));
assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc"));
- assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
- assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
- assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
- assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
- assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
- assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
- assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
+ }
+ assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa"));
+ assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc"));
+ assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa"));
binaryDictionary.flushWithGC();
binaryDictionary.close();
dictFile.delete();
}
- // TODO: Evaluate performance of GC
public void testAddBigramWordsAndFlashWithGC() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddBigramWordsAndFlashWithGC(formatVersion);
+ }
+ }
+
+ // TODO: Evaluate performance of GC
+ private void testAddBigramWordsAndFlashWithGC(final int formatVersion) {
final int wordCount = 100;
final int bigramCount = 1000;
final int codePointSetSize = 30;
@@ -425,7 +620,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -434,19 +629,18 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final ArrayList<String> words = new ArrayList<String>();
- final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
+ final ArrayList<String> words = new ArrayList<>();
+ final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
- final HashMap<Pair<String, String>, Integer> bigramProbabilities =
- new HashMap<Pair<String, String>, Integer>();
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
for (int i = 0; i < wordCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
unigramProbabilities.put(word, unigramProbability);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
for (int i = 0; i < bigramCount; i++) {
@@ -455,11 +649,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
if (TextUtils.equals(word0, word1)) {
continue;
}
- final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
bigramWords.add(bigram);
- final int bigramProbability = random.nextInt(0xF);
+ final int unigramProbability = unigramProbabilities.get(word1);
+ final int bigramProbability =
+ unigramProbability + random.nextInt(0xFF - unigramProbability);
bigramProbabilities.put(bigram, bigramProbability);
- binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
}
binaryDictionary.flushWithGC();
@@ -468,19 +664,27 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
for (final Pair<String, String> bigram : bigramWords) {
- final int unigramProbability = unigramProbabilities.get(bigram.second);
final int bigramProbability = bigramProbabilities.get(bigram);
- final int probability = binaryDictionary.calculateProbability(unigramProbability,
- bigramProbability);
- assertEquals(probability,
- binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+ assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+ isValidBigram(binaryDictionary, bigram.first, bigram.second));
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
+ }
}
dictFile.delete();
}
- public void testRandomOperetionsAndFlashWithGC() {
+ public void testRandomOperationsAndFlashWithGC() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testRandomOperationsAndFlashWithGC(formatVersion);
+ }
+ }
+
+ private void testRandomOperationsAndFlashWithGC(final int formatVersion) {
final int flashWithGCIterationCount = 50;
final int operationCountInEachIteration = 200;
final int initialUnigramCount = 100;
@@ -494,7 +698,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
@@ -502,18 +706,17 @@ public class BinaryDictionaryTests extends AndroidTestCase {
BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final ArrayList<String> words = new ArrayList<String>();
- final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
+ final ArrayList<String> words = new ArrayList<>();
+ final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
- final HashMap<Pair<String, String>, Integer> bigramProbabilities =
- new HashMap<Pair<String, String>, Integer>();
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
for (int i = 0; i < initialUnigramCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
unigramProbabilities.put(word, unigramProbability);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
binaryDictionary.flushWithGC();
binaryDictionary.close();
@@ -529,7 +732,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
unigramProbabilities.put(word, unigramProbability);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
// Add bigram.
if (random.nextFloat() < addBigramProb && words.size() > 2) {
@@ -543,11 +746,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
if (TextUtils.equals(word0, word1)) {
continue;
}
- final int bigramProbability = random.nextInt(0xF);
- final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+ final int unigramProbability = unigramProbabilities.get(word1);
+ final int bigramProbability =
+ unigramProbability + random.nextInt(0xFF - unigramProbability);
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
bigramWords.add(bigram);
bigramProbabilities.put(bigram, bigramProbability);
- binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
}
// Remove bigram.
if (random.nextFloat() < removeBigramProb && !bigramWords.isEmpty()) {
@@ -555,7 +760,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final Pair<String, String> bigram = bigramWords.get(bigramIndex);
bigramWords.remove(bigramIndex);
bigramProbabilities.remove(bigram);
- binaryDictionary.removeBigramWords(bigram.first, bigram.second);
+ removeBigramEntry(binaryDictionary, bigram.first, bigram.second);
}
}
@@ -568,17 +773,20 @@ public class BinaryDictionaryTests extends AndroidTestCase {
// Test whether the all bigram operations are collectlly handled.
for (int i = 0; i < bigramWords.size(); i++) {
final Pair<String, String> bigram = bigramWords.get(i);
- final int unigramProbability = unigramProbabilities.get(bigram.second);
final int probability;
if (bigramProbabilities.containsKey(bigram)) {
final int bigramProbability = bigramProbabilities.get(bigram);
- probability = binaryDictionary.calculateProbability(unigramProbability,
- bigramProbability);
+ probability = bigramProbability;
} else {
probability = Dictionary.NOT_A_PROBABILITY;
}
- assertEquals(probability,
- binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(probability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
+ }
+ assertEquals(probability != Dictionary.NOT_A_PROBABILITY,
+ isValidBigram(binaryDictionary, bigram.first, bigram.second));
}
binaryDictionary.flushWithGC();
binaryDictionary.close();
@@ -588,6 +796,12 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testAddManyUnigramsAndFlushWithGC() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddManyUnigramsAndFlushWithGC(formatVersion);
+ }
+ }
+
+ private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) {
final int flashWithGCIterationCount = 3;
final int codePointSetSize = 50;
@@ -596,13 +810,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
- final ArrayList<String> words = new ArrayList<String>();
- final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
+ final ArrayList<String> words = new ArrayList<>();
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
BinaryDictionary binaryDictionary;
@@ -615,7 +829,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
unigramProbabilities.put(word, unigramProbability);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
for (int j = 0; j < words.size(); j++) {
@@ -632,6 +846,12 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testUnigramAndBigramCount() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testUnigramAndBigramCount(formatVersion);
+ }
+ }
+
+ private void testUnigramAndBigramCount(final int formatVersion) {
final int flashWithGCIterationCount = 10;
final int codePointSetSize = 50;
final int unigramCountPerIteration = 1000;
@@ -641,13 +861,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
File dictFile = null;
try {
- dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
- final ArrayList<String> words = new ArrayList<String>();
- final HashSet<Pair<String, String>> bigrams = new HashSet<Pair<String, String>>();
+ final ArrayList<String> words = new ArrayList<>();
+ final HashSet<Pair<String, String>> bigrams = new HashSet<>();
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
BinaryDictionary binaryDictionary;
@@ -659,7 +879,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final String word = CodePointUtils.generateWord(random, codePointSet);
words.add(word);
final int unigramProbability = random.nextInt(0xFF);
- binaryDictionary.addUnigramWord(word, unigramProbability);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
}
for (int j = 0; j < bigramCountPerIteration; j++) {
final String word0 = words.get(random.nextInt(words.size()));
@@ -667,22 +887,630 @@ public class BinaryDictionaryTests extends AndroidTestCase {
if (TextUtils.equals(word0, word1)) {
continue;
}
- bigrams.add(new Pair<String, String>(word0, word1));
+ bigrams.add(new Pair<>(word0, word1));
final int bigramProbability = random.nextInt(0xF);
- binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
}
- assertEquals(new HashSet<String>(words).size(), Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
- assertEquals(new HashSet<Pair<String, String>>(bigrams).size(), Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.BIGRAM_COUNT_QUERY)));
+ assertEquals(new HashSet<>(words).size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
+ assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY)));
binaryDictionary.flushWithGC();
- assertEquals(new HashSet<String>(words).size(), Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
- assertEquals(new HashSet<Pair<String, String>>(bigrams).size(), Integer.parseInt(
- binaryDictionary.getPropertyForTests(BinaryDictionary.BIGRAM_COUNT_QUERY)));
+ assertEquals(new HashSet<>(words).size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
+ assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY)));
binaryDictionary.close();
}
dictFile.delete();
}
+
+ public void testAddMultipleDictionaryEntries() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddMultipleDictionaryEntries(formatVersion);
+ }
+ }
+
+ private void testAddMultipleDictionaryEntries(final int formatVersion) {
+ final int codePointSetSize = 20;
+ final int lmParamCount = 1000;
+ final double bigramContinueRate = 0.9;
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
+
+ final LanguageModelParam[] languageModelParams = new LanguageModelParam[lmParamCount];
+ String prevWord = null;
+ for (int i = 0; i < languageModelParams.length; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ final int probability = random.nextInt(0xFF);
+ final int bigramProbability = probability + random.nextInt(0xFF - probability);
+ unigramProbabilities.put(word, probability);
+ if (prevWord == null) {
+ languageModelParams[i] = new LanguageModelParam(word, probability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+ } else {
+ languageModelParams[i] = new LanguageModelParam(prevWord, word, probability,
+ bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+ bigramProbabilities.put(new Pair<>(prevWord, word),
+ bigramProbability);
+ }
+ prevWord = (random.nextDouble() < bigramContinueRate) ? word : null;
+ }
+
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ binaryDictionary.addMultipleDictionaryEntries(languageModelParams);
+
+ for (Map.Entry<String, Integer> entry : unigramProbabilities.entrySet()) {
+ assertEquals((int)entry.getValue(), binaryDictionary.getFrequency(entry.getKey()));
+ }
+
+ for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) {
+ final String word0 = entry.getKey().first;
+ final String word1 = entry.getKey().second;
+ final int bigramProbability = entry.getValue();
+ assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+ isValidBigram(binaryDictionary, word0, word1));
+ if (canCheckBigramProbability(formatVersion)) {
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, word0, word1));
+ }
+ }
+ }
+
+ public void testGetWordProperties() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testGetWordProperties(formatVersion);
+ }
+ }
+
+ private void testGetWordProperties(final int formatVersion) {
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+ final int UNIGRAM_COUNT = 1000;
+ final int BIGRAM_COUNT = 1000;
+ final int codePointSetSize = 20;
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord",
+ false /* isBeginningOfSentence */);
+ assertFalse(invalidWordProperty.isValid());
+
+ final ArrayList<String> words = new ArrayList<>();
+ final HashMap<String, Integer> wordProbabilities = new HashMap<>();
+ final HashMap<String, HashSet<String>> bigrams = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
+
+ for (int i = 0; i < UNIGRAM_COUNT; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ final int unigramProbability = random.nextInt(0xFF);
+ final boolean isNotAWord = random.nextBoolean();
+ final boolean isBlacklisted = random.nextBoolean();
+ // TODO: Add tests for historical info.
+ binaryDictionary.addUnigramEntry(word, unigramProbability,
+ null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY,
+ false /* isBeginningOfSentence */, isNotAWord, isBlacklisted,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ words.add(word);
+ wordProbabilities.put(word, unigramProbability);
+ final WordProperty wordProperty = binaryDictionary.getWordProperty(word,
+ false /* isBeginningOfSentence */);
+ assertEquals(word, wordProperty.mWord);
+ assertTrue(wordProperty.isValid());
+ assertEquals(isNotAWord, wordProperty.mIsNotAWord);
+ assertEquals(isBlacklisted, wordProperty.mIsBlacklistEntry);
+ assertEquals(false, wordProperty.mHasBigrams);
+ assertEquals(false, wordProperty.mHasShortcuts);
+ assertEquals(unigramProbability, wordProperty.mProbabilityInfo.mProbability);
+ assertTrue(wordProperty.mShortcutTargets.isEmpty());
+ }
+
+ for (int i = 0; i < BIGRAM_COUNT; i++) {
+ final int word0Index = random.nextInt(wordProbabilities.size());
+ final int word1Index = random.nextInt(wordProbabilities.size());
+ if (word0Index == word1Index) {
+ continue;
+ }
+ final String word0 = words.get(word0Index);
+ final String word1 = words.get(word1Index);
+ final int unigramProbability = wordProbabilities.get(word1);
+ final int bigramProbability =
+ unigramProbability + random.nextInt(0xFF - unigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ if (!bigrams.containsKey(word0)) {
+ final HashSet<String> bigramWord1s = new HashSet<>();
+ bigrams.put(word0, bigramWord1s);
+ }
+ bigrams.get(word0).add(word1);
+ bigramProbabilities.put(new Pair<>(word0, word1), bigramProbability);
+ }
+
+ for (int i = 0; i < words.size(); i++) {
+ final String word0 = words.get(i);
+ if (!bigrams.containsKey(word0)) {
+ continue;
+ }
+ final HashSet<String> bigramWord1s = bigrams.get(word0);
+ final WordProperty wordProperty = binaryDictionary.getWordProperty(word0,
+ false /* isBeginningOfSentence */);
+ assertEquals(bigramWord1s.size(), wordProperty.mBigrams.size());
+ for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
+ final String word1 = wordProperty.mBigrams.get(j).mWord;
+ assertTrue(bigramWord1s.contains(word1));
+ if (canCheckBigramProbability(formatVersion)) {
+ final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1));
+ assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+ }
+ }
+ }
+ }
+
+ public void testIterateAllWords() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testIterateAllWords(formatVersion);
+ }
+ }
+
+ private void testIterateAllWords(final int formatVersion) {
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+ final int UNIGRAM_COUNT = 1000;
+ final int BIGRAM_COUNT = 1000;
+ final int codePointSetSize = 20;
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord",
+ false /* isBeginningOfSentence */);
+ assertFalse(invalidWordProperty.isValid());
+
+ final ArrayList<String> words = new ArrayList<>();
+ final HashMap<String, Integer> wordProbabilitiesToCheckLater = new HashMap<>();
+ final HashMap<String, HashSet<String>> bigrams = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilitiesToCheckLater =
+ new HashMap<>();
+
+ for (int i = 0; i < UNIGRAM_COUNT; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ final int unigramProbability = random.nextInt(0xFF);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ words.add(word);
+ wordProbabilitiesToCheckLater.put(word, unigramProbability);
+ }
+
+ for (int i = 0; i < BIGRAM_COUNT; i++) {
+ final int word0Index = random.nextInt(wordProbabilitiesToCheckLater.size());
+ final int word1Index = random.nextInt(wordProbabilitiesToCheckLater.size());
+ if (word0Index == word1Index) {
+ continue;
+ }
+ final String word0 = words.get(word0Index);
+ final String word1 = words.get(word1Index);
+ final int unigramProbability = wordProbabilitiesToCheckLater.get(word1);
+ final int bigramProbability =
+ unigramProbability + random.nextInt(0xFF - unigramProbability);
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ if (!bigrams.containsKey(word0)) {
+ final HashSet<String> bigramWord1s = new HashSet<>();
+ bigrams.put(word0, bigramWord1s);
+ }
+ bigrams.get(word0).add(word1);
+ bigramProbabilitiesToCheckLater.put(new Pair<>(word0, word1), bigramProbability);
+ }
+
+ final HashSet<String> wordSet = new HashSet<>(words);
+ final HashSet<Pair<String, String>> bigramSet =
+ new HashSet<>(bigramProbabilitiesToCheckLater.keySet());
+ int token = 0;
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ binaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ final String word0 = wordProperty.mWord;
+ assertEquals((int)wordProbabilitiesToCheckLater.get(word0),
+ wordProperty.mProbabilityInfo.mProbability);
+ wordSet.remove(word0);
+ final HashSet<String> bigramWord1s = bigrams.get(word0);
+ for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
+ final String word1 = wordProperty.mBigrams.get(j).mWord;
+ assertTrue(bigramWord1s.contains(word1));
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
+ if (canCheckBigramProbability(formatVersion)) {
+ final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
+ assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+ }
+ bigramSet.remove(bigram);
+ }
+ token = result.mNextToken;
+ } while (token != 0);
+ assertTrue(wordSet.isEmpty());
+ assertTrue(bigramSet.isEmpty());
+ }
+
+ public void testAddShortcuts() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddShortcuts(formatVersion);
+ }
+ }
+
+ private void testAddShortcuts(final int formatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final int unigramProbability = 100;
+ final int shortcutProbability = 10;
+ binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz",
+ shortcutProbability, false /* isBeginningOfSentence */,
+ false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */);
+ WordProperty wordProperty = binaryDictionary.getWordProperty("aaa",
+ false /* isBeginningOfSentence */);
+ assertEquals(1, wordProperty.mShortcutTargets.size());
+ assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord);
+ assertEquals(shortcutProbability, wordProperty.mShortcutTargets.get(0).getProbability());
+ final int updatedShortcutProbability = 2;
+ binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz",
+ updatedShortcutProbability, false /* isBeginningOfSentence */,
+ false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */);
+ wordProperty = binaryDictionary.getWordProperty("aaa",
+ false /* isBeginningOfSentence */);
+ assertEquals(1, wordProperty.mShortcutTargets.size());
+ assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord);
+ assertEquals(updatedShortcutProbability,
+ wordProperty.mShortcutTargets.get(0).getProbability());
+ binaryDictionary.addUnigramEntry("aaa", unigramProbability, "yyy",
+ shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */,
+ false /* isBlacklisted */, 0 /* timestamp */);
+ final HashMap<String, Integer> shortcutTargets = new HashMap<>();
+ shortcutTargets.put("zzz", updatedShortcutProbability);
+ shortcutTargets.put("yyy", shortcutProbability);
+ wordProperty = binaryDictionary.getWordProperty("aaa",
+ false /* isBeginningOfSentence */);
+ assertEquals(2, wordProperty.mShortcutTargets.size());
+ for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord));
+ assertEquals((int)shortcutTargets.get(shortcutTarget.mWord),
+ shortcutTarget.getProbability());
+ shortcutTargets.remove(shortcutTarget.mWord);
+ }
+ shortcutTargets.put("zzz", updatedShortcutProbability);
+ shortcutTargets.put("yyy", shortcutProbability);
+ binaryDictionary.flushWithGC();
+ wordProperty = binaryDictionary.getWordProperty("aaa",
+ false /* isBeginningOfSentence */);
+ assertEquals(2, wordProperty.mShortcutTargets.size());
+ for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord));
+ assertEquals((int)shortcutTargets.get(shortcutTarget.mWord),
+ shortcutTarget.getProbability());
+ shortcutTargets.remove(shortcutTarget.mWord);
+ }
+ }
+
+ public void testAddManyShortcuts() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testAddManyShortcuts(formatVersion);
+ }
+ }
+
+ private void testAddManyShortcuts(final int formatVersion) {
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+ final int UNIGRAM_COUNT = 1000;
+ final int SHORTCUT_COUNT = 10000;
+ final int codePointSetSize = 20;
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+ final ArrayList<String> words = new ArrayList<>();
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<String, HashMap<String, Integer>> shortcutTargets = new HashMap<>();
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ for (int i = 0; i < UNIGRAM_COUNT; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ final int unigramProbability = random.nextInt(0xFF);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
+ words.add(word);
+ unigramProbabilities.put(word, unigramProbability);
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ }
+ for (int i = 0; i < SHORTCUT_COUNT; i++) {
+ final String shortcutTarget = CodePointUtils.generateWord(random, codePointSet);
+ final int shortcutProbability = random.nextInt(0xF);
+ final String word = words.get(random.nextInt(words.size()));
+ final int unigramProbability = unigramProbabilities.get(word);
+ binaryDictionary.addUnigramEntry(word, unigramProbability, shortcutTarget,
+ shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */,
+ false /* isBlacklisted */, 0 /* timestamp */);
+ if (shortcutTargets.containsKey(word)) {
+ final HashMap<String, Integer> shortcutTargetsOfWord = shortcutTargets.get(word);
+ shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability);
+ } else {
+ final HashMap<String, Integer> shortcutTargetsOfWord = new HashMap<>();
+ shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability);
+ shortcutTargets.put(word, shortcutTargetsOfWord);
+ }
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ }
+
+ for (final String word : words) {
+ final WordProperty wordProperty = binaryDictionary.getWordProperty(word,
+ false /* isBeginningOfSentence */);
+ assertEquals((int)unigramProbabilities.get(word),
+ wordProperty.mProbabilityInfo.mProbability);
+ if (!shortcutTargets.containsKey(word)) {
+ // The word does not have shortcut targets.
+ continue;
+ }
+ assertEquals(shortcutTargets.get(word).size(), wordProperty.mShortcutTargets.size());
+ for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ final String targetCodePonts = shortcutTarget.mWord;
+ assertEquals((int)shortcutTargets.get(word).get(targetCodePonts),
+ shortcutTarget.getProbability());
+ }
+ }
+ }
+
+ public void testDictMigration() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+ }
+ }
+
+ private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ final int unigramProbability = 100;
+ addUnigramWord(binaryDictionary, "aaa", unigramProbability);
+ addUnigramWord(binaryDictionary, "bbb", unigramProbability);
+ final int bigramProbability = 150;
+ addBigramWords(binaryDictionary, "aaa", "bbb", bigramProbability);
+ final int shortcutProbability = 10;
+ binaryDictionary.addUnigramEntry("ccc", unigramProbability, "xxx", shortcutProbability,
+ false /* isBeginningOfSentence */, false /* isNotAWord */,
+ false /* isBlacklisted */, 0 /* timestamp */);
+ binaryDictionary.addUnigramEntry("ddd", unigramProbability, null /* shortcutTarget */,
+ Dictionary.NOT_A_PROBABILITY, false /* isBeginningOfSentence */,
+ true /* isNotAWord */, true /* isBlacklisted */, 0 /* timestamp */);
+ binaryDictionary.addNgramEntry(PrevWordsInfo.BEGINNING_OF_SENTENCE,
+ "aaa", bigramProbability, 0 /* timestamp */);
+ assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
+ assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb"));
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
+ assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion());
+ assertTrue(binaryDictionary.migrateTo(toFormatVersion));
+ assertTrue(binaryDictionary.isValidDictionary());
+ assertEquals(toFormatVersion, binaryDictionary.getFormatVersion());
+ assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
+ assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb"));
+ if (canCheckBigramProbability(toFormatVersion)) {
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb"));
+ assertEquals(bigramProbability, binaryDictionary.getNgramProbability(
+ PrevWordsInfo.BEGINNING_OF_SENTENCE, "aaa"));
+ }
+ assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
+ WordProperty wordProperty = binaryDictionary.getWordProperty("ccc",
+ false /* isBeginningOfSentence */);
+ assertEquals(1, wordProperty.mShortcutTargets.size());
+ assertEquals("xxx", wordProperty.mShortcutTargets.get(0).mWord);
+ wordProperty = binaryDictionary.getWordProperty("ddd",
+ false /* isBeginningOfSentence */);
+ assertTrue(wordProperty.mIsBlacklistEntry);
+ assertTrue(wordProperty.mIsNotAWord);
+ }
+
+ public void testLargeDictMigration() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+ }
+ }
+
+ private void testLargeDictMigration(final int fromFormatVersion, final int toFormatVersion) {
+ final int UNIGRAM_COUNT = 3000;
+ final int BIGRAM_COUNT = 3000;
+ final int codePointSetSize = 50;
+ final long seed = System.currentTimeMillis();
+ final Random random = new Random(seed);
+
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final ArrayList<String> words = new ArrayList<>();
+ final ArrayList<Pair<String, String>> bigrams = new ArrayList<>();
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+ final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>();
+
+ for (int i = 0; i < UNIGRAM_COUNT; i++) {
+ final String word = CodePointUtils.generateWord(random, codePointSet);
+ final int unigramProbability = random.nextInt(0xFF);
+ addUnigramWord(binaryDictionary, word, unigramProbability);
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ words.add(word);
+ unigramProbabilities.put(word, unigramProbability);
+ }
+
+ for (int i = 0; i < BIGRAM_COUNT; i++) {
+ final int word0Index = random.nextInt(words.size());
+ final int word1Index = random.nextInt(words.size());
+ if (word0Index == word1Index) {
+ continue;
+ }
+ final String word0 = words.get(word0Index);
+ final String word1 = words.get(word1Index);
+ final int unigramProbability = unigramProbabilities.get(word1);
+ final int bigramProbability =
+ random.nextInt(0xFF - unigramProbability) + unigramProbability;
+ addBigramWords(binaryDictionary, word0, word1, bigramProbability);
+ if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
+ bigrams.add(bigram);
+ bigramProbabilities.put(bigram, bigramProbability);
+ }
+ assertTrue(binaryDictionary.migrateTo(toFormatVersion));
+
+ for (final String word : words) {
+ assertEquals((int)unigramProbabilities.get(word), binaryDictionary.getFrequency(word));
+ }
+ assertEquals(unigramProbabilities.size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
+
+ for (final Pair<String, String> bigram : bigrams) {
+ if (canCheckBigramProbability(toFormatVersion)) {
+ assertEquals((int)bigramProbabilities.get(bigram),
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
+ }
+ assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second));
+ }
+ assertEquals(bigramProbabilities.size(), Integer.parseInt(
+ binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY)));
+ }
+
+ public void testBeginningOfSentence() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ if (supportsBeginningOfSentence(formatVersion)) {
+ testBeginningOfSentence(formatVersion);
+ }
+ }
+ }
+
+ private void testBeginningOfSentence(final int formatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ final int dummyProbability = 0;
+ final PrevWordsInfo prevWordsInfoBeginningOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE;
+ final int bigramProbability = 200;
+ addUnigramWord(binaryDictionary, "aaa", dummyProbability);
+ binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
+ assertEquals(bigramProbability,
+ binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa"));
+ binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
+ addUnigramWord(binaryDictionary, "bbb", dummyProbability);
+ binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "bbb", bigramProbability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */);
+ binaryDictionary.flushWithGC();
+ assertEquals(bigramProbability,
+ binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa"));
+ assertEquals(bigramProbability,
+ binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "bbb"));
+ }
+
+ public void testGetMaxFrequencyOfExactMatches() {
+ for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+ testGetMaxFrequencyOfExactMatches(formatVersion);
+ }
+ }
+
+ private void testGetMaxFrequencyOfExactMatches(final int formatVersion) {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ addUnigramWord(binaryDictionary, "abc", 10);
+ addUnigramWord(binaryDictionary, "aBc", 15);
+ assertEquals(15, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
+ addUnigramWord(binaryDictionary, "ab'c", 20);
+ assertEquals(20, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
+ addUnigramWord(binaryDictionary, "a-b-c", 25);
+ assertEquals(25, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
+ addUnigramWord(binaryDictionary, "ab-'-'-'-c", 30);
+ assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
+ addUnigramWord(binaryDictionary, "ab c", 255);
+ assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
index c4fd5a0c4..6e894decf 100644
--- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
+++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
@@ -50,8 +50,7 @@ public class BlueUnderlineTests extends InputTestsBase {
final SpanGetter spanBefore = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart);
assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd);
- assertEquals("extend blue underline, span color", true,
- spanBefore.isAutoCorrectionIndicator());
+ assertTrue("extend blue underline, span color", spanBefore.isAutoCorrectionIndicator());
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
// Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span
@@ -61,6 +60,7 @@ public class BlueUnderlineTests extends InputTestsBase {
public void testBlueUnderlineOnBackspace() {
final String STRING_TO_TYPE = "tgis";
+ final int typedLength = STRING_TO_TYPE.length();
final int EXPECTED_SUGGESTION_SPAN_START = -1;
final int EXPECTED_UNDERLINE_SPAN_START = 0;
final int EXPECTED_UNDERLINE_SPAN_END = 4;
@@ -68,6 +68,8 @@ public class BlueUnderlineTests extends InputTestsBase {
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
type(Constants.CODE_SPACE);
+ // typedLength + 1 because we also typed a space
+ mLatinIME.onUpdateSelection(0, 0, typedLength + 1, typedLength + 1, -1, -1);
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
type(Constants.CODE_DELETE);
@@ -77,8 +79,8 @@ public class BlueUnderlineTests extends InputTestsBase {
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
final SpanGetter suggestionSpan = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
- assertEquals("show no blue underline after backspace, span start should be -1",
- EXPECTED_SUGGESTION_SPAN_START, suggestionSpan.mStart);
+ assertFalse("show no blue underline after backspace, span should not be the auto-"
+ + "correction indicator", suggestionSpan.isAutoCorrectionIndicator());
final SpanGetter underlineSpan = new SpanGetter(mEditText.getText(), UnderlineSpan.class);
assertEquals("should be composing, so should have an underline span",
EXPECTED_UNDERLINE_SPAN_START, underlineSpan.mStart);
@@ -104,7 +106,8 @@ public class BlueUnderlineTests extends InputTestsBase {
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
- assertNull("blue underline removed when cursor is moved", span.mSpan);
+ assertFalse("blue underline removed when cursor is moved",
+ span.isAutoCorrectionIndicator());
}
public void testComposingStopsOnSpace() {
diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
new file mode 100644
index 000000000..70b8f530a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.Locale;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches;
+
+/**
+ * Unit test for DistracterFilter
+ */
+@LargeTest
+public class DistracterFilterTest extends InputTestsBase {
+ private DistracterFilterCheckingExactMatches mDistracterFilter;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDistracterFilter = new DistracterFilterCheckingExactMatches(getContext());
+ mDistracterFilter.updateEnabledSubtypes(mLatinIME.getEnabledSubtypesForTest());
+ }
+
+ public void testIsDistractorToWordsInDictionaries() {
+ final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
+
+ final Locale localeEnUs = new Locale("en", "US");
+ String typedWord;
+
+ typedWord = "Bill";
+ // For this test case, we consider "Bill" is a distracter to "bill".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "nOt";
+ // For this test case, we consider "nOt" is a distracter to "not".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "youre";
+ // For this test case, we consider "youre" is a distracter to "you're".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "Banana";
+ // For this test case, we consider "Banana" is a distracter to "banana".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "orange";
+ // For this test case, we consider "orange" is not a distracter to any word in dictionaries.
+ assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "Orange";
+ // For this test case, we consider "Orange" is a distracter to "orange".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "café";
+ // For this test case, we consider "café" is a distracter to "cafe".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "cafe";
+ // For this test case, we consider "cafe" is not a distracter to any word in dictionaries.
+ assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "I'll";
+ // For this test case, we consider "I'll" is not a distracter to any word in dictionaries.
+ assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "ill";
+ // For this test case, we consider "ill" is a distracter to "I'll"
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "asdfd";
+ // For this test case, we consider "asdfd" is not a distracter to any word in dictionaries.
+ assertFalse(
+ mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ typedWord = "thank";
+ // For this test case, we consider "thank" is not a distracter to any other word
+ // in dictionaries.
+ assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+ final Locale localeDeDe = new Locale("de", "DE");
+
+ typedWord = "fuer";
+ // For this test case, we consider "fuer" is a distracter to "für".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe));
+
+ typedWord = "fUEr";
+ // For this test case, we consider "fUEr" is a distracter to "für".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe));
+
+ typedWord = "fur";
+ // For this test case, we consider "fur" is a distracter to "für".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe));
+
+ final Locale localeFrFr = new Locale("fr", "FR");
+
+ typedWord = "a";
+ // For this test case, we consider "a" is a distracter to "à".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr));
+
+ typedWord = "à";
+ // For this test case, we consider "à" is not a distracter to any word in dictionaries.
+ assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr));
+
+ typedWord = "etre";
+ // For this test case, we consider "etre" is a distracter to "être".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr));
+
+ typedWord = "États-unis";
+ // For this test case, we consider "États-unis" is a distracter to "États-Unis".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr));
+
+ typedWord = "ÉtatsUnis";
+ // For this test case, we consider "ÉtatsUnis" is a distracter to "États-Unis".
+ assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+ EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr));
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
deleted file mode 100644
index 6aae1044e..000000000
--- a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-/**
- * Unit test for ExpandableDictionary
- */
-@SmallTest
-public class ExpandableDictionaryTests extends AndroidTestCase {
-
- private final static int UNIGRAM_FREQ = 50;
- // See UserBinaryDictionary for more information about this variable.
- // For tests, its actual value does not matter.
- private final static int SHORTCUT_FREQ = 14;
-
- public void testAddWordAndGetWordFrequency() {
- final ExpandableDictionary dict = new ExpandableDictionary(Dictionary.TYPE_USER);
-
- // Add words
- dict.addWord("abcde", "abcde", UNIGRAM_FREQ, SHORTCUT_FREQ);
- dict.addWord("abcef", null, UNIGRAM_FREQ + 1, 0);
-
- // Check words
- assertFalse(dict.isValidWord("abcde"));
- assertEquals(UNIGRAM_FREQ, dict.getWordFrequency("abcde"));
- assertTrue(dict.isValidWord("abcef"));
- assertEquals(UNIGRAM_FREQ+1, dict.getWordFrequency("abcef"));
-
- dict.addWord("abc", null, UNIGRAM_FREQ + 2, 0);
- assertTrue(dict.isValidWord("abc"));
- assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
-
- // Add existing word with lower frequency
- dict.addWord("abc", null, UNIGRAM_FREQ, 0);
- assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
-
- // Add existing word with higher frequency
- dict.addWord("abc", null, UNIGRAM_FREQ + 3, 0);
- assertEquals(UNIGRAM_FREQ + 3, dict.getWordFrequency("abc"));
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
index cadd0f8f3..09309bcc0 100644
--- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
@@ -19,7 +19,9 @@ package com.android.inputmethod.latin;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary;
+import com.android.inputmethod.latin.makedict.ProbabilityInfo;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import java.util.HashMap;
@@ -31,18 +33,18 @@ import java.util.HashMap;
public class FusionDictionaryTests extends AndroidTestCase {
public void testFindWordInTree() {
FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
+ new DictionaryOptions(new HashMap<String,String>()));
- dict.add("abc", 10, null, false /* isNotAWord */);
+ dict.add("abc", new ProbabilityInfo(10), null, false /* isNotAWord */);
assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa"));
assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "abc"));
- dict.add("aa", 10, null, false /* isNotAWord */);
+ dict.add("aa", new ProbabilityInfo(10), null, false /* isNotAWord */);
assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa"));
assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aa"));
- dict.add("babcd", 10, null, false /* isNotAWord */);
- dict.add("bacde", 10, null, false /* isNotAWord */);
+ dict.add("babcd", new ProbabilityInfo(10), null, false /* isNotAWord */);
+ dict.add("bacde", new ProbabilityInfo(10), null, false /* isNotAWord */);
assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "ba"));
assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "babcd"));
assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "bacde"));
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 8ad8689d8..de9475af4 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -16,7 +16,10 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.settings.Settings;
+
import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
import android.view.inputmethod.BaseInputConnection;
@LargeTest
@@ -32,7 +35,7 @@ public class InputLogicTests extends InputTestsBase {
final String WORD_TO_TYPE = "this";
final String EXPECTED_RESULT = "thi";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
type(Constants.CODE_DELETE);
assertEquals("press suggestion then backspace", EXPECTED_RESULT,
@@ -44,9 +47,8 @@ public class InputLogicTests extends InputTestsBase {
final String WORD_TO_PICK = "this";
final String EXPECTED_RESULT = "thi";
type(WORD_TO_TYPE);
- // Choose the auto-correction, which is always in position 0. For "tgis", the
- // auto-correction should be "this".
- pickSuggestionManually(0, WORD_TO_PICK);
+ // Choose the auto-correction. For "tgis", the auto-correction should be "this".
+ pickSuggestionManually(WORD_TO_PICK);
mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
assertEquals("pick typed word over auto-correction then backspace", WORD_TO_PICK,
mEditText.getText().toString());
@@ -59,9 +61,8 @@ public class InputLogicTests extends InputTestsBase {
final String WORD_TO_TYPE = "tgis";
final String EXPECTED_RESULT = "tgi";
type(WORD_TO_TYPE);
- // Choose the typed word, which should be in position 1 (because position 0 should
- // be occupied by the "this" auto-correction, as checked by testAutoCorrect())
- pickSuggestionManually(1, WORD_TO_TYPE);
+ // Choose the typed word.
+ pickSuggestionManually(WORD_TO_TYPE);
mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
assertEquals("pick typed word over auto-correction then backspace", WORD_TO_TYPE,
mEditText.getText().toString());
@@ -75,9 +76,8 @@ public class InputLogicTests extends InputTestsBase {
final String WORD_TO_PICK = "thus";
final String EXPECTED_RESULT = "thu";
type(WORD_TO_TYPE);
- // Choose the second suggestion, which should be in position 2 and should be "thus"
- // when "tgis is typed.
- pickSuggestionManually(2, WORD_TO_PICK);
+ // Choose the second suggestion, which should be "thus" when "tgis" is typed.
+ pickSuggestionManually(WORD_TO_PICK);
mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1);
assertEquals("pick different suggestion then backspace", WORD_TO_PICK,
mEditText.getText().toString());
@@ -179,6 +179,8 @@ public class InputLogicTests extends InputTestsBase {
}
public void testDoubleSpace() {
+ // Set default pref just in case
+ setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true);
// U+1F607 is an emoji
final String[] STRINGS_TO_TYPE =
new String[] { "this ", "a+ ", "\u1F607 ", ".. ", ") ", "( ", "% " };
@@ -200,6 +202,76 @@ public class InputLogicTests extends InputTestsBase {
assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
}
+ private void testDoubleSpacePeriodWithSettings(final boolean expectsPeriod,
+ final Object... settingsKeysValues) {
+ final Object[] oldSettings = new Object[settingsKeysValues.length / 2];
+ final String STRING_WITHOUT_PERIOD = "this ";
+ final String STRING_WITH_PERIOD = "this. ";
+ final String EXPECTED_RESULT = expectsPeriod ? STRING_WITH_PERIOD : STRING_WITHOUT_PERIOD;
+ try {
+ for (int i = 0; i < settingsKeysValues.length; i += 2) {
+ if (settingsKeysValues[i + 1] instanceof String) {
+ oldSettings[i / 2] = setStringPreference((String)settingsKeysValues[i],
+ (String)settingsKeysValues[i + 1], "0");
+ } else {
+ oldSettings[i / 2] = setBooleanPreference((String)settingsKeysValues[i],
+ (Boolean)settingsKeysValues[i + 1], false);
+ }
+ }
+ mLatinIME.loadSettings();
+ mEditText.setText("");
+ type(STRING_WITHOUT_PERIOD);
+ assertEquals("double-space-to-period with specific settings "
+ + TextUtils.join(" ", settingsKeysValues),
+ EXPECTED_RESULT, mEditText.getText().toString());
+ } finally {
+ // Restore old settings
+ for (int i = 0; i < settingsKeysValues.length; i += 2) {
+ if (null == oldSettings[i / 2]) {
+ break;
+ } if (oldSettings[i / 2] instanceof String) {
+ setStringPreference((String)settingsKeysValues[i], (String)oldSettings[i / 2],
+ "");
+ } else {
+ setBooleanPreference((String)settingsKeysValues[i], (Boolean)oldSettings[i / 2],
+ false);
+ }
+ }
+ }
+ }
+
+ public void testDoubleSpacePeriod() {
+ // Reset settings to default, else these tests will go flaky.
+ setStringPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING, "0", "0");
+ setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, "1", "1");
+ setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true);
+ testDoubleSpacePeriodWithSettings(true /* expectsPeriod */);
+ // "Suggestion visibility" to "always hide"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS_SETTING, "2");
+ // "Suggestion visibility" to "portrait only"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS_SETTING, "1");
+ // "Suggestion visibility" to "always show"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS_SETTING, "0");
+
+ // "Double-space period" to "off"
+ testDoubleSpacePeriodWithSettings(false, Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false);
+
+ // "Auto-correction" to "off"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0");
+ // "Auto-correction" to "modest"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "1");
+ // "Auto-correction" to "very aggressive"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "3");
+
+ // "Suggestion visibility" to "always hide" and "Auto-correction" to "off"
+ testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS_SETTING, "0",
+ Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0");
+ // "Suggestion visibility" to "always hide" and "Auto-correction" to "off"
+ testDoubleSpacePeriodWithSettings(false, Settings.PREF_SHOW_SUGGESTIONS_SETTING, "0",
+ Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0",
+ Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false);
+ }
+
public void testBackspaceAtStartAfterAutocorrect() {
final String STRING_TO_TYPE = "tgis ";
final int typedLength = STRING_TO_TYPE.length();
@@ -234,7 +306,7 @@ public class InputLogicTests extends InputTestsBase {
final String WORD_TO_TYPE = "this";
final String EXPECTED_RESULT = WORD_TO_TYPE;
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
assertEquals("no space after manual pick", EXPECTED_RESULT,
mEditText.getText().toString());
}
@@ -244,7 +316,7 @@ public class InputLogicTests extends InputTestsBase {
final String WORD2_TO_TYPE = "is";
final String EXPECTED_RESULT = "this is";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
type(WORD2_TO_TYPE);
assertEquals("manual pick then type", EXPECTED_RESULT, mEditText.getText().toString());
}
@@ -254,20 +326,32 @@ public class InputLogicTests extends InputTestsBase {
final String WORD2_TO_TYPE = "!";
final String EXPECTED_RESULT = "this!";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
type(WORD2_TO_TYPE);
assertEquals("manual pick then separator", EXPECTED_RESULT, mEditText.getText().toString());
}
+ // This test matches the one in InputLogicTestsNonEnglish. In some non-English languages,
+ // ! and ? are clustering punctuation signs.
+ public void testClusteringPunctuation() {
+ final String WORD1_TO_TYPE = "test";
+ final String WORD2_TO_TYPE = "!!?!:!";
+ final String EXPECTED_RESULT = "test!!?!:!";
+ type(WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
+ type(WORD2_TO_TYPE);
+ assertEquals("clustering punctuation", EXPECTED_RESULT, mEditText.getText().toString());
+ }
+
public void testManualPickThenStripperThenPick() {
final String WORD_TO_TYPE = "this";
final String STRIPPER = "\n";
final String EXPECTED_RESULT = "this\nthis";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
type(STRIPPER);
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
assertEquals("manual pick then \\n then manual pick", EXPECTED_RESULT,
mEditText.getText().toString());
}
@@ -277,7 +361,7 @@ public class InputLogicTests extends InputTestsBase {
final String WORD2_TO_TYPE = " is";
final String EXPECTED_RESULT = "this is";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
type(WORD2_TO_TYPE);
assertEquals("manual pick then space then type", EXPECTED_RESULT,
mEditText.getText().toString());
@@ -288,11 +372,9 @@ public class InputLogicTests extends InputTestsBase {
final String WORD2_TO_PICK = "is";
final String EXPECTED_RESULT = "this is";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
- // Here we fake picking a word through bigram prediction. This test is taking
- // advantage of the fact that Latin IME blindly trusts the caller of #pickSuggestionManually
- // to actually pass the right string.
- pickSuggestionManually(1, WORD2_TO_PICK);
+ pickSuggestionManually(WORD1_TO_TYPE);
+ // Here we fake picking a word through bigram prediction.
+ pickSuggestionManually(WORD2_TO_PICK);
assertEquals("manual pick then manual pick", EXPECTED_RESULT,
mEditText.getText().toString());
}
@@ -307,12 +389,14 @@ public class InputLogicTests extends InputTestsBase {
}
public void testResumeSuggestionOnBackspace() {
- final String WORD_TO_TYPE = "and this ";
- type(WORD_TO_TYPE);
+ final String STRING_TO_TYPE = "and this ";
+ final int typedLength = STRING_TO_TYPE.length();
+ type(STRING_TO_TYPE);
assertEquals("resume suggestion on backspace", -1,
BaseInputConnection.getComposingSpanStart(mEditText.getText()));
assertEquals("resume suggestion on backspace", -1,
BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+ mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
type(Constants.CODE_DELETE);
assertEquals("resume suggestion on backspace", 4,
BaseInputConnection.getComposingSpanStart(mEditText.getText()));
@@ -348,4 +432,211 @@ public class InputLogicTests extends InputTestsBase {
helperTestComposing("a'", true);
}
// TODO: Add some tests for non-BMP characters
+
+ public void testAutoCorrectByUserHistory() {
+ final String WORD_TO_BE_CORRECTED = "qpmx";
+ final String NOT_CORRECTED_RESULT = "qpmx ";
+ final String DESIRED_WORD = "qpmz";
+ final String CORRECTED_RESULT = "qpmz ";
+ final int typeCountNotToAutocorrect = 1;
+ final int typeCountToAutoCorrect = 16;
+ int startIndex = 0;
+ int endIndex = 0;
+
+ for (int i = 0; i < typeCountNotToAutocorrect; i++) {
+ type(DESIRED_WORD);
+ type(Constants.CODE_SPACE);
+ }
+ startIndex = mEditText.getText().length();
+ type(WORD_TO_BE_CORRECTED);
+ type(Constants.CODE_SPACE);
+ endIndex = mEditText.getText().length();
+ assertEquals("not auto-corrected by user history", NOT_CORRECTED_RESULT,
+ mEditText.getText().subSequence(startIndex, endIndex).toString());
+ for (int i = typeCountNotToAutocorrect; i < typeCountToAutoCorrect; i++) {
+ type(DESIRED_WORD);
+ type(Constants.CODE_SPACE);
+ }
+ startIndex = mEditText.getText().length();
+ type(WORD_TO_BE_CORRECTED);
+ type(Constants.CODE_SPACE);
+ endIndex = mEditText.getText().length();
+ assertEquals("auto-corrected by user history",
+ CORRECTED_RESULT, mEditText.getText().subSequence(startIndex, endIndex).toString());
+ }
+
+ public void testPredictionsAfterSpace() {
+ final String WORD_TO_TYPE = "Barack ";
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Test the first prediction is displayed
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("predictions after space", "Obama",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
+ }
+
+ public void testPredictionsWithDoubleSpaceToPeriod() {
+ mLatinIME.clearPersonalizedDictionariesForTest();
+ final String WORD_TO_TYPE = "Barack ";
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // No need to test here, testPredictionsAfterSpace is testing it already
+ type(" ");
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Test the predictions have been cleared
+ SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("predictions cleared after double-space-to-period", suggestedWords.size(), 0);
+ type(Constants.CODE_DELETE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Test the first prediction is displayed
+ suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("predictions after cancel double-space-to-period", "Obama",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
+ }
+
+ public void testPredictionsAfterManualPick() {
+ final String WORD_TO_TYPE = "Barack";
+ type(WORD_TO_TYPE);
+ // Choose the auto-correction. For "Barack", the auto-correction should be "Barack".
+ pickSuggestionManually(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Test the first prediction is displayed
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("predictions after manual pick", "Obama",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
+ }
+
+ public void testPredictionsAfterPeriod() {
+ mLatinIME.clearPersonalizedDictionariesForTest();
+ final String WORD_TO_TYPE = "Barack. ";
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("No prediction after period after inputting once.", 0, suggestedWords.size());
+
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("Beginning-of-Sentence prediction after inputting 2 times.", "Barack",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
+ }
+
+ public void testPredictionsAfterRecorrection() {
+ final String PREFIX = "A ";
+ final String WORD_TO_TYPE = "Barack";
+ final String FIRST_NON_TYPED_SUGGESTION = "Barrack";
+ final int endOfPrefix = PREFIX.length();
+ final int endOfWord = endOfPrefix + WORD_TO_TYPE.length();
+ final int endOfSuggestion = endOfPrefix + FIRST_NON_TYPED_SUGGESTION.length();
+ final int indexForManualCursor = endOfPrefix + 3; // +3 because it's after "Bar" in "Barack"
+ type(PREFIX);
+ mLatinIME.onUpdateSelection(0, 0, endOfPrefix, endOfPrefix, -1, -1);
+ type(WORD_TO_TYPE);
+ pickSuggestionManually(FIRST_NON_TYPED_SUGGESTION);
+ mLatinIME.onUpdateSelection(endOfPrefix, endOfPrefix, endOfSuggestion, endOfSuggestion,
+ -1, -1);
+ runMessages();
+ type(" ");
+ mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion,
+ endOfSuggestion + 1, endOfSuggestion + 1, -1, -1);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Simulate a manual cursor move
+ mInputConnection.setSelection(indexForManualCursor, indexForManualCursor);
+ mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1,
+ indexForManualCursor, indexForManualCursor, -1, -1);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ pickSuggestionManually(WORD_TO_TYPE);
+ mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor,
+ endOfWord, endOfWord, -1, -1);
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ // Test the first prediction is displayed
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("predictions after recorrection", "Obama",
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
+ }
+
+ public void testComposingMultipleBackspace() {
+ final String WORD_TO_TYPE = "radklro";
+ final int TIMES_TO_TYPE = 3;
+ final int TIMES_TO_BACKSPACE = 8;
+ type(WORD_TO_TYPE);
+ type(Constants.CODE_DELETE);
+ type(Constants.CODE_DELETE);
+ type(Constants.CODE_DELETE);
+ type(WORD_TO_TYPE);
+ type(Constants.CODE_DELETE);
+ type(Constants.CODE_DELETE);
+ type(WORD_TO_TYPE);
+ type(Constants.CODE_DELETE);
+ type(Constants.CODE_DELETE);
+ type(Constants.CODE_DELETE);
+ assertEquals("composing with multiple backspace",
+ WORD_TO_TYPE.length() * TIMES_TO_TYPE - TIMES_TO_BACKSPACE,
+ mEditText.getText().length());
+ }
+
+ public void testManySingleQuotes() {
+ final String WORD_TO_AUTOCORRECT = "i";
+ final String WORD_AUTOCORRECTED = "I";
+ final String QUOTES = "''''''''''''''''''''";
+ final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
+ final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
+ type(WORD_TO_TYPE);
+ assertEquals("auto-correct with many trailing single quotes", EXPECTED_RESULT,
+ mEditText.getText().toString());
+ }
+
+ public void testManySingleQuotesOneByOne() {
+ final String WORD_TO_AUTOCORRECT = "i";
+ final String WORD_AUTOCORRECTED = "I";
+ final String QUOTES = "''''''''''''''''''''";
+ final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
+ final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
+
+ for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
+ type(WORD_TO_TYPE.substring(i, i+1));
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ }
+ assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT,
+ mEditText.getText().toString());
+ }
+
+ public void testTypingSingleQuotesOneByOne() {
+ final String WORD_TO_TYPE = "it's ";
+ final String EXPECTED_RESULT = WORD_TO_TYPE;
+ for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
+ type(WORD_TO_TYPE.substring(i, i+1));
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ }
+ assertEquals("type words letter by letter", EXPECTED_RESULT,
+ mEditText.getText().toString());
+ }
+
+ public void testSwitchLanguages() {
+ final String WORD_TO_TYPE_FIRST_PART = "com";
+ final String WORD_TO_TYPE_SECOND_PART = "md";
+ final String EXPECTED_RESULT = "comme";
+ changeLanguage("en");
+ type(WORD_TO_TYPE_FIRST_PART);
+ changeLanguage("fr");
+ runMessages();
+ type(WORD_TO_TYPE_SECOND_PART);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
+ assertEquals("Suggestions updated after switching languages",
+ EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(1) : null);
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java
index 0f0ebafb9..2560407dc 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java
@@ -19,8 +19,6 @@ package com.android.inputmethod.latin;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.inputmethod.BaseInputConnection;
-import com.android.inputmethod.latin.suggestions.SuggestionStripView;
-
@LargeTest
public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase {
public void testAutoCorrectForLanguageWithoutSpaces() {
@@ -99,7 +97,8 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase {
assertEquals("predictions in lang without spaces", "Barack",
mEditText.getText().toString());
// Test the first prediction is displayed
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
assertEquals("predictions in lang without spaces", "Obama",
- mLatinIME.getFirstSuggestedWord());
+ suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
}
}
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
index 2d736e338..866f8894c 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
@@ -18,8 +18,6 @@ package com.android.inputmethod.latin;
import android.test.suitebuilder.annotation.LargeTest;
-import com.android.inputmethod.latin.suggestions.SuggestionStripView;
-
@LargeTest
public class InputLogicTestsNonEnglish extends InputTestsBase {
final String NEXT_WORD_PREDICTION_OPTION = "next_word_prediction";
@@ -39,12 +37,25 @@ public class InputLogicTestsNonEnglish extends InputTestsBase {
final String EXPECTED_RESULT = "test !";
changeLanguage("fr");
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
type(WORD2_TO_TYPE);
assertEquals("manual pick then separator for French", EXPECTED_RESULT,
mEditText.getText().toString());
}
+ public void testClusteringPunctuationForFrench() {
+ final String WORD1_TO_TYPE = "test";
+ final String WORD2_TO_TYPE = "!!?!:!";
+ // In English, the expected result would be "test!!?!:!"
+ final String EXPECTED_RESULT = "test !!?! : !";
+ changeLanguage("fr");
+ type(WORD1_TO_TYPE);
+ pickSuggestionManually(WORD1_TO_TYPE);
+ type(WORD2_TO_TYPE);
+ assertEquals("clustering punctuation for French", EXPECTED_RESULT,
+ mEditText.getText().toString());
+ }
+
public void testWordThenSpaceThenPunctuationFromStripTwiceForFrench() {
final String WORD_TO_TYPE = "test ";
final String PUNCTUATION_FROM_STRIP = "!";
@@ -60,9 +71,9 @@ public class InputLogicTestsNonEnglish extends InputTestsBase {
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
assertTrue("type word then type space should display punctuation strip",
- mLatinIME.isShowingPunctuationList());
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
+ mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions());
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
assertEquals("type word then type space then punctuation from strip twice for French",
EXPECTED_RESULT, mEditText.getText().toString());
} finally {
@@ -84,8 +95,9 @@ public class InputLogicTestsNonEnglish extends InputTestsBase {
type(WORD_TO_TYPE);
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
+ final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
assertEquals("type word then type space yields predictions for French",
- EXPECTED_RESULT, mLatinIME.getFirstSuggestedWord());
+ EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
} finally {
setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption,
defaultNextWordPredictionOption);
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
new file mode 100644
index 000000000..61eae4e8b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Pair;
+
+/*
+ * Relevant characters for this test :
+ * Spurs the need to reorder :
+ * U+1031 MYANMAR VOWEL SIGN E : ေ
+ * U+1004 U+103A U+1039 Kinzi. It's a compound character.
+ *
+ * List of consonants :
+ * U+1000 MYANMAR LETTER KA က
+ * U+1001 MYANMAR LETTER KHA ခ
+ * U+1002 MYANMAR LETTER GA ဂ
+ * U+1003 MYANMAR LETTER GHA ဃ
+ * U+1004 MYANMAR LETTER NGA င
+ * U+1005 MYANMAR LETTER CA စ
+ * U+1006 MYANMAR LETTER CHA ဆ
+ * U+1007 MYANMAR LETTER JA ဇ
+ * U+1008 MYANMAR LETTER JHA ဈ
+ * U+1009 MYANMAR LETTER NYA ဉ
+ * U+100A MYANMAR LETTER NNYA ည
+ * U+100B MYANMAR LETTER TTA ဋ
+ * U+100C MYANMAR LETTER TTHA ဌ
+ * U+100D MYANMAR LETTER DDA ဍ
+ * U+100E MYANMAR LETTER DDHA ဎ
+ * U+100F MYANMAR LETTER NNA ဏ
+ * U+1010 MYANMAR LETTER TA တ
+ * U+1011 MYANMAR LETTER THA ထ
+ * U+1012 MYANMAR LETTER DA ဒ
+ * U+1013 MYANMAR LETTER DHA ဓ
+ * U+1014 MYANMAR LETTER NA န
+ * U+1015 MYANMAR LETTER PA ပ
+ * U+1016 MYANMAR LETTER PHA ဖ
+ * U+1017 MYANMAR LETTER BA ဗ
+ * U+1018 MYANMAR LETTER BHA ဘ
+ * U+1019 MYANMAR LETTER MA မ
+ * U+101A MYANMAR LETTER YA ယ
+ * U+101B MYANMAR LETTER RA ရ
+ * U+101C MYANMAR LETTER LA လ
+ * U+101D MYANMAR LETTER WA ဝ
+ * U+101E MYANMAR LETTER SA သ
+ * U+101F MYANMAR LETTER HA ဟ
+ * U+1020 MYANMAR LETTER LLA ဠ
+ * U+103F MYANMAR LETTER GREAT SA ဿ
+ *
+ * List of medials :
+ * U+103B MYANMAR CONSONANT SIGN MEDIAL YA ျ
+ * U+103C MYANMAR CONSONANT SIGN MEDIAL RA ြ
+ * U+103D MYANMAR CONSONANT SIGN MEDIAL WA ွ
+ * U+103E MYANMAR CONSONANT SIGN MEDIAL HA ှ
+ * U+105E MYANMAR CONSONANT SIGN MON MEDIAL NA ၞ
+ * U+105F MYANMAR CONSONANT SIGN MON MEDIAL MA ၟ
+ * U+1060 MYANMAR CONSONANT SIGN MON MEDIAL LA ၠ
+ * U+1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA ႂ
+ *
+ * Other relevant characters :
+ * U+200C ZERO WIDTH NON-JOINER
+ * U+200B ZERO WIDTH SPACE
+ */
+
+@LargeTest
+@SuppressWarnings("rawtypes")
+public class InputLogicTestsReorderingMyanmar extends InputTestsBase {
+ // The tests are formatted as follows.
+ // Each test is an entry in the array of Pair arrays.
+
+ // One test is an array of pairs. Each pair contains, in the `first' member,
+ // the code points that the next key press should contain. In the `second'
+ // member is stored the string that should be in the text view after this
+ // key press.
+
+ private static final Pair[][] TESTS = {
+
+ // Tests for U+1031 MYANMAR VOWEL SIGN E : ေ
+ new Pair[] { // Type : U+1031 U+1000 U+101F ေ က ဟ
+ Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ
+ Pair.create(new int[] { 0x1000 }, "\u1000\u1031"), // ကေ
+ Pair.create(new int[] { 0x101F }, "\u1000\u1031\u101F") // ကေဟ
+ },
+
+ new Pair[] { // Type : U+1000 U+1031 U+101F က ေ ဟ
+ Pair.create(new int[] { 0x1000 }, "\u1000"), // က
+ Pair.create(new int[] { 0x1031 }, "\u1000\u200B\u1031"), // က‌ေ
+ Pair.create(new int[] { 0x101F }, "\u1000\u101F\u1031") // ကဟေ
+ },
+
+ new Pair[] { // Type : U+1031 U+101D U+103E U+1018 ေ ဝ ှ ဘ
+ Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ
+ Pair.create(new int[] { 0x101D }, "\u101D\u1031"), // ဝေ
+ Pair.create(new int[] { 0x103E }, "\u101D\u103E\u1031"), // ဝှေ
+ Pair.create(new int[] { 0x1018 }, "\u101D\u103E\u1031\u1018") // ဝှေဘ
+ },
+
+ new Pair[] { // Type : U+1031 U+1014 U+1031 U+1000 U+102C U+1004 U+103A U+1038 U+101C
+ // U+102C U+1038 U+104B ေ န ေ က ာ င ် း လ ာ း ။
+ Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ
+ Pair.create(new int[] { 0x1014 }, "\u1014\u1031"), // နေ
+ Pair.create(new int[] { 0x1031 }, "\u1014\u1031\u1031"), // နေ‌ေ
+ Pair.create(new int[] { 0x1000 }, "\u1014\u1031\u1000\u1031"), // နေကေ
+ Pair.create(new int[] { 0x102C }, "\u1014\u1031\u1000\u1031\u102C"), // နေကော
+ Pair.create(new int[] { 0x1004 }, "\u1014\u1031\u1000\u1031\u102C\u1004"), // နေကောင
+ Pair.create(new int[] { 0x103A }, // နေကောင်
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A"),
+ Pair.create(new int[] { 0x1038 }, // နေကောင်း
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038"),
+ Pair.create(new int[] { 0x101C }, // နေကောင်းလ
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C"),
+ Pair.create(new int[] { 0x102C }, // နေကောင်းလာ
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C"),
+ Pair.create(new int[] { 0x1038 }, // နေကောင်းလား
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038"),
+ Pair.create(new int[] { 0x104B }, // နေကောင်းလား။
+ "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038\u104B")
+ },
+
+ new Pair[] { // Type : U+1031 U+1031 U+1031 U+1000 ေ ေ ေ က
+ Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ
+ Pair.create(new int[] { 0x1031 }, "\u1031\u1031"), // ေေ
+ Pair.create(new int[] { 0x1031 }, "\u1031\u1031\u1031"), // U+1031ေေေ
+ Pair.create(new int[] { 0x1000 }, "\u1031\u1031\u1000\u1031") // ေေကေ
+ },
+
+ new Pair[] { // Type : U+1031 U+1001 U+103B U+103D U+1038 ေ ခ ျ ွ း
+ Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ
+ Pair.create(new int[] { 0x1001 }, "\u1001\u1031"), // ခေ
+ Pair.create(new int[] { 0x103B }, "\u1001\u103B\u1031"), // ချေ
+ Pair.create(new int[] { 0x103D }, "\u1001\u103B\u103D\u1031"), // ချွေ
+ Pair.create(new int[] { 0x1038 }, "\u1001\u103B\u103D\u1031\u1038") // ချွေး
+ },
+
+ // Tests for Kinzi U+1004 U+103A U+1039 :
+
+ /* Kinzi reordering is not implemented yet. Uncomment these tests when it is.
+
+ new Pair[] { // Type : U+1021 U+1002 (U+1004 U+103A U+1039)
+ // U+101C U+1014 U+103A အ ဂ (င ် ္) လ န ်
+ Pair.create(new int[] { 0x1021 }, "\u1021"), // အ
+ Pair.create(new int[] { 0x1002 }, "\u1021\u1002"), // အဂ
+ Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // အင်္ဂ
+ "\u1021\u1004\u103A\u1039\u1002"),
+ Pair.create(new int[] { 0x101C }, // အင်္ဂလ
+ "\u1021\u1004\u103A\u1039\u1002\u101C"),
+ Pair.create(new int[] { 0x1014 }, // အင်္ဂလန
+ "\u1021\u1004\u103A\u1039\u1002\u101C\u1014"),
+ Pair.create(new int[] { 0x103A }, // အင်္ဂလန်
+ "\u1021\u1004\u103A\u1039\u1002\u101C\u1014\u103A")
+ },
+
+ new Pair[] { //Type : kinzi after a whole syllable U+101E U+1001 U+103B U+102D U+102F
+ // (U+1004 U+103A U+1039) U+1004 U+103A U+1038 သ ခ ျ ိ ု င ် ္ င ် း
+ Pair.create(new int[] { 0x101E }, "\u101E"), // သခ
+ Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ
+ Pair.create(new int[] { 0x103B }, "\u101E\u1001\u103B"), // သချ
+ Pair.create(new int[] { 0x102D }, "\u101E\u1001\u103B\u102D"), // သချိ
+ Pair.create(new int[] { 0x102F }, "\u101E\u1001\u103B\u102D\u102F"), // သချို
+ Pair.create(new int[] { 0x1004, 0x103A, 0x1039}, // သင်္ချို
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"),
+ Pair.create(new int[] { 0x1004 }, // သင်္ချိုင
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"),
+ Pair.create(new int[] { 0x103A }, // သင်္ချိုင်
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"),
+ Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038")
+ },
+
+ new Pair[] { // Type : kinzi after the consonant U+101E U+1001 (U+1004 U+103A U+1039)
+ // U+103B U+102D U+102F U+1004 U+103A U+1038 သ ခ င ် ္ ျ ိ ု င ် း
+ Pair.create(new int[] { 0x101E }, "\u101E"), // သခ
+ Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ
+ Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // သင်္ခ
+ "\u101E\u1004\u103A\u1039\u1001"),
+ Pair.create(new int[] { 0x103B }, // သင်္ချ
+ "\u101E\u1004\u103A\u1039\u1001\u103B"),
+ Pair.create(new int[] { 0x102D }, // သင်္ချိ
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D"),
+ Pair.create(new int[] { 0x102F }, // သင်္ချို
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"),
+ Pair.create(new int[] { 0x1004 }, // သင်္ချိုင
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"),
+ Pair.create(new int[] { 0x103A }, // သင်္ချိုင်
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"),
+ Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း
+ "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038")
+ },
+ */
+ };
+
+ @SuppressWarnings("unchecked")
+ private void doMyanmarTest(final int testNumber, final Pair[] test) {
+ int stepNumber = 0;
+ for (final Pair<int[], String> step : test) {
+ ++stepNumber;
+ final int[] input = step.first;
+ final String expectedResult = step.second;
+ if (input.length > 1) {
+ mLatinIME.onTextInput(new String(input, 0, input.length));
+ } else {
+ type(input[0]);
+ }
+ assertEquals("Myanmar reordering test " + testNumber + ", step " + stepNumber,
+ expectedResult, mEditText.getText().toString());
+ }
+ }
+
+ public void testMyanmarReordering() {
+ int testNumber = 0;
+ changeLanguage("my_MM", "CombiningRules=MyanmarReordering");
+ for (final Pair[] test : TESTS) {
+ // Small trick to reset LatinIME : setText("") and send updateSelection with values
+ // LatinIME has never seen, and cursor pos 0,0.
+ mEditText.setText("");
+ mLatinIME.onUpdateSelection(1, 1, 0, 0, -1, -1);
+ doMyanmarTest(++testNumber, test);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
index 5095f9606..1a47cddf4 100644
--- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
@@ -55,14 +55,22 @@ public class InputPointersTests extends AndroidTestCase {
final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
final int limit = src.getXCoordinates().length * 2 + 10;
for (int i = 0; i < limit; i++) {
- src.addPointer(i, i * 2, i * 3, i * 4);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ src.addPointer(x, y, pointerId, time);
assertEquals("size after add " + i, i + 1, src.getPointerSize());
}
for (int i = 0; i < limit; i++) {
- assertEquals("xCoordinates at " + i, i, src.getXCoordinates()[i]);
- assertEquals("yCoordinates at " + i, i * 2, src.getYCoordinates()[i]);
- assertEquals("pointerIds at " + i, i * 3, src.getPointerIds()[i]);
- assertEquals("times at " + i, i * 4, src.getTimes()[i]);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ assertEquals("xCoordinates at " + i, x, src.getXCoordinates()[i]);
+ assertEquals("yCoordinates at " + i, y, src.getYCoordinates()[i]);
+ assertEquals("pointerIds at " + i, pointerId, src.getPointerIds()[i]);
+ assertEquals("times at " + i, time, src.getTimes()[i]);
}
}
@@ -70,14 +78,22 @@ public class InputPointersTests extends AndroidTestCase {
final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
final int limit = 1000, step = 100;
for (int i = 0; i < limit; i += step) {
- src.addPointer(i, i, i * 2, i * 3, i * 4);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ src.addPointerAt(i, x, y, pointerId, time);
assertEquals("size after add at " + i, i + 1, src.getPointerSize());
}
for (int i = 0; i < limit; i += step) {
- assertEquals("xCoordinates at " + i, i, src.getXCoordinates()[i]);
- assertEquals("yCoordinates at " + i, i * 2, src.getYCoordinates()[i]);
- assertEquals("pointerIds at " + i, i * 3, src.getPointerIds()[i]);
- assertEquals("times at " + i, i * 4, src.getTimes()[i]);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ assertEquals("xCoordinates at " + i, x, src.getXCoordinates()[i]);
+ assertEquals("yCoordinates at " + i, y, src.getYCoordinates()[i]);
+ assertEquals("pointerIds at " + i, pointerId, src.getPointerIds()[i]);
+ assertEquals("times at " + i, time, src.getTimes()[i]);
}
}
@@ -85,7 +101,11 @@ public class InputPointersTests extends AndroidTestCase {
final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
final int limit = src.getXCoordinates().length * 2 + 10;
for (int i = 0; i < limit; i++) {
- src.addPointer(i, i * 2, i * 3, i * 4);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ src.addPointer(x, y, pointerId, time);
}
final InputPointers dst = new InputPointers(DEFAULT_CAPACITY);
dst.set(src);
@@ -100,7 +120,11 @@ public class InputPointersTests extends AndroidTestCase {
final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
final int limit = 100;
for (int i = 0; i < limit; i++) {
- src.addPointer(i, i * 2, i * 3, i * 4);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ src.addPointer(x, y, pointerId, time);
}
final InputPointers dst = new InputPointers(DEFAULT_CAPACITY);
dst.copy(src);
@@ -121,106 +145,135 @@ public class InputPointersTests extends AndroidTestCase {
}
public void testAppend() {
- final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
- final int srcLen = 100;
- for (int i = 0; i < srcLen; i++) {
- src.addPointer(i, i * 2, i * 3, i * 4);
- }
- final int dstLen = 50;
+ final int dstLength = 50;
final InputPointers dst = new InputPointers(DEFAULT_CAPACITY);
- for (int i = 0; i < dstLen; i++) {
- final int value = -i - 1;
- dst.addPointer(value * 4, value * 3, value * 2, value);
+ for (int i = 0; i < dstLength; i++) {
+ final int x = i * 4;
+ final int y = i * 3;
+ final int pointerId = i * 2;
+ final int time = i;
+ dst.addPointer(x, y, pointerId, time);
}
final InputPointers dstCopy = new InputPointers(DEFAULT_CAPACITY);
dstCopy.copy(dst);
- dst.append(src, 0, 0);
- assertEquals("size after append zero", dstLen, dst.getPointerSize());
+ final ResizableIntArray srcXCoords = new ResizableIntArray(DEFAULT_CAPACITY);
+ final ResizableIntArray srcYCoords = new ResizableIntArray(DEFAULT_CAPACITY);
+ final ResizableIntArray srcPointerIds = new ResizableIntArray(DEFAULT_CAPACITY);
+ final ResizableIntArray srcTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ final int srcLength = 100;
+ final int srcPointerId = 10;
+ for (int i = 0; i < srcLength; i++) {
+ final int x = i;
+ final int y = i * 2;
+ // The time value must be larger than <code>dst</code>.
+ final int time = i * 4 + dstLength;
+ srcXCoords.add(x);
+ srcYCoords.add(y);
+ srcPointerIds.add(srcPointerId);
+ srcTimes.add(time);
+ }
+
+ final int startPos = 0;
+ dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords,
+ startPos, 0 /* length */);
+ assertEquals("size after append zero", dstLength, dst.getPointerSize());
assertIntArrayEquals("xCoordinates after append zero",
- dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
+ dstCopy.getXCoordinates(), startPos, dst.getXCoordinates(), startPos, dstLength);
assertIntArrayEquals("yCoordinates after append zero",
- dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
+ dstCopy.getYCoordinates(), startPos, dst.getYCoordinates(), startPos, dstLength);
assertIntArrayEquals("pointerIds after append zero",
- dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
+ dstCopy.getPointerIds(), startPos, dst.getPointerIds(), startPos, dstLength);
assertIntArrayEquals("times after append zero",
- dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
+ dstCopy.getTimes(), startPos, dst.getTimes(), startPos, dstLength);
- dst.append(src, 0, srcLen);
- assertEquals("size after append", dstLen + srcLen, dst.getPointerSize());
+ dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords,
+ startPos, srcLength);
+ assertEquals("size after append", dstLength + srcLength, dst.getPointerSize());
assertTrue("primitive length after append",
- dst.getPointerIds().length >= dstLen + srcLen);
+ dst.getPointerIds().length >= dstLength + srcLength);
assertIntArrayEquals("original xCoordinates values after append",
- dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
+ dstCopy.getXCoordinates(), startPos, dst.getXCoordinates(), startPos, dstLength);
assertIntArrayEquals("original yCoordinates values after append",
- dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
+ dstCopy.getYCoordinates(), startPos, dst.getYCoordinates(), startPos, dstLength);
assertIntArrayEquals("original pointerIds values after append",
- dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
+ dstCopy.getPointerIds(), startPos, dst.getPointerIds(), startPos, dstLength);
assertIntArrayEquals("original times values after append",
- dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
+ dstCopy.getTimes(), startPos, dst.getTimes(), startPos, dstLength);
assertIntArrayEquals("appended xCoordinates values after append",
- src.getXCoordinates(), 0, dst.getXCoordinates(), dstLen, srcLen);
+ srcXCoords.getPrimitiveArray(), startPos, dst.getXCoordinates(),
+ dstLength, srcLength);
assertIntArrayEquals("appended yCoordinates values after append",
- src.getYCoordinates(), 0, dst.getYCoordinates(), dstLen, srcLen);
+ srcYCoords.getPrimitiveArray(), startPos, dst.getYCoordinates(),
+ dstLength, srcLength);
assertIntArrayEquals("appended pointerIds values after append",
- src.getPointerIds(), 0, dst.getPointerIds(), dstLen, srcLen);
+ srcPointerIds.getPrimitiveArray(), startPos, dst.getPointerIds(),
+ dstLength, srcLength);
assertIntArrayEquals("appended times values after append",
- src.getTimes(), 0, dst.getTimes(), dstLen, srcLen);
+ srcTimes.getPrimitiveArray(), startPos, dst.getTimes(), dstLength, srcLength);
}
public void testAppendResizableIntArray() {
- final int srcLen = 100;
+ final int dstLength = 50;
+ final InputPointers dst = new InputPointers(DEFAULT_CAPACITY);
+ for (int i = 0; i < dstLength; i++) {
+ final int x = i * 4;
+ final int y = i * 3;
+ final int pointerId = i * 2;
+ final int time = i;
+ dst.addPointer(x, y, pointerId, time);
+ }
+ final InputPointers dstCopy = new InputPointers(DEFAULT_CAPACITY);
+ dstCopy.copy(dst);
+
+ final int srcLength = 100;
final int srcPointerId = 1;
- final int[] srcPointerIds = new int[srcLen];
+ final int[] srcPointerIds = new int[srcLength];
Arrays.fill(srcPointerIds, srcPointerId);
final ResizableIntArray srcTimes = new ResizableIntArray(DEFAULT_CAPACITY);
final ResizableIntArray srcXCoords = new ResizableIntArray(DEFAULT_CAPACITY);
final ResizableIntArray srcYCoords= new ResizableIntArray(DEFAULT_CAPACITY);
- for (int i = 0; i < srcLen; i++) {
- srcTimes.add(i * 2);
- srcXCoords.add(i * 3);
- srcYCoords.add(i * 4);
+ for (int i = 0; i < srcLength; i++) {
+ // The time value must be larger than <code>dst</code>.
+ final int time = i * 2 + dstLength;
+ final int x = i * 3;
+ final int y = i * 4;
+ srcTimes.add(time);
+ srcXCoords.add(x);
+ srcYCoords.add(y);
}
- final int dstLen = 50;
- final InputPointers dst = new InputPointers(DEFAULT_CAPACITY);
- for (int i = 0; i < dstLen; i++) {
- final int value = -i - 1;
- dst.addPointer(value * 4, value * 3, value * 2, value);
- }
- final InputPointers dstCopy = new InputPointers(DEFAULT_CAPACITY);
- dstCopy.copy(dst);
dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, 0);
- assertEquals("size after append zero", dstLen, dst.getPointerSize());
+ assertEquals("size after append zero", dstLength, dst.getPointerSize());
assertIntArrayEquals("xCoordinates after append zero",
- dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
+ dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLength);
assertIntArrayEquals("yCoordinates after append zero",
- dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
+ dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLength);
assertIntArrayEquals("pointerIds after append zero",
- dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
+ dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLength);
assertIntArrayEquals("times after append zero",
- dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
+ dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLength);
- dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, srcLen);
- assertEquals("size after append", dstLen + srcLen, dst.getPointerSize());
+ dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, srcLength);
+ assertEquals("size after append", dstLength + srcLength, dst.getPointerSize());
assertTrue("primitive length after append",
- dst.getPointerIds().length >= dstLen + srcLen);
+ dst.getPointerIds().length >= dstLength + srcLength);
assertIntArrayEquals("original xCoordinates values after append",
- dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
+ dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLength);
assertIntArrayEquals("original yCoordinates values after append",
- dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
+ dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLength);
assertIntArrayEquals("original pointerIds values after append",
- dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
+ dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLength);
assertIntArrayEquals("original times values after append",
- dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
+ dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLength);
assertIntArrayEquals("appended xCoordinates values after append",
- srcXCoords.getPrimitiveArray(), 0, dst.getXCoordinates(), dstLen, srcLen);
+ srcXCoords.getPrimitiveArray(), 0, dst.getXCoordinates(), dstLength, srcLength);
assertIntArrayEquals("appended yCoordinates values after append",
- srcYCoords.getPrimitiveArray(), 0, dst.getYCoordinates(), dstLen, srcLen);
+ srcYCoords.getPrimitiveArray(), 0, dst.getYCoordinates(), dstLength, srcLength);
assertIntArrayEquals("appended pointerIds values after append",
- srcPointerIds, 0, dst.getPointerIds(), dstLen, srcLen);
+ srcPointerIds, 0, dst.getPointerIds(), dstLength, srcLength);
assertIntArrayEquals("appended times values after append",
- srcTimes.getPrimitiveArray(), 0, dst.getTimes(), dstLen, srcLen);
+ srcTimes.getPrimitiveArray(), 0, dst.getTimes(), dstLength, srcLength);
}
// TODO: Consolidate this method with
@@ -250,14 +303,24 @@ public class InputPointersTests extends AndroidTestCase {
final int limit = 100;
final int shiftAmount = 20;
for (int i = 0; i < limit; i++) {
- src.addPointer(i, i * 2, i * 3, i * 4);
+ final int x = i;
+ final int y = i * 2;
+ final int pointerId = i * 3;
+ final int time = i * 4;
+ src.addPointer(x, y, pointerId, time);
}
src.shift(shiftAmount);
+ assertEquals("length after shift", src.getPointerSize(), limit - shiftAmount);
for (int i = 0; i < limit - shiftAmount; ++i) {
- assertEquals("xCoordinates at " + i, i + shiftAmount, src.getXCoordinates()[i]);
- assertEquals("yCoordinates at " + i, (i + shiftAmount) * 2, src.getYCoordinates()[i]);
- assertEquals("pointerIds at " + i, (i + shiftAmount) * 3, src.getPointerIds()[i]);
- assertEquals("times at " + i, (i + shiftAmount) * 4, src.getTimes()[i]);
+ final int oldIndex = i + shiftAmount;
+ final int x = oldIndex;
+ final int y = oldIndex * 2;
+ final int pointerId = oldIndex * 3;
+ final int time = oldIndex * 4;
+ assertEquals("xCoordinates at " + i, x, src.getXCoordinates()[i]);
+ assertEquals("yCoordinates at " + i, y, src.getYCoordinates()[i]);
+ assertEquals("pointerIds at " + i, pointerId, src.getPointerIds()[i]);
+ assertEquals("times at " + i, time, src.getTimes()[i]);
}
}
}
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index b9b52a6f3..986fb1097 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -25,35 +25,48 @@ import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.style.CharacterStyle;
import android.text.style.SuggestionSpan;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodSubtype;
import android.widget.EditText;
import android.widget.FrameLayout;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.settings.DebugSettings;
+import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.Locale;
+import java.util.concurrent.TimeUnit;
public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
+ private static final String TAG = InputTestsBase.class.getSimpleName();
- private static final String PREF_DEBUG_MODE = "debug_mode";
+ // Default value for auto-correction threshold. This is the string representation of the
+ // index in the resources array of auto-correction threshold settings.
+ private static final String DEFAULT_AUTO_CORRECTION_THRESHOLD = "1";
- // The message that sets the underline is posted with a 200 ms delay
- protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200;
+ // The message that sets the underline is posted with a 500 ms delay
+ protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 500;
// The message that sets predictions is posted with a 200 ms delay
protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200;
+ private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 60;
protected LatinIME mLatinIME;
protected Keyboard mKeyboard;
protected MyEditText mEditText;
protected View mInputView;
protected InputConnection mInputConnection;
+ private boolean mPreviousBigramPredictionSettings;
+ private String mPreviousAutoCorrectSetting;
// A helper class to ease span tests
public static class SpanGetter {
@@ -135,13 +148,30 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
final boolean previousSetting = prefs.getBoolean(key, defaultValue);
final SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(key, value);
- editor.commit();
+ editor.apply();
return previousSetting;
}
- // returns the previous setting value
- protected boolean setDebugMode(final boolean value) {
- return setBooleanPreference(PREF_DEBUG_MODE, value, false);
+ protected String setStringPreference(final String key, final String value,
+ final String defaultValue) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME);
+ final String previousSetting = prefs.getString(key, defaultValue);
+ final SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(key, value);
+ editor.apply();
+ return previousSetting;
+ }
+
+ protected void setDebugMode(final boolean value) {
+ setBooleanPreference(DebugSettings.PREF_DEBUG_MODE, value, false);
+ setBooleanPreference(Settings.PREF_KEY_IS_INTERNAL, value, false);
+ }
+
+ protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
+ // Some tests that inherit from us need to add some data in the EditorInfo (see
+ // AppWorkaroundsTests#enrichEditorInfo() for a concrete example of this). Since we
+ // control the EditorInfo, we supply a hook here for children to override.
+ return ei;
}
@Override
@@ -154,15 +184,19 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
mEditText.setEnabled(true);
setupService();
mLatinIME = getService();
- final boolean previousDebugSetting = setDebugMode(true);
+ setDebugMode(true);
+ mPreviousBigramPredictionSettings = setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS,
+ true, true /* defaultValue */);
+ mPreviousAutoCorrectSetting = setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD,
+ DEFAULT_AUTO_CORRECTION_THRESHOLD, DEFAULT_AUTO_CORRECTION_THRESHOLD);
mLatinIME.onCreate();
- setDebugMode(previousDebugSetting);
- final EditorInfo ei = new EditorInfo();
+ EditorInfo ei = new EditorInfo();
final InputConnection ic = mEditText.onCreateInputConnection(ei);
final LayoutInflater inflater =
(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ViewGroup vg = new FrameLayout(getContext());
mInputView = inflater.inflate(R.layout.input_view, vg);
+ ei = enrichEditorInfo(ei);
mLatinIME.onCreateInputMethodInterface().startInput(ic, ei);
mLatinIME.setInputView(mInputView);
mLatinIME.onBindInput();
@@ -170,6 +204,27 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
mLatinIME.onStartInputView(ei, false);
mInputConnection = ic;
changeLanguage("en_US");
+ // Run messages to avoid the messages enqueued by startInputView() and its friends
+ // to run on a later call and ruin things. We need to wait first because some of them
+ // can be posted with a delay (notably, MSG_RESUME_SUGGESTIONS)
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mLatinIME.onFinishInputView(true);
+ mLatinIME.onFinishInput();
+ runMessages();
+ mLatinIME.mHandler.removeAllMessages();
+ setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mPreviousBigramPredictionSettings,
+ true /* defaultValue */);
+ setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, mPreviousAutoCorrectSetting,
+ DEFAULT_AUTO_CORRECTION_THRESHOLD);
+ setDebugMode(false);
+ mLatinIME.recycle();
+ super.tearDown();
+ mLatinIME = null;
}
// We need to run the messages added to the handler from LatinIME. The only way to do
@@ -199,7 +254,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
}
// type(int) and type(String): helper methods to send a code point resp. a string to LatinIME.
- protected void type(final int codePoint) {
+ protected void typeInternal(final int codePoint, final boolean isKeyRepeat) {
// onPressKey and onReleaseKey are explicitly deactivated here, but they do happen in the
// code (although multitouch/slide input and other factors make the sequencing complicated).
// They are supposed to be entirely deconnected from the input logic from LatinIME point of
@@ -208,60 +263,90 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
// but keep them in mind if something breaks. Commenting them out as is should work.
//mLatinIME.onPressKey(codePoint, 0 /* repeatCount */, true /* isSinglePointer */);
final Key key = mKeyboard.getKey(codePoint);
- if (key != null) {
+ if (key == null) {
+ mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
+ isKeyRepeat);
+ } else {
final int x = key.getX() + key.getWidth() / 2;
final int y = key.getY() + key.getHeight() / 2;
- mLatinIME.onCodeInput(codePoint, x, y);
- return;
+ mLatinIME.onCodeInput(codePoint, x, y, isKeyRepeat);
}
- mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ // Also see the comment at the top of this function about onReleaseKey
//mLatinIME.onReleaseKey(codePoint, false /* withSliding */);
}
+ protected void type(final int codePoint) {
+ typeInternal(codePoint, false /* isKeyRepeat */);
+ }
+
+ protected void repeatKey(final int codePoint) {
+ typeInternal(codePoint, true /* isKeyRepeat */);
+ }
+
protected void type(final String stringToType) {
for (int i = 0; i < stringToType.length(); i = stringToType.offsetByCodePoints(i, 1)) {
type(stringToType.codePointAt(i));
}
}
- protected void waitForDictionaryToBeLoaded() {
- int remainingAttempts = 300;
- while (remainingAttempts > 0 && mLatinIME.isCurrentlyWaitingForMainDictionary()) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- // Don't do much
- } finally {
- --remainingAttempts;
- }
+ protected void waitForDictionariesToBeLoaded() {
+ try {
+ mLatinIME.waitForLoadingDictionaries(
+ TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted during waiting for loading main dictionary.", e);
}
}
protected void changeLanguage(final String locale) {
- changeLanguageWithoutWait(locale);
- waitForDictionaryToBeLoaded();
+ changeLanguage(locale, null);
+ }
+
+ protected void changeLanguage(final String locale, final String combiningSpec) {
+ changeLanguageWithoutWait(locale, combiningSpec);
+ waitForDictionariesToBeLoaded();
}
- protected void changeLanguageWithoutWait(final String locale) {
+ protected void changeLanguageWithoutWait(final String locale, final String combiningSpec) {
mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
- SubtypeSwitcher.getInstance().forceLocale(mEditText.mCurrentLocale);
- mLatinIME.loadKeyboard();
+ // TODO: this is forcing a QWERTY keyboard for all locales, which is wrong.
+ // It's still better than using whatever keyboard is the current one, but we
+ // should actually use the default keyboard for this locale.
+ // TODO: Use {@link InputMethodSubtype.InputMethodSubtypeBuilder} directly or indirectly so
+ // that {@link InputMethodSubtype#isAsciiCapable} can return the correct value.
+ final String EXTRA_VALUE_FOR_TEST =
+ "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE
+ + null == combiningSpec ? "" : ("," + combiningSpec);
+ final InputMethodSubtype subtype = InputMethodSubtypeCompatUtils.newInputMethodSubtype(
+ R.string.subtype_no_language_qwerty,
+ R.drawable.ic_ime_switcher_dark,
+ locale,
+ Constants.Subtype.KEYBOARD_MODE,
+ EXTRA_VALUE_FOR_TEST,
+ false /* isAuxiliary */,
+ false /* overridesImplicitlyEnabledSubtype */,
+ 0 /* id */);
+ SubtypeSwitcher.getInstance().forceSubtype(subtype);
+ mLatinIME.onCurrentInputMethodSubtypeChanged(subtype);
runMessages();
mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
+ mLatinIME.clearPersonalizedDictionariesForTest();
}
protected void changeKeyboardLocaleAndDictLocale(final String keyboardLocale,
final String dictLocale) {
changeLanguage(keyboardLocale);
if (!keyboardLocale.equals(dictLocale)) {
- mLatinIME.replaceMainDictionaryForTest(
- LocaleUtils.constructLocaleFromString(dictLocale));
+ mLatinIME.replaceDictionariesForTest(LocaleUtils.constructLocaleFromString(dictLocale));
}
- waitForDictionaryToBeLoaded();
+ waitForDictionariesToBeLoaded();
}
- protected void pickSuggestionManually(final int index, final String suggestion) {
- mLatinIME.pickSuggestionManually(index, new SuggestedWordInfo(suggestion, 1,
+ protected void pickSuggestionManually(final String suggestion) {
+ mLatinIME.pickSuggestionManually(new SuggestedWordInfo(suggestion, 1,
SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */,
SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
index 5e98cdf8d..f5e993de8 100644
--- a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
+++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
@@ -30,10 +30,10 @@ public class LatinImeStressTests extends InputTestsBase {
final int maxWordCountToTypeInEachIteration = 20;
final long seed = System.currentTimeMillis();
final Random random = new Random(seed);
- final int codePointSetSize = 30;
final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER;
for (int i = 0; i < switchCount; ++i) {
- changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+ changeLanguageWithoutWait(locales[random.nextInt(locales.length)],
+ null /* combiningSpec */);
final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
for (int j = 0; j < wordCount; ++j) {
final String word = CodePointUtils.generateWord(random, codePointSet);
@@ -41,7 +41,7 @@ public class LatinImeStressTests extends InputTestsBase {
}
}
}
- public void testSwitchLanguagesAndInputRandamCodePoints() {
+ public void testSwitchLanguagesAndInputRandomCodePoints() {
final String[] locales = {"en_US", "de", "el", "es", "fi", "it", "nl", "pt", "ru"};
final int switchCount = 50;
final int maxWordCountToTypeInEachIteration = 20;
@@ -50,7 +50,8 @@ public class LatinImeStressTests extends InputTestsBase {
final int codePointSetSize = 30;
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
for (int i = 0; i < switchCount; ++i) {
- changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+ changeLanguageWithoutWait(locales[random.nextInt(locales.length)],
+ null /* combiningSpec */);
final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
for (int j = 0; j < wordCount; ++j) {
final String word = CodePointUtils.generateWord(random, codePointSet);
diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
index 84ff6b307..64750fbda 100644
--- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java
+++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin;
+import android.provider.Settings.Secure;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.inputmethod.latin.R;
@@ -40,9 +41,9 @@ public class PunctuationTests extends InputTestsBase {
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
assertTrue("type word then type space should display punctuation strip",
- mLatinIME.isShowingPunctuationList());
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
+ mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions());
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
assertEquals("type word then type space then punctuation from strip twice",
EXPECTED_RESULT, mEditText.getText().toString());
} finally {
@@ -65,9 +66,9 @@ public class PunctuationTests extends InputTestsBase {
final String PUNCTUATION_FROM_STRIP = "!";
final String EXPECTED_RESULT = "this!! is";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
+ pickSuggestionManually(WORD1_TO_TYPE);
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
+ pickSuggestionManually(PUNCTUATION_FROM_STRIP);
type(WORD2_TO_TYPE);
assertEquals("pick word then pick punctuation twice then type", EXPECTED_RESULT,
mEditText.getText().toString());
@@ -78,8 +79,8 @@ public class PunctuationTests extends InputTestsBase {
final String WORD2_TO_PICK = "!is";
final String EXPECTED_RESULT = "this!is";
type(WORD1_TO_TYPE);
- pickSuggestionManually(0, WORD1_TO_TYPE);
- pickSuggestionManually(1, WORD2_TO_PICK);
+ pickSuggestionManually(WORD1_TO_TYPE);
+ pickSuggestionManually(WORD2_TO_PICK);
assertEquals("manual pick then manual pick a word with punct at start", EXPECTED_RESULT,
mEditText.getText().toString());
}
@@ -89,7 +90,7 @@ public class PunctuationTests extends InputTestsBase {
final String PUNCTUATION = ":";
final String EXPECTED_RESULT = "this:";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
type(PUNCTUATION);
assertEquals("manually pick word then colon",
EXPECTED_RESULT, mEditText.getText().toString());
@@ -100,7 +101,7 @@ public class PunctuationTests extends InputTestsBase {
final String PUNCTUATION = "(";
final String EXPECTED_RESULT = "this (";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
type(PUNCTUATION);
assertEquals("manually pick word then open paren",
EXPECTED_RESULT, mEditText.getText().toString());
@@ -111,7 +112,7 @@ public class PunctuationTests extends InputTestsBase {
final String PUNCTUATION = ")";
final String EXPECTED_RESULT = "this)";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
type(PUNCTUATION);
assertEquals("manually pick word then close paren",
EXPECTED_RESULT, mEditText.getText().toString());
@@ -122,7 +123,7 @@ public class PunctuationTests extends InputTestsBase {
final String SPECIAL_KEY = ":-)";
final String EXPECTED_RESULT = "this :-)";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
mLatinIME.onTextInput(SPECIAL_KEY);
assertEquals("manually pick word then press the smiley key",
EXPECTED_RESULT, mEditText.getText().toString());
@@ -133,7 +134,7 @@ public class PunctuationTests extends InputTestsBase {
final String SPECIAL_KEY = ".com";
final String EXPECTED_RESULT = "this.com";
type(WORD_TO_TYPE);
- pickSuggestionManually(0, WORD_TO_TYPE);
+ pickSuggestionManually(WORD_TO_TYPE);
mLatinIME.onTextInput(SPECIAL_KEY);
assertEquals("manually pick word then press the .com key",
EXPECTED_RESULT, mEditText.getText().toString());
@@ -153,7 +154,9 @@ public class PunctuationTests extends InputTestsBase {
final String WORD_TO_TYPE = "you'f ";
final String EXPECTED_RESULT = "you'd ";
type(WORD_TO_TYPE);
- assertEquals("auto-correction with single quote inside",
+ assertEquals("auto-correction with single quote inside. ID = "
+ + Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID)
+ + " ; Suggestions = " + mLatinIME.getSuggestedWordsForTest(),
EXPECTED_RESULT, mEditText.getText().toString());
}
@@ -161,7 +164,37 @@ public class PunctuationTests extends InputTestsBase {
final String WORD_TO_TYPE = "'tgis' ";
final String EXPECTED_RESULT = "'this' ";
type(WORD_TO_TYPE);
- assertEquals("auto-correction with single quotes around",
+ assertEquals("auto-correction with single quotes around. ID = "
+ + Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID)
+ + " ; Suggestions = " + mLatinIME.getSuggestedWordsForTest(),
+ EXPECTED_RESULT, mEditText.getText().toString());
+ }
+
+ public void testAutoSpaceWithDoubleQuotes() {
+ final String STRING_TO_TYPE = "He said\"hello\"to me. I replied,\"hi\"."
+ + "Then, 5\"passed. He said\"bye\"and left.";
+ final String EXPECTED_RESULT = "He said \"hello\" to me. I replied, \"hi\". "
+ + "Then, 5\" passed. He said \"bye\" and left. \"";
+ // Split by double quote, so that we can type the double quotes individually.
+ for (final String partToType : STRING_TO_TYPE.split("\"")) {
+ // Split at word boundaries. This regexp means "anywhere that is preceded
+ // by a word character but not followed by a word character, OR that is not
+ // preceded by a word character but followed by a word character".
+ // We need to input word by word because auto-spaces are only active when
+ // manually picking or gesturing (which we can't simulate yet), but only words
+ // can be picked.
+ final String[] wordsToType = partToType.split("(?<=\\w)(?!\\w)|(?<!\\w)(?=\\w)");
+ for (final String wordToType : wordsToType) {
+ type(wordToType);
+ if (wordToType.matches("^\\w+$")) {
+ // Only pick selection if that was a word, because if that was not a word,
+ // then we don't have a composition.
+ pickSuggestionManually(wordToType);
+ }
+ }
+ type("\"");
+ }
+ assertEquals("auto-space with double quotes",
EXPECTED_RESULT, mEditText.getText().toString());
}
}
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
index c0dd9933c..199922491 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
@@ -16,15 +16,13 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.utils.TextRange;
-
+import android.content.res.Resources;
import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableString;
-import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
import android.view.inputmethod.ExtractedText;
@@ -32,6 +30,14 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.PrevWordsInfoUtils;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.ScriptUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
+import com.android.inputmethod.latin.utils.TextRange;
+
import java.util.Locale;
@SmallTest
@@ -39,11 +45,19 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
// The following is meant to be a reasonable default for
// the "word_separators" resource.
- private static final String sSeparators = ".,:;!?-";
+ private SpacingAndPunctuations mSpacingAndPunctuations;
@Override
protected void setUp() throws Exception {
super.setUp();
+ final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(final Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ final Resources res = getContext().getResources();
+ mSpacingAndPunctuations = job.runInLocale(res, Locale.ENGLISH);
}
private class MockConnection extends InputConnectionWrapper {
@@ -78,6 +92,10 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
mExtractedText = extractedText;
}
+ public int cursorPos() {
+ return mTextBefore.length();
+ }
+
/* (non-Javadoc)
* @see android.view.inputmethod.InputConnectionWrapper#getTextBeforeCursor(int, int)
*/
@@ -120,13 +138,16 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
}
private class MockInputMethodService extends InputMethodService {
- InputConnection mInputConnection;
- public void setInputConnection(final InputConnection inputConnection) {
- mInputConnection = inputConnection;
+ private MockConnection mMockConnection;
+ public void setInputConnection(final MockConnection mockConnection) {
+ mMockConnection = mockConnection;
+ }
+ public int cursorPos() {
+ return mMockConnection.cursorPos();
}
@Override
public InputConnection getCurrentInputConnection() {
- return mInputConnection;
+ return mMockConnection;
}
}
@@ -137,9 +158,26 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
*/
public void testGetPreviousWord() {
// If one of the following cases breaks, the bigram suggestions won't work.
- assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSeparators, 2), "abc");
- assertNull(RichInputConnection.getNthPreviousWord("abc", sSeparators, 2));
- assertNull(RichInputConnection.getNthPreviousWord("abc. def", sSeparators, 2));
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+
+ assertFalse(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
+ assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
+
+ // For n-gram
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[1].mWord, "abc");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[1],
+ WordInfo.BEGINNING_OF_SENTENCE);
// The following tests reflect the current behavior of the function
// RichInputConnection#getNthPreviousWord.
@@ -148,20 +186,46 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
// this function if needed - especially since it does not seem very
// logical. These tests are just there to catch any unintentional
// changes in the behavior of the RichInputConnection#getPreviousWord method.
- assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSeparators, 2), "abc");
- assertEquals(RichInputConnection.getNthPreviousWord("abc def.", sSeparators, 2), "abc");
- assertEquals(RichInputConnection.getNthPreviousWord("abc def .", sSeparators, 2), "def");
- assertNull(RichInputConnection.getNthPreviousWord("abc ", sSeparators, 2));
-
- assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSeparators, 1), "def");
- assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSeparators, 1), "def");
- assertNull(RichInputConnection.getNthPreviousWord("abc def.", sSeparators, 1));
- assertNull(RichInputConnection.getNthPreviousWord("abc def .", sSeparators, 1));
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def ", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def.", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def .", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "def");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def ", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc 'def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "'def");
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc def .", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc, def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc? def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc! def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
+ assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+ "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
}
/**
* Test logic in getting the word range at the cursor.
*/
+ private static final int[] SPACE = { Constants.CODE_SPACE };
+ static final int[] TAB = { Constants.CODE_TAB };
+ private static final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t");
+ // A character that needs surrogate pair to represent its code point (U+2008A).
+ private static final String SUPPLEMENTARY_CHAR = "\uD840\uDC8A";
+ private static final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお
+ private static final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και
+
public void testGetWordRangeAtCursor() {
ExtractedText et = new ExtractedText();
final MockInputMethodService mockInputMethodService = new MockInputMethodService();
@@ -173,50 +237,42 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
ic.beginBatchEdit();
// basic case
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
assertTrue(TextUtils.equals("word", r.mWord));
- // more than one word
- r = ic.getWordRangeAtCursor(" ", 1);
- assertTrue(TextUtils.equals("word word", r.mWord));
- ic.endBatchEdit();
-
// tab character instead of space
mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et));
ic.beginBatchEdit();
- r = ic.getWordRangeAtCursor("\t", 1);
+ r = ic.getWordRangeAtCursor(TAB, ScriptUtils.SCRIPT_LATIN);
ic.endBatchEdit();
- assertTrue(TextUtils.equals("word\tword", r.mWord));
-
- // only one word doesn't go too far
- mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et));
- ic.beginBatchEdit();
- r = ic.getWordRangeAtCursor("\t", 1);
- ic.endBatchEdit();
- assertTrue(TextUtils.equals("word\tword", r.mWord));
+ assertTrue(TextUtils.equals("word", r.mWord));
- // tab or space
- mockInputMethodService.setInputConnection(new MockConnection("one word\two", "rd", et));
+ // splitting on supplementary character
+ mockInputMethodService.setInputConnection(
+ new MockConnection("one word" + SUPPLEMENTARY_CHAR + "wo", "rd", et));
ic.beginBatchEdit();
- r = ic.getWordRangeAtCursor(" \t", 1);
+ r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+ ScriptUtils.SCRIPT_LATIN);
ic.endBatchEdit();
- assertTrue(TextUtils.equals("word\tword", r.mWord));
+ assertTrue(TextUtils.equals("word", r.mWord));
- // tab or space multiword
- mockInputMethodService.setInputConnection(new MockConnection("one word\two", "rd", et));
+ // split on chars outside the specified script
+ mockInputMethodService.setInputConnection(
+ new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et));
ic.beginBatchEdit();
- r = ic.getWordRangeAtCursor(" \t", 2);
+ r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+ ScriptUtils.SCRIPT_LATIN);
ic.endBatchEdit();
- assertTrue(TextUtils.equals("one word\tword", r.mWord));
+ assertTrue(TextUtils.equals("word", r.mWord));
- // splitting on supplementary character
- final String supplementaryChar = "\uD840\uDC8A";
+ // likewise for greek
mockInputMethodService.setInputConnection(
- new MockConnection("one word" + supplementaryChar + "wo", "rd", et));
+ new MockConnection("text" + GREEK_WORD, "text", et));
ic.beginBatchEdit();
- r = ic.getWordRangeAtCursor(supplementaryChar, 0);
+ r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
+ ScriptUtils.SCRIPT_GREEK);
ic.endBatchEdit();
- assertTrue(TextUtils.equals("word", r.mWord));
+ assertTrue(TextUtils.equals(GREEK_WORD, r.mWord));
}
/**
@@ -244,7 +300,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
TextRange r;
SuggestionSpan[] suggestions;
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 1);
MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -256,7 +312,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
10 /* start */, 16 /* end */, 0 /* flags */);
mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 2);
MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -269,7 +325,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
5 /* start */, 16 /* end */, 0 /* flags */);
mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 1);
MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -281,7 +337,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
10 /* start */, 20 /* end */, 0 /* flags */);
mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 1);
MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -293,7 +349,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
5 /* start */, 20 /* end */, 0 /* flags */);
mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 1);
MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
@@ -305,8 +361,86 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
5 /* start */, 20 /* end */, 0 /* flags */);
mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
- r = ic.getWordRangeAtCursor(" ", 0);
+ r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
suggestions = r.getSuggestionSpansAtWord();
assertEquals(suggestions.length, 0);
}
+
+ public void testCursorTouchingWord() {
+ final MockInputMethodService ims = new MockInputMethodService();
+ final RichInputConnection ic = new RichInputConnection(ims);
+ final SpacingAndPunctuations sap = mSpacingAndPunctuations;
+
+ ims.setInputConnection(new MockConnection("users", 5));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("users'", 5));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("users'", 6));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("'users'", 6));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("'users'", 7));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("users '", 6));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("users '", 7));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("re-", 3));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("re--", 4));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("-", 1));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection("--", 2));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" -", 2));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" --", 3));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" users '", 1));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" users '", 3));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" users '", 7));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" users are", 7));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertTrue(ic.isCursorTouchingWord(sap));
+
+ ims.setInputConnection(new MockConnection(" users 'are", 7));
+ ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
+ assertFalse(ic.isCursorTouchingWord(sap));
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java
new file mode 100644
index 000000000..db3c9baa9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.WordComposer;
+
+@LargeTest
+public class ShiftModeTests extends InputTestsBase {
+
+ @Override
+ protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
+ ei.inputType |= TextUtils.CAP_MODE_SENTENCES;
+ ei.initialCapsMode = TextUtils.CAP_MODE_SENTENCES;
+ return ei;
+ }
+
+ private boolean isCapsModeAutoShifted() {
+ return mLatinIME.mKeyboardSwitcher.getKeyboardShiftMode()
+ == WordComposer.CAPS_MODE_AUTO_SHIFTED;
+ }
+
+ public void testTypicalSentence() {
+ assertTrue("Initial auto caps state", isCapsModeAutoShifted());
+ type("Test");
+ assertFalse("Caps after letter", isCapsModeAutoShifted());
+ type(" ");
+ assertFalse("Caps after space", isCapsModeAutoShifted());
+ type("some,");
+ assertFalse("Caps after comma", isCapsModeAutoShifted());
+ type(" ");
+ assertFalse("Caps after comma space", isCapsModeAutoShifted());
+ type("words.");
+ assertFalse("Caps directly after period", isCapsModeAutoShifted());
+ type(" ");
+ assertTrue("Caps after period space", isCapsModeAutoShifted());
+ }
+
+ public void testBackspace() {
+ assertTrue("Initial auto caps state", isCapsModeAutoShifted());
+ type("A");
+ assertFalse("Caps state after one letter", isCapsModeAutoShifted());
+ type(Constants.CODE_DELETE);
+ assertTrue("Auto caps state at start after delete", isCapsModeAutoShifted());
+ }
+
+ public void testRepeatingBackspace() {
+ final String SENTENCE_TO_TYPE = "Test sentence. Another.";
+ final int BACKSPACE_COUNT =
+ SENTENCE_TO_TYPE.length() - SENTENCE_TO_TYPE.lastIndexOf(' ') - 1;
+
+ type(SENTENCE_TO_TYPE);
+ assertFalse("Caps after typing \"" + SENTENCE_TO_TYPE + "\"", isCapsModeAutoShifted());
+ type(Constants.CODE_DELETE);
+ for (int i = 1; i < BACKSPACE_COUNT; ++i) {
+ repeatKey(Constants.CODE_DELETE);
+ }
+ assertFalse("Caps immediately after repeating Backspace a lot", isCapsModeAutoShifted());
+ sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+ runMessages();
+ assertTrue("Caps after a while after repeating Backspace a lot", isCapsModeAutoShifted());
+ }
+
+ public void testAutoCapsAfterDigitsPeriod() {
+ changeLanguage("en");
+ type("On 22.11.");
+ assertFalse("(English) Auto caps after digits-period", isCapsModeAutoShifted());
+ type(" ");
+ assertTrue("(English) Auto caps after digits-period-whitespace", isCapsModeAutoShifted());
+ mEditText.setText("");
+ changeLanguage("fr");
+ type("Le 22.");
+ assertFalse("(French) Auto caps after digits-period", isCapsModeAutoShifted());
+ type(" ");
+ assertTrue("(French) Auto caps after digits-period-whitespace", isCapsModeAutoShifted());
+ mEditText.setText("");
+ changeLanguage("de");
+ type("Am 22.");
+ assertFalse("(German) Auto caps after digits-period", isCapsModeAutoShifted());
+ type(" ");
+ // For German, no auto-caps in this case
+ assertFalse("(German) Auto caps after digits-period-whitespace", isCapsModeAutoShifted());
+ }
+
+ public void testAutoCapsAfterInvertedMarks() {
+ changeLanguage("es");
+ assertTrue("(Spanish) Auto caps at start", isCapsModeAutoShifted());
+ type("Hey. ¿");
+ assertTrue("(Spanish) Auto caps after inverted what", isCapsModeAutoShifted());
+ mEditText.setText("");
+ type("¡");
+ assertTrue("(Spanish) Auto caps after inverted bang", isCapsModeAutoShifted());
+ }
+
+ public void testOtherSentenceSeparators() {
+ changeLanguage("hy_AM");
+ assertTrue("(Armenian) Auto caps at start", isCapsModeAutoShifted());
+ type("Hey. ");
+ assertFalse("(Armenian) No auto-caps after latin period", isCapsModeAutoShifted());
+ type("Hey\u0589");
+ assertFalse("(Armenian) No auto-caps directly after armenian period",
+ isCapsModeAutoShifted());
+ type(" ");
+ assertTrue("(Armenian) Auto-caps after armenian period-whitespace",
+ isCapsModeAutoShifted());
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index 375352067..66b4a9c71 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -16,12 +16,10 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.ArrayList;
import java.util.Locale;
@@ -33,7 +31,7 @@ public class SuggestedWordsTests extends AndroidTestCase {
final String TYPED_WORD = "typed";
final int TYPED_WORD_FREQ = 5;
final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
- final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> list = new ArrayList<>();
list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ,
SuggestedWordInfo.KIND_TYPED, null /* sourceDict */,
SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
@@ -46,24 +44,23 @@ public class SuggestedWordsTests extends AndroidTestCase {
}
final SuggestedWords words = new SuggestedWords(
- list,
+ list, null /* rawSuggestions */,
false /* typedWordValid */,
false /* willAutoCorrect */,
- false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction*/);
assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
assertEquals("typed", words.getWord(0));
- assertEquals(SuggestedWordInfo.KIND_TYPED, words.getInfo(0).mKind);
+ assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED));
assertEquals("0", words.getWord(1));
- assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(1).mKind);
+ assertTrue(words.getInfo(1).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
assertEquals("4", words.getWord(5));
- assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(5).mKind);
+ assertTrue(words.getInfo(5).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord();
assertEquals(words.size() - 1, wordsWithoutTyped.size());
assertEquals("0", wordsWithoutTyped.getWord(0));
- assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind);
+ assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
}
// Helper for testGetTransformedWordInfo
diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
index 1434c6b63..c44544f3d 100644
--- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java
+++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
@@ -19,6 +19,9 @@ package com.android.inputmethod.latin;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
+
/**
* Unit tests for WordComposer.
*/
@@ -26,10 +29,19 @@ import android.test.suitebuilder.annotation.SmallTest;
public class WordComposerTests extends AndroidTestCase {
public void testMoveCursor() {
final WordComposer wc = new WordComposer();
+ // BMP is the Basic Multilingual Plane, as defined by Unicode. This includes
+ // most characters for most scripts, including all Roman alphabet languages,
+ // CJK, Arabic, Hebrew. Notable exceptions include some emoji and some
+ // very rare Chinese ideograms. BMP characters can be encoded on 2 bytes
+ // in UTF-16, whereas those outside the BMP need 4 bytes.
+ // http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
final String STR_WITHIN_BMP = "abcdef";
- wc.setComposingWord(STR_WITHIN_BMP, null);
- assertEquals(wc.size(),
- STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length()));
+ final int[] CODEPOINTS_WITHIN_BMP = StringUtils.toCodePointArray(STR_WITHIN_BMP);
+ final int[] COORDINATES_WITHIN_BMP =
+ CoordinateUtils.newCoordinateArray(CODEPOINTS_WITHIN_BMP.length,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ wc.setComposingWord(CODEPOINTS_WITHIN_BMP, COORDINATES_WITHIN_BMP);
+ assertEquals(wc.size(), STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length()));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
wc.setCursorPositionWithinWord(2);
assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
@@ -46,12 +58,20 @@ public class WordComposerTests extends AndroidTestCase {
// Move the cursor past the end of the word
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15));
+ // Do what LatinIME does when the cursor is moved outside of the word,
+ // and check the behavior is correct.
+ wc.reset();
// \uD861\uDED7 is 𨛗, a character outside the BMP
final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh";
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
- assertEquals(wc.size(), STR_WITH_SUPPLEMENTARY_CHAR.codePointCount(0,
- STR_WITH_SUPPLEMENTARY_CHAR.length()));
+ final int[] CODEPOINTS_WITH_SUPPLEMENTARY_CHAR =
+ StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR);
+ final int[] COORDINATES_WITH_SUPPLEMENTARY_CHAR =
+ CoordinateUtils.newCoordinateArray(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
+ assertEquals(wc.size(), CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length);
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
wc.setCursorPositionWithinWord(3);
assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
@@ -60,33 +80,42 @@ public class WordComposerTests extends AndroidTestCase {
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
wc.setCursorPositionWithinWord(3);
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));
- wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
+ wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR,
+ COORDINATES_WITH_SUPPLEMENTARY_CHAR);
wc.setCursorPositionWithinWord(2);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
new file mode 100644
index 000000000..bc856f113
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+/**
+ * A base class of the binary dictionary decoder.
+ */
+public abstract class AbstractDictDecoder implements DictDecoder {
+ private static final int SUCCESS = 0;
+ private static final int ERROR_CANNOT_READ = 1;
+ private static final int ERROR_WRONG_FORMAT = 2;
+
+ @Override @UsedForTesting
+ public int getTerminalPosition(final String word)
+ throws IOException, UnsupportedFormatException {
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
+ }
+ return BinaryDictIOUtils.getTerminalPosition(this, word);
+ }
+
+ @Override @UsedForTesting
+ public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words,
+ final TreeMap<Integer, Integer> frequencies,
+ final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams)
+ throws IOException, UnsupportedFormatException {
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
+ }
+ BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams);
+ }
+
+ /**
+ * Check whether the header contains the expected information. This is a no-error method,
+ * that will return an error code and never throw a checked exception.
+ * @return an error code, either ERROR_* or SUCCESS.
+ */
+ private int checkHeader() {
+ try {
+ readHeader();
+ } catch (IOException e) {
+ return ERROR_CANNOT_READ;
+ } catch (UnsupportedFormatException e) {
+ return ERROR_WRONG_FORMAT;
+ }
+ return SUCCESS;
+ }
+
+ @Override
+ public boolean hasValidRawBinaryDictionary() {
+ return checkHeader() == SUCCESS;
+ }
+
+ // Placeholder implementations below. These are actually unused.
+ @Override
+ public void openDictBuffer() throws FileNotFoundException, IOException,
+ UnsupportedFormatException {
+ }
+
+ @Override
+ public boolean isDictBufferOpen() {
+ return false;
+ }
+
+ @Override
+ public PtNodeInfo readPtNode(final int ptNodePos) {
+ return null;
+ }
+
+ @Override
+ public void setPosition(int newPos) {
+ }
+
+ @Override
+ public int getPosition() {
+ return 0;
+ }
+
+ @Override
+ public int readPtNodeCount() {
+ return 0;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 32c07e106..406046a74 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -17,30 +17,28 @@
package com.android.inputmethod.latin.makedict;
import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
+import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
@@ -52,39 +50,21 @@ import java.util.TreeMap;
@LargeTest
public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
private static final String TAG = BinaryDictDecoderEncoderTests.class.getSimpleName();
- private static final int DEFAULT_MAX_UNIGRAMS = 100;
+ private static final int DEFAULT_MAX_UNIGRAMS = 300;
private static final int DEFAULT_CODE_POINT_SET_SIZE = 50;
+ private static final int LARGE_CODE_POINT_SET_SIZE = 300;
private static final int UNIGRAM_FREQ = 10;
private static final int BIGRAM_FREQ = 50;
private static final int TOLERANCE_OF_BIGRAM_FREQ = 5;
private static final int NUM_OF_NODES_HAVING_SHORTCUTS = 50;
private static final int NUM_OF_SHORTCUTS = 5;
- private static final int USE_BYTE_ARRAY = 1;
- private static final int USE_BYTE_BUFFER = 2;
-
- private static final ArrayList<String> sWords = CollectionUtils.newArrayList();
- private static final SparseArray<List<Integer>> sEmptyBigrams =
- CollectionUtils.newSparseArray();
- private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray();
- private static final SparseArray<List<Integer>> sChainBigrams =
- CollectionUtils.newSparseArray();
- private static final HashMap<String, List<String>> sShortcuts = CollectionUtils.newHashMap();
-
- private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2);
- private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE =
- new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */);
- private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE =
- new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */);
- private static final FormatSpec.FormatOptions VERSION4_WITHOUT_DYNAMIC_UPDATE =
- new FormatSpec.FormatOptions(4, false /* supportsDynamicUpdate */);
- private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE =
- new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */);
- private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP =
- new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */,
- true /* hasTimestamp */);
-
- private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+ private static final ArrayList<String> sWords = new ArrayList<>();
+ private static final ArrayList<String> sWordsWithVariousCodePoints = new ArrayList<>();
+ private static final SparseArray<List<Integer>> sEmptyBigrams = new SparseArray<>();
+ private static final SparseArray<List<Integer>> sStarBigrams = new SparseArray<>();
+ private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>();
+ private static final HashMap<String, List<String>> sShortcuts = new HashMap<>();
public BinaryDictDecoderEncoderTests() {
this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
@@ -92,12 +72,12 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
public BinaryDictDecoderEncoderTests(final long seed, final int maxUnigrams) {
super();
+ BinaryDictionaryUtils.setCurrentTimeForTest(0);
Log.e(TAG, "Testing dictionary: seed is " + seed);
final Random random = new Random(seed);
sWords.clear();
- final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
- random);
- generateWords(maxUnigrams, random, codePointSet);
+ sWordsWithVariousCodePoints.clear();
+ generateWords(maxUnigrams, random);
for (int i = 0; i < sWords.size(); ++i) {
sChainBigrams.put(i, new ArrayList<Integer>());
@@ -124,23 +104,35 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
}
- private DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) {
- if (formatOptions.mVersion == FormatSpec.VERSION4) {
- return new Ver4DictEncoder(getContext().getCacheDir());
- } else if (formatOptions.mVersion == 3 || formatOptions.mVersion == 2) {
- return new Ver3DictEncoder(file);
- } else {
- throw new RuntimeException("The format option has a wrong version : "
- + formatOptions.mVersion);
- }
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ BinaryDictionaryUtils.setCurrentTimeForTest(0);
}
- private void generateWords(final int number, final Random random, final int[] codePointSet) {
- final Set<String> wordSet = CollectionUtils.newHashSet();
+ @Override
+ protected void tearDown() throws Exception {
+ // Quit test mode.
+ BinaryDictionaryUtils.setCurrentTimeForTest(-1);
+ super.tearDown();
+ }
+
+ private void generateWords(final int number, final Random random) {
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
+ random);
+ final Set<String> wordSet = new HashSet<>();
while (wordSet.size() < number) {
wordSet.add(CodePointUtils.generateWord(random, codePointSet));
}
sWords.addAll(wordSet);
+
+ final int[] largeCodePointSet = CodePointUtils.generateCodePointSet(
+ LARGE_CODE_POINT_SET_SIZE, random);
+ wordSet.clear();
+ while (wordSet.size() < number) {
+ wordSet.add(CodePointUtils.generateWord(random, largeCodePointSet));
+ }
+ sWordsWithVariousCodePoints.addAll(wordSet);
}
/**
@@ -150,14 +142,14 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final List<String> words, final HashMap<String, List<String>> shortcutMap) {
for (int i = 0; i < number; ++i) {
final String word = words.get(i);
- final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList();
+ final ArrayList<WeightedString> shortcuts = new ArrayList<>();
if (shortcutMap != null && shortcutMap.containsKey(word)) {
for (final String shortcut : shortcutMap.get(word)) {
shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ));
}
}
- dict.add(word, UNIGRAM_FREQ, (shortcutMap == null) ? null : shortcuts,
- false /* isNotAWord */);
+ dict.add(word, new ProbabilityInfo(UNIGRAM_FREQ),
+ (shortcutMap == null) ? null : shortcuts, false /* isNotAWord */);
}
}
@@ -167,7 +159,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
for (int i = 0; i < bigrams.size(); ++i) {
final int w1 = bigrams.keyAt(i);
for (int w2 : bigrams.valueAt(i)) {
- dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ);
+ dict.setBigram(words.get(w1), words.get(w2), new ProbabilityInfo(BIGRAM_FREQ));
}
}
}
@@ -186,7 +178,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
long now = -1, diff = -1;
try {
- final DictEncoder dictEncoder = getDictEncoder(file, formatOptions);
+ final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions);
now = System.currentTimeMillis();
// If you need to dump the dict to a textual file, uncomment the line below and the
@@ -241,56 +233,23 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
private String outputOptions(final int bufferType,
final FormatSpec.FormatOptions formatOptions) {
String result = " : buffer type = "
- + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
- result += " : version = " + formatOptions.mVersion;
- return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate;
+ + ((bufferType == BinaryDictUtils.USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
+ return result + " : version = " + formatOptions.mVersion;
}
- private DictionaryOptions getDictionaryOptions(final String id, final String version) {
- final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(),
- false, false);
- options.mAttributes.put("version", version);
- options.mAttributes.put("dictionary", id);
- return options;
- }
-
- private File setUpDictionaryFile(final String name, final String version) {
- File file = null;
- try {
- file = new File(getContext().getCacheDir(), name + "." + version
- + TEST_DICT_FILE_EXTENSION);
- file.createNewFile();
- } catch (IOException e) {
- // do nothing
- }
- assertTrue("Failed to create the dictionary file.", file.exists());
- return file;
- }
-
- private DictDecoder getDictDecoder(final File file, final int bufferType,
- final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
- if (formatOptions.mVersion == FormatSpec.VERSION4) {
- final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
- return FormatSpec.getDictDecoder(new File(getContext().getCacheDir(),
- header.getId() + "." + header.getVersion()), bufferType);
- } else {
- return FormatSpec.getDictDecoder(file, bufferType);
- }
- }
// Tests for readDictionaryBinary and writeDictionaryBinary
private long timeReadingAndCheckDict(final File file, final List<String> words,
final SparseArray<List<Integer>> bigrams,
- final HashMap<String, List<String>> shortcutMap, final int bufferType,
- final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
+ final HashMap<String, List<String>> shortcutMap, final int bufferType) {
long now, diff = -1;
FusionDictionary dict = null;
try {
- final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions,
- dictOptions);
+ final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
+ bufferType);
now = System.currentTimeMillis();
- dict = dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */);
+ dict = dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
diff = System.currentTimeMillis() - now;
} catch (IOException e) {
Log.e(TAG, "IOException while reading dictionary", e);
@@ -310,17 +269,17 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final String dictName = "runReadAndWrite";
final String dictVersion = Long.toString(System.currentTimeMillis());
- final File file = setUpDictionaryFile(dictName, dictVersion);
+ final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+ getContext().getCacheDir());
final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- getDictionaryOptions(dictName, dictVersion));
+ BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
addUnigrams(words.size(), dict, words, shortcuts);
addBigrams(dict, words, bigrams);
checkDictionary(dict, words, bigrams, shortcuts);
final long write = timeWritingDictToFile(file, dict, formatOptions);
- final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType,
- formatOptions, dict.mOptions);
+ final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType);
return "PROF: read=" + read + "ms, write=" + write + "ms :" + message
+ " : " + outputOptions(bufferType, formatOptions);
@@ -340,6 +299,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
"chain with shortcuts"));
results.add(runReadAndWrite(sWords, sStarBigrams, sShortcuts, bufferType, formatOptions,
"star with shortcuts"));
+ results.add(runReadAndWrite(sWordsWithVariousCodePoints, sEmptyBigrams,
+ null /* shortcuts */, bufferType, formatOptions,
+ "unigram with various code points"));
}
// Unit test for CharEncoding.readString and CharEncoding.writeString.
@@ -349,8 +311,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final byte[] buffer = new byte[50 * 3];
final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer);
for (final String word : sWords) {
- Log.d("testReadAndWriteString", "write : " + word);
- Arrays.fill(buffer, (byte)0);
+ Arrays.fill(buffer, (byte) 0);
CharEncoding.writeString(buffer, 0, word);
dictBuffer.position(0);
final String str = CharEncoding.readString(dictBuffer);
@@ -359,29 +320,28 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testReadAndWriteWithByteBuffer() {
- final List<String> results = CollectionUtils.newArrayList();
-
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2);
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
-
+ final List<String> results = new ArrayList<>();
+
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
+ BinaryDictUtils.VERSION2_OPTIONS);
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
+ BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
+ BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
for (final String result : results) {
Log.d(TAG, result);
}
}
public void testReadAndWriteWithByteArray() {
- final List<String> results = CollectionUtils.newArrayList();
+ final List<String> results = new ArrayList<>();
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2);
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
- runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
+ BinaryDictUtils.VERSION2_OPTIONS);
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
+ BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+ runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
+ BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
for (final String result : results) {
Log.d(TAG, result);
@@ -394,62 +354,62 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final SparseArray<List<Integer>> expectedBigrams,
final TreeMap<Integer, String> resultWords,
final TreeMap<Integer, Integer> resultFrequencies,
- final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams) {
+ final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams,
+ final boolean checkProbability) {
// check unigrams
- final Set<String> actualWordsSet = new HashSet<String>(resultWords.values());
- final Set<String> expectedWordsSet = new HashSet<String>(expectedWords);
+ final Set<String> actualWordsSet = new HashSet<>(resultWords.values());
+ final Set<String> expectedWordsSet = new HashSet<>(expectedWords);
assertEquals(actualWordsSet, expectedWordsSet);
-
- for (int freq : resultFrequencies.values()) {
- assertEquals(freq, UNIGRAM_FREQ);
+ if (checkProbability) {
+ for (int freq : resultFrequencies.values()) {
+ assertEquals(freq, UNIGRAM_FREQ);
+ }
}
// check bigrams
- final HashMap<String, List<String>> expBigrams = new HashMap<String, List<String>>();
+ final HashMap<String, Set<String>> expBigrams = new HashMap<>();
for (int i = 0; i < expectedBigrams.size(); ++i) {
final String word1 = expectedWords.get(expectedBigrams.keyAt(i));
for (int w2 : expectedBigrams.valueAt(i)) {
if (expBigrams.get(word1) == null) {
- expBigrams.put(word1, new ArrayList<String>());
+ expBigrams.put(word1, new HashSet<String>());
}
expBigrams.get(word1).add(expectedWords.get(w2));
}
}
- final HashMap<String, List<String>> actBigrams = new HashMap<String, List<String>>();
+ final HashMap<String, Set<String>> actBigrams = new HashMap<>();
for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) {
final String word1 = resultWords.get(entry.getKey());
final int unigramFreq = resultFrequencies.get(entry.getKey());
for (PendingAttribute attr : entry.getValue()) {
final String word2 = resultWords.get(attr.mAddress);
if (actBigrams.get(word1) == null) {
- actBigrams.put(word1, new ArrayList<String>());
+ actBigrams.put(word1, new HashSet<String>());
}
actBigrams.get(word1).add(word2);
- final int bigramFreq = BinaryDictIOUtils.reconstructBigramFrequency(
- unigramFreq, attr.mFrequency);
- assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ);
+ if (checkProbability) {
+ final int bigramFreq = BinaryDictIOUtils.reconstructBigramFrequency(
+ unigramFreq, attr.mFrequency);
+ assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ);
+ }
}
}
-
assertEquals(actBigrams, expBigrams);
}
private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words,
final SparseArray<List<Integer>> bigrams, final int bufferType,
- final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
- FileInputStream inStream = null;
-
- final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap();
- final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams =
- CollectionUtils.newTreeMap();
- final TreeMap<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap();
+ final boolean checkProbability) {
+ final TreeMap<Integer, String> resultWords = new TreeMap<>();
+ final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = new TreeMap<>();
+ final TreeMap<Integer, Integer> resultFreqs = new TreeMap<>();
long now = -1, diff = -1;
try {
- final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions,
- dictOptions);
+ final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
+ bufferType);
now = System.currentTimeMillis();
dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams);
diff = System.currentTimeMillis() - now;
@@ -457,17 +417,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
Log.e(TAG, "IOException", e);
} catch (UnsupportedFormatException e) {
Log.e(TAG, "UnsupportedFormatException", e);
- } finally {
- if (inStream != null) {
- try {
- inStream.close();
- } catch (IOException e) {
- // do nothing
- }
- }
}
- checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams);
+ checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams, checkProbability);
return diff;
}
@@ -476,20 +428,24 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final FormatSpec.FormatOptions formatOptions, final String message) {
final String dictName = "runReadUnigrams";
final String dictVersion = Long.toString(System.currentTimeMillis());
- final File file = setUpDictionaryFile(dictName, dictVersion);
+ final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+ getContext().getCacheDir());
// making the dictionary from lists of words.
final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- getDictionaryOptions(dictName, dictVersion));
+ BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
addUnigrams(words.size(), dict, words, null /* shortcutMap */);
addBigrams(dict, words, bigrams);
timeWritingDictToFile(file, dict, formatOptions);
+ // Caveat: Currently, the Java code to read a v4 dictionary doesn't calculate the
+ // probability when there's a timestamp for the entry.
+ // TODO: Abandon the Java code, and implement the v4 dictionary reading code in native.
long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType,
- formatOptions, dict.mOptions);
+ !formatOptions.mHasTimestamp /* checkProbability */);
long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */,
- bufferType, formatOptions, dict.mOptions);
+ bufferType);
return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap
+ " : " + message + " : " + outputOptions(bufferType, formatOptions);
@@ -506,15 +462,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testReadUnigramsAndBigramsBinaryWithByteBuffer() {
- final ArrayList<String> results = CollectionUtils.newArrayList();
+ final ArrayList<String> results = new ArrayList<>();
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER,
- VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
+ runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
+ BinaryDictUtils.VERSION2_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
@@ -522,15 +473,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testReadUnigramsAndBigramsBinaryWithByteArray() {
- final ArrayList<String> results = CollectionUtils.newArrayList();
+ final ArrayList<String> results = new ArrayList<>();
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
- runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY,
- VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
+ runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
+ BinaryDictUtils.VERSION2_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
@@ -541,7 +487,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
private String getWordFromBinary(final DictDecoder dictDecoder, final int address) {
if (dictDecoder.getPosition() != 0) dictDecoder.setPosition(0);
- FileHeader fileHeader = null;
+ DictionaryHeader fileHeader = null;
try {
fileHeader = dictDecoder.readHeader();
} catch (IOException e) {
@@ -550,8 +496,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
return null;
}
if (fileHeader == null) return null;
- return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize,
- address, fileHeader.mFormatOptions).mWord;
+ return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mBodyOffset,
+ address).mWord;
}
private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word,
@@ -578,20 +524,22 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final FormatOptions formatOptions, final String message) {
final String dictName = "testGetTerminalPosition";
final String dictVersion = Long.toString(System.currentTimeMillis());
- final File file = setUpDictionaryFile(dictName, dictVersion);
+ final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+ getContext().getCacheDir());
final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- getDictionaryOptions(dictName, dictVersion));
+ BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
addBigrams(dict, words, bigrams);
timeWritingDictToFile(file, dict, formatOptions);
- final DictDecoder dictDecoder = getDictDecoder(file, DictDecoder.USE_BYTEARRAY,
- formatOptions, dict.mOptions);
+ final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
+ DictDecoder.USE_BYTEARRAY);
try {
dictDecoder.openDictBuffer();
} catch (IOException e) {
- // ignore
+ Log.e(TAG, "IOException while opening the buffer", e);
+ } catch (UnsupportedFormatException e) {
Log.e(TAG, "IOException while opening the buffer", e);
}
assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen());
@@ -636,67 +584,113 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testGetTerminalPosition() {
- final ArrayList<String> results = CollectionUtils.newArrayList();
-
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION2);
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
-
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION2);
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
+ final ArrayList<String> results = new ArrayList<>();
+
+ runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY,
+ BinaryDictUtils.VERSION2_OPTIONS);
+ runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER,
+ BinaryDictUtils.VERSION2_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
}
}
- private void runTestDeleteWord(final FormatOptions formatOptions) {
- final String dictName = "testDeleteWord";
+ public void testVer2DictGetWordProperty() {
+ final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+ final ArrayList<String> words = sWords;
+ final HashMap<String, List<String>> shortcuts = sShortcuts;
+ final String dictName = "testGetWordProperty";
final String dictVersion = Long.toString(System.currentTimeMillis());
- final File file = setUpDictionaryFile(dictName, dictVersion);
-
final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(
- new HashMap<String, String>(), false, false));
- addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
+ BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
+ addUnigrams(words.size(), dict, words, shortcuts);
+ addBigrams(dict, words, sEmptyBigrams);
+ final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+ getContext().getCacheDir());
+ file.delete();
timeWritingDictToFile(file, dict, formatOptions);
-
- final DictUpdater dictUpdater;
- if (formatOptions.mVersion == 3) {
- dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
- } else if (formatOptions.mVersion == 4) {
- dictUpdater = new Ver4DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
- } else {
- throw new RuntimeException("DictUpdater for version " + formatOptions.mVersion
- + " doesn't exist.");
- }
-
- try {
- MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
- dictUpdater.getTerminalPosition(sWords.get(0)));
- dictUpdater.deleteWord(sWords.get(0));
- assertEquals(FormatSpec.NOT_VALID_WORD,
- dictUpdater.getTerminalPosition(sWords.get(0)));
-
- MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
- dictUpdater.getTerminalPosition(sWords.get(5)));
- dictUpdater.deleteWord(sWords.get(5));
- assertEquals(FormatSpec.NOT_VALID_WORD,
- dictUpdater.getTerminalPosition(sWords.get(5)));
- } catch (IOException e) {
- } catch (UnsupportedFormatException e) {
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(file.getAbsolutePath(),
+ 0 /* offset */, file.length(), true /* useFullEditDistance */,
+ Locale.ENGLISH, dictName, false /* isUpdatable */);
+ for (final String word : words) {
+ final WordProperty wordProperty = binaryDictionary.getWordProperty(word,
+ false /* isBeginningOfSentence */);
+ assertEquals(word, wordProperty.mWord);
+ assertEquals(UNIGRAM_FREQ, wordProperty.getProbability());
+ if (shortcuts.containsKey(word)) {
+ assertEquals(shortcuts.get(word).size(), wordProperty.mShortcutTargets.size());
+ final List<String> shortcutList = shortcuts.get(word);
+ assertTrue(wordProperty.mHasShortcuts);
+ for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ assertTrue(shortcutList.contains(shortcutTarget.mWord));
+ assertEquals(UNIGRAM_FREQ, shortcutTarget.getProbability());
+ shortcutList.remove(shortcutTarget.mWord);
+ }
+ assertTrue(shortcutList.isEmpty());
+ }
}
}
- public void testDeleteWord() {
- runTestDeleteWord(VERSION3_WITH_DYNAMIC_UPDATE);
- runTestDeleteWord(VERSION4_WITH_DYNAMIC_UPDATE);
+ public void testVer2DictIteration() {
+ final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+ final ArrayList<String> words = sWords;
+ final HashMap<String, List<String>> shortcuts = sShortcuts;
+ final SparseArray<List<Integer>> bigrams = sEmptyBigrams;
+ final String dictName = "testGetWordProperty";
+ final String dictVersion = Long.toString(System.currentTimeMillis());
+ final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+ BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
+ addUnigrams(words.size(), dict, words, shortcuts);
+ addBigrams(dict, words, bigrams);
+ final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+ getContext().getCacheDir());
+ timeWritingDictToFile(file, dict, formatOptions);
+ Log.d(TAG, file.getAbsolutePath());
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(file.getAbsolutePath(),
+ 0 /* offset */, file.length(), true /* useFullEditDistance */,
+ Locale.ENGLISH, dictName, false /* isUpdatable */);
+
+ final HashSet<String> wordSet = new HashSet<>(words);
+ final HashSet<Pair<String, String>> bigramSet = new HashSet<>();
+
+ for (int i = 0; i < words.size(); i++) {
+ final List<Integer> bigramList = bigrams.get(i);
+ if (bigramList != null) {
+ for (final Integer word1Index : bigramList) {
+ final String word1 = words.get(word1Index);
+ bigramSet.add(new Pair<>(words.get(i), word1));
+ }
+ }
+ }
+ int token = 0;
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ binaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ final String word0 = wordProperty.mWord;
+ assertEquals(UNIGRAM_FREQ, wordProperty.mProbabilityInfo.mProbability);
+ wordSet.remove(word0);
+ if (shortcuts.containsKey(word0)) {
+ assertEquals(shortcuts.get(word0).size(), wordProperty.mShortcutTargets.size());
+ final List<String> shortcutList = shortcuts.get(word0);
+ assertNotNull(wordProperty.mShortcutTargets);
+ for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ assertTrue(shortcutList.contains(shortcutTarget.mWord));
+ assertEquals(UNIGRAM_FREQ, shortcutTarget.getProbability());
+ shortcutList.remove(shortcutTarget.mWord);
+ }
+ assertTrue(shortcutList.isEmpty());
+ }
+ for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
+ final String word1 = wordProperty.mBigrams.get(j).mWord;
+ final Pair<String, String> bigram = new Pair<>(word0, word1);
+ assertTrue(bigramSet.contains(bigram));
+ bigramSet.remove(bigram);
+ }
+ token = result.mNextToken;
+ } while (token != 0);
+ assertTrue(wordSet.isEmpty());
+ assertTrue(bigramSet.isEmpty());
}
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
new file mode 100644
index 000000000..96604a197
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Decodes binary files for a FusionDictionary.
+ *
+ * All the methods in this class are static.
+ *
+ * TODO: Move this file to makedict/internal.
+ * TODO: Rename this class to DictDecoderUtils.
+ */
+public final class BinaryDictDecoderUtils {
+ private BinaryDictDecoderUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ @UsedForTesting
+ public interface DictBuffer {
+ public int readUnsignedByte();
+ public int readUnsignedShort();
+ public int readUnsignedInt24();
+ public int readInt();
+ public int position();
+ public void position(int newPosition);
+ @UsedForTesting
+ public void put(final byte b);
+ public int limit();
+ @UsedForTesting
+ public int capacity();
+ }
+
+ public static final class ByteBufferDictBuffer implements DictBuffer {
+ private ByteBuffer mBuffer;
+
+ public ByteBufferDictBuffer(final ByteBuffer buffer) {
+ mBuffer = buffer;
+ }
+
+ @Override
+ public int readUnsignedByte() {
+ return mBuffer.get() & 0xFF;
+ }
+
+ @Override
+ public int readUnsignedShort() {
+ return mBuffer.getShort() & 0xFFFF;
+ }
+
+ @Override
+ public int readUnsignedInt24() {
+ final int retval = readUnsignedByte();
+ return (retval << 16) + readUnsignedShort();
+ }
+
+ @Override
+ public int readInt() {
+ return mBuffer.getInt();
+ }
+
+ @Override
+ public int position() {
+ return mBuffer.position();
+ }
+
+ @Override
+ public void position(int newPos) {
+ mBuffer.position(newPos);
+ }
+
+ @Override
+ public void put(final byte b) {
+ mBuffer.put(b);
+ }
+
+ @Override
+ public int limit() {
+ return mBuffer.limit();
+ }
+
+ @Override
+ public int capacity() {
+ return mBuffer.capacity();
+ }
+ }
+
+ /**
+ * A class grouping utility function for our specific character encoding.
+ */
+ static final class CharEncoding {
+ private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
+ private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
+
+ /**
+ * Helper method to find out whether this code fits on one byte
+ */
+ private static boolean fitsOnOneByte(final int character) {
+ return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE
+ && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
+ }
+
+ /**
+ * Compute the size of a character given its character code.
+ *
+ * Char format is:
+ * 1 byte = bbbbbbbb match
+ * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
+ * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
+ * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
+ * 00011111 would be outside unicode.
+ * else: iso-latin-1 code
+ * This allows for the whole unicode range to be encoded, including chars outside of
+ * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
+ * characters which should never happen anyway (and still work, but take 3 bytes).
+ *
+ * @param character the character code.
+ * @return the size in binary encoded-form, either 1 or 3 bytes.
+ */
+ static int getCharSize(final int character) {
+ // See char encoding in FusionDictionary.java
+ if (fitsOnOneByte(character)) return 1;
+ if (FormatSpec.INVALID_CHARACTER == character) return 1;
+ return 3;
+ }
+
+ /**
+ * Compute the byte size of a character array.
+ */
+ static int getCharArraySize(final int[] chars) {
+ int size = 0;
+ for (int character : chars) size += getCharSize(character);
+ return size;
+ }
+
+ /**
+ * Writes a char array to a byte buffer.
+ *
+ * @param codePoints the code point array to write.
+ * @param buffer the byte buffer to write to.
+ * @param index the index in buffer to write the character array to.
+ * @return the index after the last character.
+ */
+ static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) {
+ for (int codePoint : codePoints) {
+ if (1 == getCharSize(codePoint)) {
+ buffer[index++] = (byte)codePoint;
+ } else {
+ buffer[index++] = (byte)(0xFF & (codePoint >> 16));
+ buffer[index++] = (byte)(0xFF & (codePoint >> 8));
+ buffer[index++] = (byte)(0xFF & codePoint);
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Writes a string with our character format to a byte buffer.
+ *
+ * This will also write the terminator byte.
+ *
+ * @param buffer the byte buffer to write to.
+ * @param origin the offset to write from.
+ * @param word the string to write.
+ * @return the size written, in bytes.
+ */
+ static int writeString(final byte[] buffer, final int origin, final String word) {
+ final int length = word.length();
+ int index = origin;
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (1 == getCharSize(codePoint)) {
+ buffer[index++] = (byte)codePoint;
+ } else {
+ buffer[index++] = (byte)(0xFF & (codePoint >> 16));
+ buffer[index++] = (byte)(0xFF & (codePoint >> 8));
+ buffer[index++] = (byte)(0xFF & codePoint);
+ }
+ }
+ buffer[index++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
+ return index - origin;
+ }
+
+ /**
+ * Writes a string with our character format to an OutputStream.
+ *
+ * This will also write the terminator byte.
+ *
+ * @param stream the OutputStream to write to.
+ * @param word the string to write.
+ * @return the size written, in bytes.
+ */
+ static int writeString(final OutputStream stream, final String word) throws IOException {
+ final int length = word.length();
+ int written = 0;
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ final int charSize = getCharSize(codePoint);
+ if (1 == charSize) {
+ stream.write((byte) codePoint);
+ } else {
+ stream.write((byte) (0xFF & (codePoint >> 16)));
+ stream.write((byte) (0xFF & (codePoint >> 8)));
+ stream.write((byte) (0xFF & codePoint));
+ }
+ written += charSize;
+ }
+ stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
+ written += FormatSpec.PTNODE_TERMINATOR_SIZE;
+ return written;
+ }
+
+ /**
+ * Reads a string from a DictBuffer. This is the converse of the above method.
+ */
+ static String readString(final DictBuffer dictBuffer) {
+ final StringBuilder s = new StringBuilder();
+ int character = readChar(dictBuffer);
+ while (character != FormatSpec.INVALID_CHARACTER) {
+ s.appendCodePoint(character);
+ character = readChar(dictBuffer);
+ }
+ return s.toString();
+ }
+
+ /**
+ * Reads a character from the buffer.
+ *
+ * This follows the character format documented earlier in this source file.
+ *
+ * @param dictBuffer the buffer, positioned over an encoded character.
+ * @return the character code.
+ */
+ static int readChar(final DictBuffer dictBuffer) {
+ int character = dictBuffer.readUnsignedByte();
+ if (!fitsOnOneByte(character)) {
+ if (FormatSpec.PTNODE_CHARACTERS_TERMINATOR == character) {
+ return FormatSpec.INVALID_CHARACTER;
+ }
+ character <<= 16;
+ character += dictBuffer.readUnsignedShort();
+ }
+ return character;
+ }
+ }
+
+ /**
+ * Reads and returns the PtNode count out of a buffer and forwards the pointer.
+ */
+ /* package */ static int readPtNodeCount(final DictBuffer dictBuffer) {
+ final int msb = dictBuffer.readUnsignedByte();
+ if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= msb) {
+ return msb;
+ } else {
+ return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8)
+ + dictBuffer.readUnsignedByte();
+ }
+ }
+
+ /**
+ * Finds, as a string, the word at the position passed as an argument.
+ *
+ * @param dictDecoder the dict decoder.
+ * @param headerSize the size of the header.
+ * @param pos the position to seek.
+ * @return the word with its frequency, as a weighted string.
+ */
+ @UsedForTesting
+ /* package for tests */ static WeightedString getWordAtPosition(final DictDecoder dictDecoder,
+ final int headerSize, final int pos) {
+ final WeightedString result;
+ final int originalPos = dictDecoder.getPosition();
+ dictDecoder.setPosition(pos);
+ result = getWordAtPositionWithoutParentAddress(dictDecoder, headerSize, pos);
+ dictDecoder.setPosition(originalPos);
+ return result;
+ }
+
+ private static WeightedString getWordAtPositionWithoutParentAddress(
+ final DictDecoder dictDecoder, final int headerSize, final int pos) {
+ dictDecoder.setPosition(headerSize);
+ final int count = dictDecoder.readPtNodeCount();
+ int groupPos = dictDecoder.getPosition();
+ final StringBuilder builder = new StringBuilder();
+ WeightedString result = null;
+
+ PtNodeInfo last = null;
+ for (int i = count - 1; i >= 0; --i) {
+ PtNodeInfo info = dictDecoder.readPtNode(groupPos);
+ groupPos = info.mEndAddress;
+ if (info.mOriginalAddress == pos) {
+ builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
+ result = new WeightedString(builder.toString(), info.mProbabilityInfo);
+ break; // and return
+ }
+ if (BinaryDictIOUtils.hasChildrenAddress(info.mChildrenAddress)) {
+ if (info.mChildrenAddress > pos) {
+ if (null == last) continue;
+ builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
+ dictDecoder.setPosition(last.mChildrenAddress);
+ i = dictDecoder.readPtNodeCount();
+ groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
+ last = null;
+ continue;
+ }
+ last = info;
+ }
+ if (0 == i && BinaryDictIOUtils.hasChildrenAddress(last.mChildrenAddress)) {
+ builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
+ dictDecoder.setPosition(last.mChildrenAddress);
+ i = dictDecoder.readPtNodeCount();
+ groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
+ last = null;
+ continue;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Helper method to pass a file name instead of a File object to isBinaryDictionary.
+ */
+ public static boolean isBinaryDictionary(final String filename) {
+ final File file = new File(filename);
+ return isBinaryDictionary(file);
+ }
+
+ /**
+ * Basic test to find out whether the file is a binary dictionary or not.
+ *
+ * @param file The file to test.
+ * @return true if it's a binary dictionary, false otherwise
+ */
+ public static boolean isBinaryDictionary(final File file) {
+ final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length());
+ if (dictDecoder == null) {
+ return false;
+ }
+ return dictDecoder.hasValidRawBinaryDictionary();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
new file mode 100644
index 000000000..084371944
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -0,0 +1,881 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+/**
+ * Encodes binary files for a FusionDictionary.
+ *
+ * All the methods in this class are static.
+ *
+ * TODO: Rename this class to DictEncoderUtils.
+ */
+public class BinaryDictEncoderUtils {
+
+ private static final boolean DBG = MakedictLog.DBG;
+
+ private BinaryDictEncoderUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ // Arbitrary limit to how much passes we consider address size compression should
+ // terminate in. At the time of this writing, our largest dictionary completes
+ // compression in five passes.
+ // If the number of passes exceeds this number, makedict bails with an exception on
+ // suspicion that a bug might be causing an infinite loop.
+ private static final int MAX_PASSES = 24;
+
+ /**
+ * Compute the binary size of the character array.
+ *
+ * If only one character, this is the size of this character. If many, it's the sum of their
+ * sizes + 1 byte for the terminator.
+ *
+ * @param characters the character array
+ * @return the size of the char array, including the terminator if any
+ */
+ static int getPtNodeCharactersSize(final int[] characters) {
+ int size = CharEncoding.getCharArraySize(characters);
+ if (characters.length > 1) size += FormatSpec.PTNODE_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
+ * Compute the binary size of the character array in a PtNode
+ *
+ * If only one character, this is the size of this character. If many, it's the sum of their
+ * sizes + 1 byte for the terminator.
+ *
+ * @param ptNode the PtNode
+ * @return the size of the char array, including the terminator if any
+ */
+ private static int getPtNodeCharactersSize(final PtNode ptNode) {
+ return getPtNodeCharactersSize(ptNode.mChars);
+ }
+
+ /**
+ * Compute the binary size of the PtNode count for a node array.
+ * @param nodeArray the nodeArray
+ * @return the size of the PtNode count, either 1 or 2 bytes.
+ */
+ private static int getPtNodeCountSize(final PtNodeArray nodeArray) {
+ return BinaryDictIOUtils.getPtNodeCountSize(nodeArray.mData.size());
+ }
+
+ /**
+ * Compute the size of a shortcut in bytes.
+ */
+ private static int getShortcutSize(final WeightedString shortcut) {
+ int size = FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
+ final String word = shortcut.mWord;
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ size += CharEncoding.getCharSize(codePoint);
+ }
+ size += FormatSpec.PTNODE_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
+ * Compute the size of a shortcut list in bytes.
+ *
+ * This is known in advance and does not change according to position in the file
+ * like address lists do.
+ */
+ static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+ if (null == shortcutList || shortcutList.isEmpty()) return 0;
+ int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+ for (final WeightedString shortcut : shortcutList) {
+ size += getShortcutSize(shortcut);
+ }
+ return size;
+ }
+
+ /**
+ * Compute the maximum size of a PtNode, assuming 3-byte addresses for everything.
+ *
+ * @param ptNode the PtNode to compute the size of.
+ * @return the maximum size of the PtNode.
+ */
+ private static int getPtNodeMaximumSize(final PtNode ptNode) {
+ int size = getNodeHeaderSize(ptNode);
+ if (ptNode.isTerminal()) {
+ // If terminal, one byte for the frequency.
+ size += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ }
+ size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address
+ size += getShortcutListSize(ptNode.mShortcutTargets);
+ if (null != ptNode.mBigrams) {
+ size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE
+ + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE)
+ * ptNode.mBigrams.size();
+ }
+ return size;
+ }
+
+ /**
+ * Compute the maximum size of each PtNode of a PtNode array, assuming 3-byte addresses for
+ * everything, and caches it in the `mCachedSize' member of the nodes; deduce the size of
+ * the containing node array, and cache it it its 'mCachedSize' member.
+ *
+ * @param ptNodeArray the node array to compute the maximum size of.
+ */
+ private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray) {
+ int size = getPtNodeCountSize(ptNodeArray);
+ for (PtNode node : ptNodeArray.mData) {
+ final int nodeSize = getPtNodeMaximumSize(node);
+ node.mCachedSize = nodeSize;
+ size += nodeSize;
+ }
+ ptNodeArray.mCachedSize = size;
+ }
+
+ /**
+ * Compute the size of the header (flag + [parent address] + characters size) of a PtNode.
+ *
+ * @param ptNode the PtNode of which to compute the size of the header
+ */
+ private static int getNodeHeaderSize(final PtNode ptNode) {
+ return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
+ }
+
+ /**
+ * Compute the size, in bytes, that an address will occupy.
+ *
+ * This can be used either for children addresses (which are always positive) or for
+ * attribute, which may be positive or negative but
+ * store their sign bit separately.
+ *
+ * @param address the address
+ * @return the byte size.
+ */
+ static int getByteSize(final int address) {
+ assert(address <= FormatSpec.UINT24_MAX);
+ if (!BinaryDictIOUtils.hasChildrenAddress(address)) {
+ return 0;
+ } else if (Math.abs(address) <= FormatSpec.UINT8_MAX) {
+ return 1;
+ } else if (Math.abs(address) <= FormatSpec.UINT16_MAX) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+
+ static int writeUIntToBuffer(final byte[] buffer, int position, final int value,
+ final int size) {
+ switch(size) {
+ case 4:
+ buffer[position++] = (byte) ((value >> 24) & 0xFF);
+ /* fall through */
+ case 3:
+ buffer[position++] = (byte) ((value >> 16) & 0xFF);
+ /* fall through */
+ case 2:
+ buffer[position++] = (byte) ((value >> 8) & 0xFF);
+ /* fall through */
+ case 1:
+ buffer[position++] = (byte) (value & 0xFF);
+ break;
+ default:
+ /* nop */
+ }
+ return position;
+ }
+
+ static void writeUIntToStream(final OutputStream stream, final int value, final int size)
+ throws IOException {
+ switch(size) {
+ case 4:
+ stream.write((value >> 24) & 0xFF);
+ /* fall through */
+ case 3:
+ stream.write((value >> 16) & 0xFF);
+ /* fall through */
+ case 2:
+ stream.write((value >> 8) & 0xFF);
+ /* fall through */
+ case 1:
+ stream.write(value & 0xFF);
+ break;
+ default:
+ /* nop */
+ }
+ }
+
+ @UsedForTesting
+ static void writeUIntToDictBuffer(final DictBuffer dictBuffer, final int value,
+ final int size) {
+ switch(size) {
+ case 4:
+ dictBuffer.put((byte) ((value >> 24) & 0xFF));
+ /* fall through */
+ case 3:
+ dictBuffer.put((byte) ((value >> 16) & 0xFF));
+ /* fall through */
+ case 2:
+ dictBuffer.put((byte) ((value >> 8) & 0xFF));
+ /* fall through */
+ case 1:
+ dictBuffer.put((byte) (value & 0xFF));
+ break;
+ default:
+ /* nop */
+ }
+ }
+
+ // End utility methods
+
+ // This method is responsible for finding a nice ordering of the nodes that favors run-time
+ // cache performance and dictionary size.
+ /* package for tests */ static ArrayList<PtNodeArray> flattenTree(
+ final PtNodeArray rootNodeArray) {
+ final int treeSize = FusionDictionary.countPtNodes(rootNodeArray);
+ MakedictLog.i("Counted nodes : " + treeSize);
+ final ArrayList<PtNodeArray> flatTree = new ArrayList<>(treeSize);
+ return flattenTreeInner(flatTree, rootNodeArray);
+ }
+
+ private static ArrayList<PtNodeArray> flattenTreeInner(final ArrayList<PtNodeArray> list,
+ final PtNodeArray ptNodeArray) {
+ // Removing the node is necessary if the tails are merged, because we would then
+ // add the same node several times when we only want it once. A number of places in
+ // the code also depends on any node being only once in the list.
+ // Merging tails can only be done if there are no attributes. Searching for attributes
+ // in LatinIME code depends on a total breadth-first ordering, which merging tails
+ // breaks. If there are no attributes, it should be fine (and reduce the file size)
+ // to merge tails, and removing the node from the list would be necessary. However,
+ // we don't merge tails because breaking the breadth-first ordering would result in
+ // extreme overhead at bigram lookup time (it would make the search function O(n) instead
+ // of the current O(log(n)), where n=number of nodes in the dictionary which is pretty
+ // high).
+ // If no nodes are ever merged, we can't have the same node twice in the list, hence
+ // searching for duplicates in unnecessary. It is also very performance consuming,
+ // since `list' is an ArrayList so it's an O(n) operation that runs on all nodes, making
+ // this simple list.remove operation O(n*n) overall. On Android this overhead is very
+ // high.
+ // For future reference, the code to remove duplicate is a simple : list.remove(node);
+ list.add(ptNodeArray);
+ final ArrayList<PtNode> branches = ptNodeArray.mData;
+ for (PtNode ptNode : branches) {
+ if (null != ptNode.mChildren) flattenTreeInner(list, ptNode.mChildren);
+ }
+ return list;
+ }
+
+ /**
+ * Get the offset from a position inside a current node array to a target node array, during
+ * update.
+ *
+ * If the current node array is before the target node array, the target node array has not
+ * been updated yet, so we should return the offset from the old position of the current node
+ * array to the old position of the target node array. If on the other hand the target is
+ * before the current node array, it already has been updated, so we should return the offset
+ * from the new position in the current node array to the new position in the target node
+ * array.
+ *
+ * @param currentNodeArray node array containing the PtNode where the offset will be written
+ * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
+ * @param targetNodeArray the target node array to get the offset to
+ * @return the offset to the target node array
+ */
+ private static int getOffsetToTargetNodeArrayDuringUpdate(final PtNodeArray currentNodeArray,
+ final int offsetFromStartOfCurrentNodeArray, final PtNodeArray targetNodeArray) {
+ final boolean isTargetBeforeCurrent = (targetNodeArray.mCachedAddressBeforeUpdate
+ < currentNodeArray.mCachedAddressBeforeUpdate);
+ if (isTargetBeforeCurrent) {
+ return targetNodeArray.mCachedAddressAfterUpdate
+ - (currentNodeArray.mCachedAddressAfterUpdate
+ + offsetFromStartOfCurrentNodeArray);
+ } else {
+ return targetNodeArray.mCachedAddressBeforeUpdate
+ - (currentNodeArray.mCachedAddressBeforeUpdate
+ + offsetFromStartOfCurrentNodeArray);
+ }
+ }
+
+ /**
+ * Get the offset from a position inside a current node array to a target PtNode, during
+ * update.
+ *
+ * @param currentNodeArray node array containing the PtNode where the offset will be written
+ * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
+ * @param targetPtNode the target PtNode to get the offset to
+ * @return the offset to the target PtNode
+ */
+ // TODO: is there any way to factorize this method with the one above?
+ private static int getOffsetToTargetPtNodeDuringUpdate(final PtNodeArray currentNodeArray,
+ final int offsetFromStartOfCurrentNodeArray, final PtNode targetPtNode) {
+ final int oldOffsetBasePoint = currentNodeArray.mCachedAddressBeforeUpdate
+ + offsetFromStartOfCurrentNodeArray;
+ final boolean isTargetBeforeCurrent = (targetPtNode.mCachedAddressBeforeUpdate
+ < oldOffsetBasePoint);
+ // If the target is before the current node array, then its address has already been
+ // updated. We can use the AfterUpdate member, and compare it to our own member after
+ // update. Otherwise, the AfterUpdate member is not updated yet, so we need to use the
+ // BeforeUpdate member, and of course we have to compare this to our own address before
+ // update.
+ if (isTargetBeforeCurrent) {
+ final int newOffsetBasePoint = currentNodeArray.mCachedAddressAfterUpdate
+ + offsetFromStartOfCurrentNodeArray;
+ return targetPtNode.mCachedAddressAfterUpdate - newOffsetBasePoint;
+ } else {
+ return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint;
+ }
+ }
+
+ /**
+ * Computes the actual node array size, based on the cached addresses of the children nodes.
+ *
+ * Each node array stores its tentative address. During dictionary address computing, these
+ * are not final, but they can be used to compute the node array size (the node array size
+ * depends on the address of the children because the number of bytes necessary to store an
+ * address depends on its numeric value. The return value indicates whether the node array
+ * contents (as in, any of the addresses stored in the cache fields) have changed with
+ * respect to their previous value.
+ *
+ * @param ptNodeArray the node array to compute the size of.
+ * @param dict the dictionary in which the word/attributes are to be found.
+ * @return false if none of the cached addresses inside the node array changed, true otherwise.
+ */
+ private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray,
+ final FusionDictionary dict) {
+ boolean changed = false;
+ int size = getPtNodeCountSize(ptNodeArray);
+ for (PtNode ptNode : ptNodeArray.mData) {
+ ptNode.mCachedAddressAfterUpdate = ptNodeArray.mCachedAddressAfterUpdate + size;
+ if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) {
+ changed = true;
+ }
+ int nodeSize = getNodeHeaderSize(ptNode);
+ if (ptNode.isTerminal()) {
+ nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ }
+ if (null != ptNode.mChildren) {
+ nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
+ nodeSize + size, ptNode.mChildren));
+ }
+ nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
+ if (null != ptNode.mBigrams) {
+ for (WeightedString bigram : ptNode.mBigrams) {
+ final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
+ nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE,
+ FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord));
+ nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
+ }
+ }
+ ptNode.mCachedSize = nodeSize;
+ size += nodeSize;
+ }
+ if (ptNodeArray.mCachedSize != size) {
+ ptNodeArray.mCachedSize = size;
+ changed = true;
+ }
+ return changed;
+ }
+
+ /**
+ * Initializes the cached addresses of node arrays and their containing nodes from their size.
+ *
+ * @param flatNodes the list of node arrays.
+ * @return the byte size of the entire stack.
+ */
+ private static int initializePtNodeArraysCachedAddresses(
+ final ArrayList<PtNodeArray> flatNodes) {
+ int nodeArrayOffset = 0;
+ for (final PtNodeArray nodeArray : flatNodes) {
+ nodeArray.mCachedAddressBeforeUpdate = nodeArrayOffset;
+ int nodeCountSize = getPtNodeCountSize(nodeArray);
+ int nodeffset = 0;
+ for (final PtNode ptNode : nodeArray.mData) {
+ ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate =
+ nodeCountSize + nodeArrayOffset + nodeffset;
+ nodeffset += ptNode.mCachedSize;
+ }
+ nodeArrayOffset += nodeArray.mCachedSize;
+ }
+ return nodeArrayOffset;
+ }
+
+ /**
+ * Updates the cached addresses of node arrays after recomputing their new positions.
+ *
+ * @param flatNodes the list of node arrays.
+ */
+ private static void updatePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes) {
+ for (final PtNodeArray nodeArray : flatNodes) {
+ nodeArray.mCachedAddressBeforeUpdate = nodeArray.mCachedAddressAfterUpdate;
+ for (final PtNode ptNode : nodeArray.mData) {
+ ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate;
+ }
+ }
+ }
+
+ /**
+ * Compute the addresses and sizes of an ordered list of PtNode arrays.
+ *
+ * This method takes a list of PtNode arrays and will update their cached address and size
+ * values so that they can be written into a file. It determines the smallest size each of the
+ * PtNode arrays can be given the addresses of its children and attributes, and store that into
+ * each PtNode.
+ * The order of the PtNode is given by the order of the array. This method makes no effort
+ * to find a good order; it only mechanically computes the size this order results in.
+ *
+ * @param dict the dictionary
+ * @param flatNodes the ordered list of PtNode arrays
+ * @return the same array it was passed. The nodes have been updated for address and size.
+ */
+ /* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict,
+ final ArrayList<PtNodeArray> flatNodes) {
+ // First get the worst possible sizes and offsets
+ for (final PtNodeArray n : flatNodes) {
+ calculatePtNodeArrayMaximumSize(n);
+ }
+ final int offset = initializePtNodeArraysCachedAddresses(flatNodes);
+
+ MakedictLog.i("Compressing the array addresses. Original size : " + offset);
+ MakedictLog.i("(Recursively seen size : " + offset + ")");
+
+ int passes = 0;
+ boolean changesDone = false;
+ do {
+ changesDone = false;
+ int ptNodeArrayStartOffset = 0;
+ for (final PtNodeArray ptNodeArray : flatNodes) {
+ ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset;
+ final int oldNodeArraySize = ptNodeArray.mCachedSize;
+ final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict);
+ final int newNodeArraySize = ptNodeArray.mCachedSize;
+ if (oldNodeArraySize < newNodeArraySize) {
+ throw new RuntimeException("Increased size ?!");
+ }
+ ptNodeArrayStartOffset += newNodeArraySize;
+ changesDone |= changed;
+ }
+ updatePtNodeArraysCachedAddresses(flatNodes);
+ ++passes;
+ if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
+ } while (changesDone);
+
+ final PtNodeArray lastPtNodeArray = flatNodes.get(flatNodes.size() - 1);
+ MakedictLog.i("Compression complete in " + passes + " passes.");
+ MakedictLog.i("After address compression : "
+ + (lastPtNodeArray.mCachedAddressAfterUpdate + lastPtNodeArray.mCachedSize));
+
+ return flatNodes;
+ }
+
+ /**
+ * Sanity-checking method.
+ *
+ * This method checks a list of PtNode arrays for juxtaposition, that is, it will do
+ * nothing if each node array's cached address is actually the previous node array's address
+ * plus the previous node's size.
+ * If this is not the case, it will throw an exception.
+ *
+ * @param arrays the list of node arrays to check
+ */
+ /* package */ static void checkFlatPtNodeArrayList(final ArrayList<PtNodeArray> arrays) {
+ int offset = 0;
+ int index = 0;
+ for (final PtNodeArray ptNodeArray : arrays) {
+ // BeforeUpdate and AfterUpdate addresses are the same here, so it does not matter
+ // which we use.
+ if (ptNodeArray.mCachedAddressAfterUpdate != offset) {
+ throw new RuntimeException("Wrong address for node " + index
+ + " : expected " + offset + ", got " +
+ ptNodeArray.mCachedAddressAfterUpdate);
+ }
+ ++index;
+ offset += ptNodeArray.mCachedSize;
+ }
+ }
+
+ /**
+ * Helper method to write a children position to a file.
+ *
+ * @param buffer the buffer to write to.
+ * @param index the index in the buffer to write the address to.
+ * @param position the position to write.
+ * @return the size in bytes the address actually took.
+ */
+ /* package */ static int writeChildrenPosition(final byte[] buffer, int index,
+ final int position) {
+ switch (getByteSize(position)) {
+ case 1:
+ buffer[index++] = (byte)position;
+ return 1;
+ case 2:
+ buffer[index++] = (byte)(0xFF & (position >> 8));
+ buffer[index++] = (byte)(0xFF & position);
+ return 2;
+ case 3:
+ buffer[index++] = (byte)(0xFF & (position >> 16));
+ buffer[index++] = (byte)(0xFF & (position >> 8));
+ buffer[index++] = (byte)(0xFF & position);
+ return 3;
+ case 0:
+ return 0;
+ default:
+ throw new RuntimeException("Position " + position + " has a strange size");
+ }
+ }
+
+ /**
+ * Helper method to write a signed children position to a file.
+ *
+ * @param buffer the buffer to write to.
+ * @param index the index in the buffer to write the address to.
+ * @param position the position to write.
+ * @return the size in bytes the address actually took.
+ */
+ /* package */ static int writeSignedChildrenPosition(final byte[] buffer, int index,
+ final int position) {
+ if (!BinaryDictIOUtils.hasChildrenAddress(position)) {
+ buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
+ } else {
+ final int absPosition = Math.abs(position);
+ buffer[index++] =
+ (byte)((position < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absPosition >> 16)));
+ buffer[index++] = (byte)(0xFF & (absPosition >> 8));
+ buffer[index++] = (byte)(0xFF & absPosition);
+ }
+ return 3;
+ }
+
+ /**
+ * Makes the flag value for a PtNode.
+ *
+ * @param hasMultipleChars whether the PtNode has multiple chars.
+ * @param isTerminal whether the PtNode is terminal.
+ * @param childrenAddressSize the size of a children address.
+ * @param hasShortcuts whether the PtNode has shortcuts.
+ * @param hasBigrams whether the PtNode has bigrams.
+ * @param isNotAWord whether the PtNode is not a word.
+ * @param isBlackListEntry whether the PtNode is a blacklist entry.
+ * @return the flags
+ */
+ static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal,
+ final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
+ final boolean isNotAWord, final boolean isBlackListEntry) {
+ byte flags = 0;
+ if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
+ if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
+ switch (childrenAddressSize) {
+ case 1:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES;
+ break;
+ case 0:
+ flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS;
+ break;
+ default:
+ throw new RuntimeException("Node with a strange address");
+ }
+ if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
+ if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+ if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+ if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
+ return flags;
+ }
+
+ /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset) {
+ return (byte) makePtNodeFlags(node.mChars.length > 1, node.isTerminal(),
+ getByteSize(childrenOffset),
+ node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(),
+ node.mBigrams != null && !node.mBigrams.isEmpty(),
+ node.mIsNotAWord, node.mIsBlacklistEntry);
+ }
+
+ /**
+ * Makes the flag value for a bigram.
+ *
+ * @param more whether there are more bigrams after this one.
+ * @param offset the offset of the bigram.
+ * @param bigramFrequency the frequency of the bigram, 0..255.
+ * @param unigramFrequency the unigram frequency of the same word, 0..255.
+ * @param word the second bigram, for debugging purposes
+ * @return the flags
+ */
+ /* package */ static final int makeBigramFlags(final boolean more, final int offset,
+ int bigramFrequency, final int unigramFrequency, final String word) {
+ int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+ + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0);
+ switch (getByteSize(offset)) {
+ case 1:
+ bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES;
+ break;
+ default:
+ throw new RuntimeException("Strange offset size");
+ }
+ if (unigramFrequency > bigramFrequency) {
+ MakedictLog.e("Unigram freq is superior to bigram freq for \"" + word
+ + "\". Bigram freq is " + bigramFrequency + ", unigram freq for "
+ + word + " is " + unigramFrequency);
+ bigramFrequency = unigramFrequency;
+ }
+ bigramFlags += getBigramFrequencyDiff(unigramFrequency, bigramFrequency)
+ & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
+ return bigramFlags;
+ }
+
+ public static int getBigramFrequencyDiff(final int unigramFrequency,
+ final int bigramFrequency) {
+ // We compute the difference between 255 (which means probability = 1) and the
+ // unigram score. We split this into a number of discrete steps.
+ // Now, the steps are numbered 0~15; 0 represents an increase of 1 step while 15
+ // represents an increase of 16 steps: a value of 15 will be interpreted as the median
+ // value of the 16th step. In all justice, if the bigram frequency is low enough to be
+ // rounded below the first step (which means it is less than half a step higher than the
+ // unigram frequency) then the unigram frequency itself is the best approximation of the
+ // bigram freq that we could possibly supply, hence we should *not* include this bigram
+ // in the file at all.
+ // until this is done, we'll write 0 and slightly overestimate this case.
+ // In other words, 0 means "between 0.5 step and 1.5 step", 1 means "between 1.5 step
+ // and 2.5 steps", and 15 means "between 15.5 steps and 16.5 steps". So we want to
+ // divide our range [unigramFreq..MAX_TERMINAL_FREQUENCY] in 16.5 steps to get the
+ // step size. Then we compute the start of the first step (the one where value 0 starts)
+ // by adding half-a-step to the unigramFrequency. From there, we compute the integer
+ // number of steps to the bigramFrequency. One last thing: we want our steps to include
+ // their lower bound and exclude their higher bound so we need to have the first step
+ // start at exactly 1 unit higher than floor(unigramFreq + half a step).
+ // Note : to reconstruct the score, the dictionary reader will need to divide
+ // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step,
+ // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best
+ // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the
+ // step pointed by the discretized frequency.
+ final float stepSize =
+ (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+ / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+ final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f);
+ final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize);
+ // If the bigram freq is less than half-a-step higher than the unigram freq, we get -1
+ // here. The best approximation would be the unigram freq itself, so we should not
+ // include this bigram in the dictionary. For now, register as 0, and live with the
+ // small over-estimation that we get in this case. TODO: actually remove this bigram
+ // if discretizedFrequency < 0.
+ return discretizedFrequency > 0 ? discretizedFrequency : 0;
+ }
+
+ /**
+ * Makes the flag value for a shortcut.
+ *
+ * @param more whether there are more attributes after this one.
+ * @param frequency the frequency of the attribute, 0..15
+ * @return the flags
+ */
+ static final int makeShortcutFlags(final boolean more, final int frequency) {
+ return (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+ + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY);
+ }
+
+ /* package */ static final int getChildrenPosition(final PtNode ptNode) {
+ int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
+ + getNodeHeaderSize(ptNode);
+ if (ptNode.isTerminal()) {
+ // A terminal node has the frequency.
+ // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
+ // position.
+ positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ }
+ return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
+ : ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
+ }
+
+ /**
+ * Write a PtNodeArray. The PtNodeArray is expected to have its final position cached.
+ *
+ * @param dict the dictionary the node array is a part of (for relative offsets).
+ * @param dictEncoder the dictionary encoder.
+ * @param ptNodeArray the node array to write.
+ */
+ @SuppressWarnings("unused")
+ /* package */ static void writePlacedPtNodeArray(final FusionDictionary dict,
+ final DictEncoder dictEncoder, final PtNodeArray ptNodeArray) {
+ // TODO: Make the code in common with BinaryDictIOUtils#writePtNode
+ dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate);
+
+ final int ptNodeCount = ptNodeArray.mData.size();
+ dictEncoder.writePtNodeCount(ptNodeCount);
+ final int parentPosition =
+ (ptNodeArray.mCachedParentAddress == FormatSpec.NO_PARENT_ADDRESS)
+ ? FormatSpec.NO_PARENT_ADDRESS
+ : ptNodeArray.mCachedParentAddress + ptNodeArray.mCachedAddressAfterUpdate;
+ for (int i = 0; i < ptNodeCount; ++i) {
+ final PtNode ptNode = ptNodeArray.mData.get(i);
+ if (dictEncoder.getPosition() != ptNode.mCachedAddressAfterUpdate) {
+ throw new RuntimeException("Bug: write index is not the same as the cached address "
+ + "of the node : " + dictEncoder.getPosition() + " <> "
+ + ptNode.mCachedAddressAfterUpdate);
+ }
+ // Sanity checks.
+ if (DBG && ptNode.getProbability() > FormatSpec.MAX_TERMINAL_FREQUENCY) {
+ throw new RuntimeException("A node has a frequency > "
+ + FormatSpec.MAX_TERMINAL_FREQUENCY
+ + " : " + ptNode.mProbabilityInfo.toString());
+ }
+ dictEncoder.writePtNode(ptNode, dict);
+ }
+ if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate
+ + ptNodeArray.mCachedSize) {
+ throw new RuntimeException("Not the same size : written "
+ + (dictEncoder.getPosition() - ptNodeArray.mCachedAddressAfterUpdate)
+ + " bytes from a node that should have " + ptNodeArray.mCachedSize + " bytes");
+ }
+ }
+
+ /**
+ * Dumps a collection of useful statistics about a list of PtNode arrays.
+ *
+ * This prints purely informative stuff, like the total estimated file size, the
+ * number of PtNode arrays, of PtNodes, the repartition of each address size, etc
+ *
+ * @param ptNodeArrays the list of PtNode arrays.
+ */
+ /* package */ static void showStatistics(ArrayList<PtNodeArray> ptNodeArrays) {
+ int firstTerminalAddress = Integer.MAX_VALUE;
+ int lastTerminalAddress = Integer.MIN_VALUE;
+ int size = 0;
+ int ptNodes = 0;
+ int maxNodes = 0;
+ int maxRuns = 0;
+ for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+ if (maxNodes < ptNodeArray.mData.size()) maxNodes = ptNodeArray.mData.size();
+ for (final PtNode ptNode : ptNodeArray.mData) {
+ ++ptNodes;
+ if (ptNode.mChars.length > maxRuns) maxRuns = ptNode.mChars.length;
+ if (ptNode.isTerminal()) {
+ if (ptNodeArray.mCachedAddressAfterUpdate < firstTerminalAddress)
+ firstTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
+ if (ptNodeArray.mCachedAddressAfterUpdate > lastTerminalAddress)
+ lastTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
+ }
+ }
+ if (ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize > size) {
+ size = ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize;
+ }
+ }
+ final int[] ptNodeCounts = new int[maxNodes + 1];
+ final int[] runCounts = new int[maxRuns + 1];
+ for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+ ++ptNodeCounts[ptNodeArray.mData.size()];
+ for (final PtNode ptNode : ptNodeArray.mData) {
+ ++runCounts[ptNode.mChars.length];
+ }
+ }
+
+ MakedictLog.i("Statistics:\n"
+ + " total file size " + size + "\n"
+ + " " + ptNodeArrays.size() + " node arrays\n"
+ + " " + ptNodes + " PtNodes (" + ((float)ptNodes / ptNodeArrays.size())
+ + " PtNodes per node)\n"
+ + " first terminal at " + firstTerminalAddress + "\n"
+ + " last terminal at " + lastTerminalAddress + "\n"
+ + " PtNode stats : max = " + maxNodes);
+ for (int i = 0; i < ptNodeCounts.length; ++i) {
+ MakedictLog.i(" " + i + " : " + ptNodeCounts[i]);
+ }
+ MakedictLog.i(" Character run stats : max = " + maxRuns);
+ for (int i = 0; i < runCounts.length; ++i) {
+ MakedictLog.i(" " + i + " : " + runCounts[i]);
+ }
+ }
+
+ /**
+ * Writes a file header to an output stream.
+ *
+ * @param destination the stream to write the file header to.
+ * @param dict the dictionary to write.
+ * @param formatOptions file format options.
+ * @return the size of the header.
+ */
+ /* package */ static int writeDictionaryHeader(final OutputStream destination,
+ final FusionDictionary dict, final FormatOptions formatOptions)
+ throws IOException, UnsupportedFormatException {
+ final int version = formatOptions.mVersion;
+ if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+ || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+ throw new UnsupportedFormatException("Requested file format version " + version
+ + ", but this implementation only supports versions "
+ + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
+ + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+ }
+
+ ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
+
+ // The magic number in big-endian order.
+ // Magic number for all versions.
+ headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 24)));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 16)));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 8)));
+ headerBuffer.write((byte) (0xFF & FormatSpec.MAGIC_NUMBER));
+ // Dictionary version.
+ headerBuffer.write((byte) (0xFF & (version >> 8)));
+ headerBuffer.write((byte) (0xFF & version));
+
+ // Options flags
+ // TODO: Remove this field.
+ final int options = 0;
+ headerBuffer.write((byte) (0xFF & (options >> 8)));
+ headerBuffer.write((byte) (0xFF & options));
+ final int headerSizeOffset = headerBuffer.size();
+ // Placeholder to be written later with header size.
+ for (int i = 0; i < 4; ++i) {
+ headerBuffer.write(0);
+ }
+ // Write out the options.
+ for (final String key : dict.mOptions.mAttributes.keySet()) {
+ final String value = dict.mOptions.mAttributes.get(key);
+ CharEncoding.writeString(headerBuffer, key);
+ CharEncoding.writeString(headerBuffer, value);
+ }
+ final int size = headerBuffer.size();
+ final byte[] bytes = headerBuffer.toByteArray();
+ // Write out the header size.
+ bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24));
+ bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16));
+ bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8));
+ bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0));
+ destination.write(bytes);
+
+ headerBuffer.close();
+ return size;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
new file mode 100644
index 000000000..9c3b37387
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Stack;
+
+public final class BinaryDictIOUtils {
+ private static final boolean DBG = false;
+
+ private BinaryDictIOUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ /**
+ * Returns new dictionary decoder.
+ *
+ * @param dictFile the dictionary file.
+ * @param bufferType The type of buffer, as one of USE_* in DictDecoder.
+ * @return new dictionary decoder if the dictionary file exists, otherwise null.
+ */
+ public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+ final long length, final int bufferType) {
+ if (dictFile.isDirectory()) {
+ return new Ver4DictDecoder(dictFile, bufferType);
+ } else if (dictFile.isFile()) {
+ return new Ver2DictDecoder(dictFile, offset, length, bufferType);
+ }
+ return null;
+ }
+
+ public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+ final long length, final DictionaryBufferFactory factory) {
+ if (dictFile.isDirectory()) {
+ return new Ver4DictDecoder(dictFile, factory);
+ } else if (dictFile.isFile()) {
+ return new Ver2DictDecoder(dictFile, offset, length, factory);
+ }
+ return null;
+ }
+
+ public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+ final long length) {
+ return getDictDecoder(dictFile, offset, length, DictDecoder.USE_READONLY_BYTEBUFFER);
+ }
+
+ private static final class Position {
+ public static final int NOT_READ_PTNODE_COUNT = -1;
+
+ public int mAddress;
+ public int mNumOfPtNode;
+ public int mPosition;
+ public int mLength;
+
+ public Position(int address, int length) {
+ mAddress = address;
+ mLength = length;
+ mNumOfPtNode = NOT_READ_PTNODE_COUNT;
+ }
+ }
+
+ /**
+ * Retrieves all node arrays without recursive call.
+ */
+ private static void readUnigramsAndBigramsBinaryInner(final DictDecoder dictDecoder,
+ final int bodyOffset, final Map<Integer, String> words,
+ final Map<Integer, Integer> frequencies,
+ final Map<Integer, ArrayList<PendingAttribute>> bigrams) {
+ int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1];
+
+ Stack<Position> stack = new Stack<>();
+ int index = 0;
+
+ Position initPos = new Position(bodyOffset, 0);
+ stack.push(initPos);
+
+ while (!stack.empty()) {
+ Position p = stack.peek();
+
+ if (DBG) {
+ MakedictLog.d("read: address=" + p.mAddress + ", numOfPtNode=" +
+ p.mNumOfPtNode + ", position=" + p.mPosition + ", length=" + p.mLength);
+ }
+
+ if (dictDecoder.getPosition() != p.mAddress) dictDecoder.setPosition(p.mAddress);
+ if (index != p.mLength) index = p.mLength;
+
+ if (p.mNumOfPtNode == Position.NOT_READ_PTNODE_COUNT) {
+ p.mNumOfPtNode = dictDecoder.readPtNodeCount();
+ p.mAddress = dictDecoder.getPosition();
+ p.mPosition = 0;
+ }
+ if (p.mNumOfPtNode == 0) {
+ stack.pop();
+ continue;
+ }
+ final PtNodeInfo ptNodeInfo = dictDecoder.readPtNode(p.mAddress);
+ for (int i = 0; i < ptNodeInfo.mCharacters.length; ++i) {
+ pushedChars[index++] = ptNodeInfo.mCharacters[i];
+ }
+ p.mPosition++;
+ if (ptNodeInfo.isTerminal()) {// found word
+ words.put(ptNodeInfo.mOriginalAddress, new String(pushedChars, 0, index));
+ frequencies.put(
+ ptNodeInfo.mOriginalAddress, ptNodeInfo.mProbabilityInfo.mProbability);
+ if (ptNodeInfo.mBigrams != null) {
+ bigrams.put(ptNodeInfo.mOriginalAddress, ptNodeInfo.mBigrams);
+ }
+ }
+
+ if (p.mPosition == p.mNumOfPtNode) {
+ stack.pop();
+ } else {
+ // The PtNode array has more PtNodes.
+ p.mAddress = dictDecoder.getPosition();
+ }
+
+ if (hasChildrenAddress(ptNodeInfo.mChildrenAddress)) {
+ final Position childrenPos = new Position(ptNodeInfo.mChildrenAddress, index);
+ stack.push(childrenPos);
+ }
+ }
+ }
+
+ /**
+ * Reads unigrams and bigrams from the binary file.
+ * Doesn't store a full memory representation of the dictionary.
+ *
+ * @param dictDecoder the dict decoder.
+ * @param words the map to store the address as a key and the word as a value.
+ * @param frequencies the map to store the address as a key and the frequency as a value.
+ * @param bigrams the map to store the address as a key and the list of address as a value.
+ * @throws IOException if the file can't be read.
+ * @throws UnsupportedFormatException if the format of the file is not recognized.
+ */
+ /* package */ static void readUnigramsAndBigramsBinary(final DictDecoder dictDecoder,
+ final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
+ final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
+ UnsupportedFormatException {
+ // Read header
+ final DictionaryHeader header = dictDecoder.readHeader();
+ readUnigramsAndBigramsBinaryInner(dictDecoder, header.mBodyOffset, words,
+ frequencies, bigrams);
+ }
+
+ /**
+ * Gets the address of the last PtNode of the exact matching word in the dictionary.
+ * If no match is found, returns NOT_VALID_WORD.
+ *
+ * @param dictDecoder the dict decoder.
+ * @param word the word we search for.
+ * @return the address of the terminal node.
+ * @throws IOException if the file can't be read.
+ * @throws UnsupportedFormatException if the format of the file is not recognized.
+ */
+ @UsedForTesting
+ /* package */ static int getTerminalPosition(final DictDecoder dictDecoder,
+ final String word) throws IOException, UnsupportedFormatException {
+ if (word == null) return FormatSpec.NOT_VALID_WORD;
+ dictDecoder.setPosition(0);
+ dictDecoder.readHeader();
+ int wordPos = 0;
+ final int wordLen = word.codePointCount(0, word.length());
+ for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
+ if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
+
+ do {
+ final int ptNodeCount = dictDecoder.readPtNodeCount();
+ boolean foundNextPtNode = false;
+ for (int i = 0; i < ptNodeCount; ++i) {
+ final int ptNodePos = dictDecoder.getPosition();
+ final PtNodeInfo currentInfo = dictDecoder.readPtNode(ptNodePos);
+ boolean same = true;
+ for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
+ p < currentInfo.mCharacters.length;
+ ++p, j = word.offsetByCodePoints(j, 1)) {
+ if (wordPos + p >= wordLen
+ || word.codePointAt(j) != currentInfo.mCharacters[p]) {
+ same = false;
+ break;
+ }
+ }
+
+ if (same) {
+ // found the PtNode matches the word.
+ if (wordPos + currentInfo.mCharacters.length == wordLen) {
+ if (!currentInfo.isTerminal()) {
+ return FormatSpec.NOT_VALID_WORD;
+ } else {
+ return ptNodePos;
+ }
+ }
+ wordPos += currentInfo.mCharacters.length;
+ if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
+ return FormatSpec.NOT_VALID_WORD;
+ }
+ foundNextPtNode = true;
+ dictDecoder.setPosition(currentInfo.mChildrenAddress);
+ break;
+ }
+ }
+ if (foundNextPtNode) break;
+ return FormatSpec.NOT_VALID_WORD;
+ } while(true);
+ }
+ return FormatSpec.NOT_VALID_WORD;
+ }
+
+ /**
+ * Writes a PtNodeCount to the stream.
+ *
+ * @param destination the stream to write.
+ * @param ptNodeCount the count.
+ * @return the size written in bytes.
+ */
+ @UsedForTesting
+ static int writePtNodeCount(final OutputStream destination, final int ptNodeCount)
+ throws IOException {
+ final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
+ // the count must fit on one byte or two bytes.
+ // Please see comments in FormatSpec.
+ if (countSize != 1 && countSize != 2) {
+ throw new RuntimeException("Strange size from getPtNodeCountSize : " + countSize);
+ }
+ final int encodedPtNodeCount = (countSize == 2) ?
+ (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount;
+ BinaryDictEncoderUtils.writeUIntToStream(destination, encodedPtNodeCount, countSize);
+ return countSize;
+ }
+
+ /**
+ * Helper method to hide the actual value of the no children address.
+ */
+ public static boolean hasChildrenAddress(final int address) {
+ return FormatSpec.NO_CHILDREN_ADDRESS != address;
+ }
+
+ /**
+ * Compute the binary size of the node count
+ * @param count the node count
+ * @return the size of the node count, either 1 or 2 bytes.
+ */
+ public static int getPtNodeCountSize(final int count) {
+ if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= count) {
+ return 1;
+ } else if (FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY >= count) {
+ return 2;
+ } else {
+ throw new RuntimeException("Can't have more than "
+ + FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY + " PtNode in a PtNodeArray (found "
+ + count + ")");
+ }
+ }
+
+ static int getChildrenAddressSize(final int optionFlags) {
+ switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+ return 1;
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+ return 2;
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+ return 3;
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Calculate bigram frequency from compressed value
+ *
+ * @param unigramFrequency
+ * @param bigramFrequency compressed frequency
+ * @return approximate bigram frequency
+ */
+ @UsedForTesting
+ public static int reconstructBigramFrequency(final int unigramFrequency,
+ final int bigramFrequency) {
+ final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+ / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+ final float resultFreqFloat = unigramFrequency + stepSize * (bigramFrequency + 1.0f);
+ return (int)resultFreqFloat;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
deleted file mode 100644
index afe5adb73..000000000
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Random;
-
-@LargeTest
-public class BinaryDictIOUtilsTests extends AndroidTestCase {
- private static final String TAG = BinaryDictIOUtilsTests.class.getSimpleName();
- private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
- new FormatSpec.FormatOptions(3, true);
-
- private static final ArrayList<String> sWords = CollectionUtils.newArrayList();
- public static final int DEFAULT_MAX_UNIGRAMS = 1500;
- private final int mMaxUnigrams;
-
- private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
-
- private static final int VERSION3 = 3;
- private static final int VERSION4 = 4;
-
- private static final String[] CHARACTERS = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
- "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- "\u00FC" /* ü */, "\u00E2" /* â */, "\u00F1" /* ñ */, // accented characters
- "\u4E9C" /* 亜 */, "\u4F0A" /* 伊 */, "\u5B87" /* 宇 */, // kanji
- "\uD841\uDE28" /* 𠘨 */, "\uD840\uDC0B" /* 𠀋 */, "\uD861\uDED7" /* 𨛗 */ // surrogate pair
- };
-
- public BinaryDictIOUtilsTests() {
- // 1500 is the default max unigrams
- this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
- }
-
- public BinaryDictIOUtilsTests(final long seed, final int maxUnigrams) {
- super();
- Log.d(TAG, "Seed for test is " + seed + ", maxUnigrams is " + maxUnigrams);
- mMaxUnigrams = maxUnigrams;
- final Random random = new Random(seed);
- sWords.clear();
- for (int i = 0; i < maxUnigrams; ++i) {
- sWords.add(generateWord(random.nextInt()));
- }
- }
-
- // Utilities for test
- private String generateWord(final int value) {
- final int lengthOfChars = CHARACTERS.length;
- StringBuilder builder = new StringBuilder("");
- long lvalue = Math.abs((long)value);
- while (lvalue > 0) {
- builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
- lvalue /= lengthOfChars;
- }
- if (builder.toString().equals("")) return "a";
- return builder.toString();
- }
-
- private static void printPtNode(final PtNodeInfo info) {
- Log.d(TAG, " PtNode at " + info.mOriginalAddress);
- Log.d(TAG, " flags = " + info.mFlags);
- Log.d(TAG, " parentAddress = " + info.mParentAddress);
- Log.d(TAG, " characters = " + new String(info.mCharacters, 0,
- info.mCharacters.length));
- if (info.mFrequency != -1) Log.d(TAG, " frequency = " + info.mFrequency);
- if (info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
- Log.d(TAG, " children address = no children address");
- } else {
- Log.d(TAG, " children address = " + info.mChildrenAddress);
- }
- if (info.mShortcutTargets != null) {
- for (final WeightedString ws : info.mShortcutTargets) {
- Log.d(TAG, " shortcuts = " + ws.mWord);
- }
- }
- if (info.mBigrams != null) {
- for (final PendingAttribute attr : info.mBigrams) {
- Log.d(TAG, " bigram = " + attr.mAddress);
- }
- }
- Log.d(TAG, " end address = " + info.mEndAddress);
- }
-
- private static void printNode(final Ver3DictDecoder dictDecoder,
- final FormatSpec.FormatOptions formatOptions) {
- final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
- Log.d(TAG, "Node at " + dictBuffer.position());
- final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
- Log.d(TAG, " ptNodeCount = " + count);
- for (int i = 0; i < count; ++i) {
- final PtNodeInfo currentInfo = dictDecoder.readPtNode(dictBuffer.position(),
- formatOptions);
- printPtNode(currentInfo);
- }
- if (formatOptions.mSupportsDynamicUpdate) {
- final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
- Log.d(TAG, " forwardLinkAddress = " + forwardLinkAddress);
- }
- }
-
- @SuppressWarnings("unused")
- private static void printBinaryFile(final Ver3DictDecoder dictDecoder)
- throws IOException, UnsupportedFormatException {
- final FileHeader fileHeader = dictDecoder.readHeader();
- final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
- while (dictBuffer.position() < dictBuffer.limit()) {
- printNode(dictDecoder, fileHeader.mFormatOptions);
- }
- }
-
- private int getWordPosition(final File file, final String word) {
- int position = FormatSpec.NOT_VALID_WORD;
-
- try {
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
- DictDecoder.USE_READONLY_BYTEBUFFER);
- position = dictDecoder.getTerminalPosition(word);
- } catch (IOException e) {
- } catch (UnsupportedFormatException e) {
- }
- return position;
- }
-
- /**
- * Find a word using the DictDecoder.
- *
- * @param dictDecoder the dict decoder
- * @param word the word searched
- * @return the found ptNodeInfo
- * @throws IOException
- * @throws UnsupportedFormatException
- */
- private static PtNodeInfo findWordByBinaryDictReader(final DictDecoder dictDecoder,
- final String word) throws IOException, UnsupportedFormatException {
- int position = dictDecoder.getTerminalPosition(word);
- if (position != FormatSpec.NOT_VALID_WORD) {
- dictDecoder.setPosition(0);
- final FileHeader header = dictDecoder.readHeader();
- dictDecoder.setPosition(position);
- return dictDecoder.readPtNode(position, header.mFormatOptions);
- }
- return null;
- }
-
- private PtNodeInfo findWordFromFile(final File file, final String word) {
- final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
- PtNodeInfo info = null;
- try {
- dictDecoder.openDictBuffer();
- info = findWordByBinaryDictReader(dictDecoder, word);
- } catch (IOException e) {
- } catch (UnsupportedFormatException e) {
- }
- return info;
- }
-
- // return amount of time to insert a word
- private long insertAndCheckWord(final File file, final String word, final int frequency,
- final boolean exist, final ArrayList<WeightedString> bigrams,
- final ArrayList<WeightedString> shortcuts, final int formatVersion) {
- long amountOfTime = -1;
- try {
- final DictUpdater dictUpdater;
- if (formatVersion == VERSION3) {
- dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
- } else {
- throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't"
- + " exist.");
- }
-
- if (!exist) {
- assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
- }
- final long now = System.nanoTime();
- dictUpdater.insertWord(word, frequency, bigrams, shortcuts, false, false);
- amountOfTime = System.nanoTime() - now;
- MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
- } catch (IOException e) {
- Log.e(TAG, "Raised an IOException while inserting a word", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Raised an UnsupportedFormatException error while inserting a word", e);
- }
- return amountOfTime;
- }
-
- private void deleteWord(final File file, final String word, final int formatVersion) {
- try {
- final DictUpdater dictUpdater;
- if (formatVersion == VERSION3) {
- dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
- } else {
- throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't"
- + " exist.");
- }
- dictUpdater.deleteWord(word);
- } catch (IOException e) {
- } catch (UnsupportedFormatException e) {
- }
- }
-
- private void checkReverseLookup(final File file, final String word, final int position) {
-
- try {
- final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
- final FileHeader fileHeader = dictDecoder.readHeader();
- assertEquals(word,
- BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize,
- position, fileHeader.mFormatOptions).mWord);
- } catch (IOException e) {
- Log.e(TAG, "Raised an IOException while looking up a word", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Raised an UnsupportedFormatException error while looking up a word", e);
- }
- }
-
- private void runTestInsertWord(final int formatVersion) {
- File file = null;
- try {
- file = File.createTempFile("testInsertWord", TEST_DICT_FILE_EXTENSION,
- getContext().getCacheDir());
- } catch (IOException e) {
- fail("IOException while creating temporary file: " + e);
- }
-
- // set an initial dictionary.
- final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
- dict.add("abcd", 10, null, false);
-
- try {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
- } catch (IOException e) {
- fail("IOException while writing an initial dictionary : " + e);
- } catch (UnsupportedFormatException e) {
- fail("UnsupportedFormatException while writing an initial dictionary : " + e);
- }
-
- MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
- insertAndCheckWord(file, "abcde", 10, false, null, null, formatVersion);
-
- insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null, formatVersion);
- checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn"));
-
- insertAndCheckWord(file, "abcdabcd", 10, false, null, null, formatVersion);
- checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd"));
-
- // update the existing word.
- insertAndCheckWord(file, "abcdabcd", 15, true, null, null, formatVersion);
-
- // split 1
- insertAndCheckWord(file, "ab", 20, false, null, null, formatVersion);
-
- // split 2
- insertAndCheckWord(file, "ami", 30, false, null, null, formatVersion);
-
- deleteWord(file, "ami", formatVersion);
- assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "ami"));
-
- insertAndCheckWord(file, "abcdabfg", 30, false, null, null, formatVersion);
-
- deleteWord(file, "abcd", formatVersion);
- assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
- }
-
- public void testInsertWord() {
- runTestInsertWord(VERSION3);
- }
-
- private void runTestInsertWordWithBigrams(final int formatVersion) {
- File file = null;
- try {
- file = File.createTempFile("testInsertWordWithBigrams", TEST_DICT_FILE_EXTENSION,
- getContext().getCacheDir());
- } catch (IOException e) {
- fail("IOException while creating temporary file: " + e);
- }
-
- // set an initial dictionary.
- final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
- dict.add("abcd", 10, null, false);
- dict.add("efgh", 15, null, false);
-
- try {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
- } catch (IOException e) {
- fail("IOException while writing an initial dictionary : " + e);
- } catch (UnsupportedFormatException e) {
- fail("UnsupportedFormatException while writing an initial dictionary : " + e);
- }
-
- final ArrayList<WeightedString> banana = new ArrayList<WeightedString>();
- banana.add(new WeightedString("banana", 10));
-
- insertAndCheckWord(file, "banana", 0, false, null, null, formatVersion);
- insertAndCheckWord(file, "recursive", 60, true, banana, null, formatVersion);
-
- final PtNodeInfo info = findWordFromFile(file, "recursive");
- int bananaPos = getWordPosition(file, "banana");
- assertNotNull(info.mBigrams);
- assertEquals(info.mBigrams.size(), 1);
- assertEquals(info.mBigrams.get(0).mAddress, bananaPos);
- }
-
- public void testInsertWordWithBigrams() {
- runTestInsertWordWithBigrams(VERSION3);
- }
-
- private void runTestRandomWords(final int formatVersion) {
- File file = null;
- try {
- file = File.createTempFile("testRandomWord", TEST_DICT_FILE_EXTENSION,
- getContext().getCacheDir());
- } catch (IOException e) {
- }
- assertNotNull(file);
-
- // set an initial dictionary.
- final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
- false));
- dict.add("initial", 10, null, false);
-
- try {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
- } catch (IOException e) {
- assertTrue(false);
- } catch (UnsupportedFormatException e) {
- assertTrue(false);
- }
-
- long maxTimeToInsert = 0, sum = 0;
- long minTimeToInsert = 100000000; // 1000000000 is an upper bound for minTimeToInsert.
- int cnt = 0;
- for (final String word : sWords) {
- final long diff = insertAndCheckWord(file, word,
- cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null, formatVersion);
- maxTimeToInsert = Math.max(maxTimeToInsert, diff);
- minTimeToInsert = Math.min(minTimeToInsert, diff);
- sum += diff;
- cnt++;
- }
- cnt = 0;
- for (final String word : sWords) {
- MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
- }
-
- Log.d(TAG, "Test version " + formatVersion);
- Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms.");
- Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms.");
- Log.d(TAG, "avg = " + ((double)sum/mMaxUnigrams/1000000) + " ms.");
- }
-
- public void testRandomWords() {
- runTestRandomWords(VERSION3);
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
new file mode 100644
index 000000000..5a3eba801
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+
+import java.io.File;
+import java.util.HashMap;
+
+public class BinaryDictUtils {
+ public static final int USE_BYTE_ARRAY = 1;
+ public static final int USE_BYTE_BUFFER = 2;
+
+ public static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+
+ public static final FormatSpec.FormatOptions VERSION2_OPTIONS =
+ new FormatSpec.FormatOptions(FormatSpec.VERSION2);
+ public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP =
+ new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */);
+ public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP =
+ new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* hasTimestamp */);
+
+ public static DictionaryOptions makeDictionaryOptions(final String id, final String version,
+ final FormatSpec.FormatOptions formatOptions) {
+ final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>());
+ options.mAttributes.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, "en_US");
+ options.mAttributes.put(DictionaryHeader.DICTIONARY_ID_KEY, id);
+ options.mAttributes.put(DictionaryHeader.DICTIONARY_VERSION_KEY, version);
+ if (formatOptions.mHasTimestamp) {
+ options.mAttributes.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ options.mAttributes.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ }
+ return options;
+ }
+
+ public static File getDictFile(final String name, final String version,
+ final FormatOptions formatOptions, final File directory) {
+ if (formatOptions.mVersion == FormatSpec.VERSION2) {
+ return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION);
+ } else if (formatOptions.mVersion == FormatSpec.VERSION4) {
+ return new File(directory, name + "." + version);
+ } else {
+ throw new RuntimeException("the format option has a wrong version : "
+ + formatOptions.mVersion);
+ }
+ }
+
+ public static DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) {
+ if (formatOptions.mVersion == FormatSpec.VERSION4) {
+ if (!file.isDirectory()) {
+ file.mkdir();
+ }
+ return new Ver4DictEncoder(file);
+ } else if (formatOptions.mVersion == FormatSpec.VERSION2) {
+ return new Ver2DictEncoder(file);
+ } else {
+ throw new RuntimeException("The format option has a wrong version : "
+ + formatOptions.mVersion);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java
new file mode 100644
index 000000000..a3b28a702
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+/**
+ * An interface of binary dictionary decoders.
+ */
+// TODO: Straighten out responsibility for the buffer's file pointer.
+public interface DictDecoder {
+
+ /**
+ * Reads and returns the file header.
+ */
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException;
+
+ /**
+ * Reads PtNode from ptNodePos.
+ * @param ptNodePos the position of PtNode.
+ * @return PtNodeInfo.
+ */
+ public PtNodeInfo readPtNode(final int ptNodePos);
+
+ /**
+ * Reads a buffer and returns the memory representation of the dictionary.
+ *
+ * This high-level method takes a buffer and reads its contents, populating a
+ * FusionDictionary structure.
+ *
+ * @param deleteDictIfBroken a flag indicating whether this method should remove the broken
+ * dictionary or not.
+ * @return the created dictionary.
+ */
+ @UsedForTesting
+ public FusionDictionary readDictionaryBinary(final boolean deleteDictIfBroken)
+ throws FileNotFoundException, IOException, UnsupportedFormatException;
+
+ /**
+ * Gets the address of the last PtNode of the exact matching word in the dictionary.
+ * If no match is found, returns NOT_VALID_WORD.
+ *
+ * @param word the word we search for.
+ * @return the address of the terminal node.
+ * @throws IOException if the file can't be read.
+ * @throws UnsupportedFormatException if the format of the file is not recognized.
+ */
+ @UsedForTesting
+ public int getTerminalPosition(final String word)
+ throws IOException, UnsupportedFormatException;
+
+ /**
+ * Reads unigrams and bigrams from the binary file.
+ * Doesn't store a full memory representation of the dictionary.
+ *
+ * @param words the map to store the address as a key and the word as a value.
+ * @param frequencies the map to store the address as a key and the frequency as a value.
+ * @param bigrams the map to store the address as a key and the list of address as a value.
+ * @throws IOException if the file can't be read.
+ * @throws UnsupportedFormatException if the format of the file is not recognized.
+ */
+ @UsedForTesting
+ public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words,
+ final TreeMap<Integer, Integer> frequencies,
+ final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams)
+ throws IOException, UnsupportedFormatException;
+
+ /**
+ * Sets the position of the buffer to the given value.
+ *
+ * @param newPos the new position
+ */
+ public void setPosition(final int newPos);
+
+ /**
+ * Gets the position of the buffer.
+ *
+ * @return the position
+ */
+ public int getPosition();
+
+ /**
+ * Reads and returns the PtNode count out of a buffer and forwards the pointer.
+ */
+ public int readPtNodeCount();
+
+ /**
+ * Opens the dictionary file and makes DictBuffer.
+ */
+ @UsedForTesting
+ public void openDictBuffer() throws FileNotFoundException, IOException,
+ UnsupportedFormatException;
+ @UsedForTesting
+ public boolean isDictBufferOpen();
+
+ // Constants for DictionaryBufferFactory.
+ public static final int USE_READONLY_BYTEBUFFER = 0x01000000;
+ public static final int USE_BYTEARRAY = 0x02000000;
+ public static final int USE_WRITABLE_BYTEBUFFER = 0x03000000;
+ public static final int MASK_DICTBUFFER = 0x0F000000;
+
+ public interface DictionaryBufferFactory {
+ public DictBuffer getDictionaryBuffer(final File file)
+ throws FileNotFoundException, IOException;
+ }
+
+ /**
+ * Creates DictionaryBuffer using a ByteBuffer
+ *
+ * This class uses less memory than DictionaryBufferFromByteArrayFactory,
+ * but doesn't perform as fast.
+ * When operating on a big dictionary, this class is preferred.
+ */
+ public static final class DictionaryBufferFromReadOnlyByteBufferFactory
+ implements DictionaryBufferFactory {
+ @Override
+ public DictBuffer getDictionaryBuffer(final File file)
+ throws FileNotFoundException, IOException {
+ FileInputStream inStream = null;
+ ByteBuffer buffer = null;
+ try {
+ inStream = new FileInputStream(file);
+ buffer = inStream.getChannel().map(FileChannel.MapMode.READ_ONLY,
+ 0, file.length());
+ } finally {
+ if (inStream != null) {
+ inStream.close();
+ }
+ }
+ if (buffer != null) {
+ return new BinaryDictDecoderUtils.ByteBufferDictBuffer(buffer);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Creates DictionaryBuffer using a byte array
+ *
+ * This class performs faster than other classes, but consumes more memory.
+ * When operating on a small dictionary, this class is preferred.
+ */
+ public static final class DictionaryBufferFromByteArrayFactory
+ implements DictionaryBufferFactory {
+ @Override
+ public DictBuffer getDictionaryBuffer(final File file)
+ throws FileNotFoundException, IOException {
+ FileInputStream inStream = null;
+ try {
+ inStream = new FileInputStream(file);
+ final byte[] array = new byte[(int) file.length()];
+ inStream.read(array);
+ return new ByteArrayDictBuffer(array);
+ } finally {
+ if (inStream != null) {
+ inStream.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates DictionaryBuffer using a writable ByteBuffer and a RandomAccessFile.
+ *
+ * This class doesn't perform as fast as other classes,
+ * but this class is the only option available for destructive operations (insert or delete)
+ * on a dictionary.
+ */
+ @UsedForTesting
+ public static final class DictionaryBufferFromWritableByteBufferFactory
+ implements DictionaryBufferFactory {
+ @Override
+ public DictBuffer getDictionaryBuffer(final File file)
+ throws FileNotFoundException, IOException {
+ RandomAccessFile raFile = null;
+ ByteBuffer buffer = null;
+ try {
+ raFile = new RandomAccessFile(file, "rw");
+ buffer = raFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
+ } finally {
+ if (raFile != null) {
+ raFile.close();
+ }
+ }
+ if (buffer != null) {
+ return new BinaryDictDecoderUtils.ByteBufferDictBuffer(buffer);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * @return whether this decoder has a valid binary dictionary that it can decode.
+ */
+ public boolean hasValidRawBinaryDictionary();
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
new file mode 100644
index 000000000..678c5ca6b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+
+import java.io.IOException;
+
+/**
+ * An interface of binary dictionary encoder.
+ */
+public interface DictEncoder {
+ @UsedForTesting
+ public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+ throws IOException, UnsupportedFormatException;
+
+ public void setPosition(final int position);
+ public int getPosition();
+ public void writePtNodeCount(final int ptNodeCount);
+ public void writeForwardLinkAddress(final int forwardLinkAddress);
+ public void writePtNode(final PtNode ptNode, final FusionDictionary dict);
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
new file mode 100644
index 000000000..4a8c178b5
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * A dictionary that can fusion heads and tails of words for more compression.
+ */
+@UsedForTesting
+public final class FusionDictionary implements Iterable<WordProperty> {
+ private static final boolean DBG = MakedictLog.DBG;
+
+ private static int CHARACTER_NOT_FOUND_INDEX = -1;
+
+ /**
+ * A node array of the dictionary, containing several PtNodes.
+ *
+ * A PtNodeArray is but an ordered array of PtNodes, which essentially contain all the
+ * real information.
+ * This class also contains fields to cache size and address, to help with binary
+ * generation.
+ */
+ public static final class PtNodeArray {
+ ArrayList<PtNode> mData;
+ // To help with binary generation
+ int mCachedSize = Integer.MIN_VALUE;
+ // mCachedAddressBefore/AfterUpdate are helpers for binary dictionary generation. They
+ // always hold the same value except between dictionary address compression, during which
+ // the update process needs to know about both values at the same time. Updating will
+ // update the AfterUpdate value, and the code will move them to BeforeUpdate before
+ // the next update pass.
+ int mCachedAddressBeforeUpdate = Integer.MIN_VALUE;
+ int mCachedAddressAfterUpdate = Integer.MIN_VALUE;
+ int mCachedParentAddress = 0;
+
+ public PtNodeArray() {
+ mData = new ArrayList<>();
+ }
+ public PtNodeArray(ArrayList<PtNode> data) {
+ Collections.sort(data, PTNODE_COMPARATOR);
+ mData = data;
+ }
+ }
+
+ /**
+ * PtNode is a group of characters, with probability information, shortcut targets, bigrams,
+ * and children (Pt means Patricia Trie).
+ *
+ * This is the central class of the in-memory representation. A PtNode is what can
+ * be seen as a traditional "trie node", except it can hold several characters at the
+ * same time. A PtNode essentially represents one or several characters in the middle
+ * of the trie tree; as such, it can be a terminal, and it can have children.
+ * In this in-memory representation, whether the PtNode is a terminal or not is represented
+ * by mProbabilityInfo. The PtNode is a terminal when the mProbabilityInfo is not null and the
+ * PtNode is not a terminal when the mProbabilityInfo is null. A terminal may have non-null
+ * shortcuts and/or bigrams, but a non-terminal may not. Moreover, children, if present,
+ * are non-null.
+ */
+ public static final class PtNode {
+ private static final int NOT_A_TERMINAL = -1;
+ final int mChars[];
+ ArrayList<WeightedString> mShortcutTargets;
+ ArrayList<WeightedString> mBigrams;
+ // null == mProbabilityInfo indicates this is not a terminal.
+ ProbabilityInfo mProbabilityInfo;
+ int mTerminalId; // NOT_A_TERMINAL == mTerminalId indicates this is not a terminal.
+ PtNodeArray mChildren;
+ boolean mIsNotAWord; // Only a shortcut
+ boolean mIsBlacklistEntry;
+ // mCachedSize and mCachedAddressBefore/AfterUpdate are helpers for binary dictionary
+ // generation. Before and After always hold the same value except during dictionary
+ // address compression, where the update process needs to know about both values at the
+ // same time. Updating will update the AfterUpdate value, and the code will move them
+ // to BeforeUpdate before the next update pass.
+ // The update process does not need two versions of mCachedSize.
+ int mCachedSize; // The size, in bytes, of this PtNode.
+ int mCachedAddressBeforeUpdate; // The address of this PtNode (before update)
+ int mCachedAddressAfterUpdate; // The address of this PtNode (after update)
+
+ public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
+ mChars = chars;
+ mProbabilityInfo = probabilityInfo;
+ mTerminalId = probabilityInfo == null ? NOT_A_TERMINAL : probabilityInfo.mProbability;
+ mShortcutTargets = shortcutTargets;
+ mBigrams = bigrams;
+ mChildren = null;
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
+ }
+
+ public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo,
+ final boolean isNotAWord, final boolean isBlacklistEntry,
+ final PtNodeArray children) {
+ mChars = chars;
+ mProbabilityInfo = probabilityInfo;
+ mShortcutTargets = shortcutTargets;
+ mBigrams = bigrams;
+ mChildren = children;
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
+ }
+
+ public void addChild(PtNode n) {
+ if (null == mChildren) {
+ mChildren = new PtNodeArray();
+ }
+ mChildren.mData.add(n);
+ }
+
+ public int getTerminalId() {
+ return mTerminalId;
+ }
+
+ public boolean isTerminal() {
+ return mProbabilityInfo != null;
+ }
+
+ public int getProbability() {
+ if (isTerminal()) {
+ return mProbabilityInfo.mProbability;
+ } else {
+ return NOT_A_TERMINAL;
+ }
+ }
+
+ public boolean getIsNotAWord() {
+ return mIsNotAWord;
+ }
+
+ public boolean getIsBlacklistEntry() {
+ return mIsBlacklistEntry;
+ }
+
+ public ArrayList<WeightedString> getShortcutTargets() {
+ // We don't want write permission to escape outside the package, so we return a copy
+ if (null == mShortcutTargets) return null;
+ final ArrayList<WeightedString> copyOfShortcutTargets =
+ new ArrayList<>(mShortcutTargets);
+ return copyOfShortcutTargets;
+ }
+
+ public ArrayList<WeightedString> getBigrams() {
+ // We don't want write permission to escape outside the package, so we return a copy
+ if (null == mBigrams) return null;
+ final ArrayList<WeightedString> copyOfBigrams = new ArrayList<>(mBigrams);
+ return copyOfBigrams;
+ }
+
+ public boolean hasSeveralChars() {
+ assert(mChars.length > 0);
+ return 1 < mChars.length;
+ }
+
+ /**
+ * Adds a word to the bigram list. Updates the probability information if the word already
+ * exists.
+ */
+ public void addBigram(final String word, final ProbabilityInfo probabilityInfo) {
+ if (mBigrams == null) {
+ mBigrams = new ArrayList<>();
+ }
+ WeightedString bigram = getBigram(word);
+ if (bigram != null) {
+ bigram.mProbabilityInfo = probabilityInfo;
+ } else {
+ bigram = new WeightedString(word, probabilityInfo);
+ mBigrams.add(bigram);
+ }
+ }
+
+ /**
+ * Gets the shortcut target for the given word. Returns null if the word is not in the
+ * shortcut list.
+ */
+ public WeightedString getShortcut(final String word) {
+ // TODO: Don't do a linear search
+ if (mShortcutTargets != null) {
+ final int size = mShortcutTargets.size();
+ for (int i = 0; i < size; ++i) {
+ WeightedString shortcut = mShortcutTargets.get(i);
+ if (shortcut.mWord.equals(word)) {
+ return shortcut;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the bigram for the given word.
+ * Returns null if the word is not in the bigrams list.
+ */
+ public WeightedString getBigram(final String word) {
+ // TODO: Don't do a linear search
+ if (mBigrams != null) {
+ final int size = mBigrams.size();
+ for (int i = 0; i < size; ++i) {
+ WeightedString bigram = mBigrams.get(i);
+ if (bigram.mWord.equals(word)) {
+ return bigram;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Updates the PtNode with the given properties. Adds the shortcut and bigram lists to
+ * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only
+ * updated if they are higher than the existing ones.
+ */
+ private void update(final ProbabilityInfo probabilityInfo,
+ final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<WeightedString> bigrams,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
+ mProbabilityInfo = ProbabilityInfo.max(mProbabilityInfo, probabilityInfo);
+ if (shortcutTargets != null) {
+ if (mShortcutTargets == null) {
+ mShortcutTargets = shortcutTargets;
+ } else {
+ final int size = shortcutTargets.size();
+ for (int i = 0; i < size; ++i) {
+ final WeightedString shortcut = shortcutTargets.get(i);
+ final WeightedString existingShortcut = getShortcut(shortcut.mWord);
+ if (existingShortcut == null) {
+ mShortcutTargets.add(shortcut);
+ } else {
+ existingShortcut.mProbabilityInfo = ProbabilityInfo.max(
+ existingShortcut.mProbabilityInfo, shortcut.mProbabilityInfo);
+ }
+ }
+ }
+ }
+ if (bigrams != null) {
+ if (mBigrams == null) {
+ mBigrams = bigrams;
+ } else {
+ final int size = bigrams.size();
+ for (int i = 0; i < size; ++i) {
+ final WeightedString bigram = bigrams.get(i);
+ final WeightedString existingBigram = getBigram(bigram.mWord);
+ if (existingBigram == null) {
+ mBigrams.add(bigram);
+ } else {
+ existingBigram.mProbabilityInfo = ProbabilityInfo.max(
+ existingBigram.mProbabilityInfo, bigram.mProbabilityInfo);
+ }
+ }
+ }
+ }
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
+ }
+ }
+
+ public final DictionaryOptions mOptions;
+ public final PtNodeArray mRootNodeArray;
+
+ public FusionDictionary(final PtNodeArray rootNodeArray, final DictionaryOptions options) {
+ mRootNodeArray = rootNodeArray;
+ mOptions = options;
+ }
+
+ public void addOptionAttribute(final String key, final String value) {
+ mOptions.mAttributes.put(key, value);
+ }
+
+ /**
+ * Helper method to convert a String to an int array.
+ */
+ static int[] getCodePoints(final String word) {
+ // TODO: this is a copy-paste of the old contents of StringUtils.toCodePointArray,
+ // which is not visible from the makedict package. Factor this code.
+ final int length = word.length();
+ if (length <= 0) return new int[] {};
+ final char[] characters = word.toCharArray();
+ final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ int codePoint = Character.codePointAt(characters, 0);
+ int dsti = 0;
+ for (int srci = Character.charCount(codePoint);
+ srci < length; srci += Character.charCount(codePoint), ++dsti) {
+ codePoints[dsti] = codePoint;
+ codePoint = Character.codePointAt(characters, srci);
+ }
+ codePoints[dsti] = codePoint;
+ return codePoints;
+ }
+
+ /**
+ * Helper method to add a word as a string.
+ *
+ * This method adds a word to the dictionary with the given frequency. Optional
+ * lists of bigrams and shortcuts can be passed here. For each word inside,
+ * they will be added to the dictionary as necessary.
+ *
+ * @param word the word to add.
+ * @param probabilityInfo probability information of the word.
+ * @param shortcutTargets a list of shortcut targets for this word, or null.
+ * @param isNotAWord true if this should not be considered a word (e.g. shortcut only)
+ */
+ public void add(final String word, final ProbabilityInfo probabilityInfo,
+ final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) {
+ add(getCodePoints(word), probabilityInfo, shortcutTargets, isNotAWord,
+ false /* isBlacklistEntry */);
+ }
+
+ /**
+ * Helper method to add a blacklist entry as a string.
+ *
+ * @param word the word to add as a blacklist entry.
+ * @param shortcutTargets a list of shortcut targets for this word, or null.
+ * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so)
+ */
+ public void addBlacklistEntry(final String word,
+ final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) {
+ add(getCodePoints(word), new ProbabilityInfo(0), shortcutTargets, isNotAWord,
+ true /* isBlacklistEntry */);
+ }
+
+ /**
+ * Sanity check for a PtNode array.
+ *
+ * This method checks that all PtNodes in a node array are ordered as expected.
+ * If they are, nothing happens. If they aren't, an exception is thrown.
+ */
+ private void checkStack(PtNodeArray ptNodeArray) {
+ ArrayList<PtNode> stack = ptNodeArray.mData;
+ int lastValue = -1;
+ for (int i = 0; i < stack.size(); ++i) {
+ int currentValue = stack.get(i).mChars[0];
+ if (currentValue <= lastValue)
+ throw new RuntimeException("Invalid stack");
+ else
+ lastValue = currentValue;
+ }
+ }
+
+ /**
+ * Helper method to add a new bigram to the dictionary.
+ *
+ * @param word0 the previous word of the context
+ * @param word1 the next word of the context
+ * @param probabilityInfo the bigram probability info
+ */
+ public void setBigram(final String word0, final String word1,
+ final ProbabilityInfo probabilityInfo) {
+ PtNode ptNode0 = findWordInTree(mRootNodeArray, word0);
+ if (ptNode0 != null) {
+ final PtNode ptNode1 = findWordInTree(mRootNodeArray, word1);
+ if (ptNode1 == null) {
+ add(getCodePoints(word1), new ProbabilityInfo(0), null, false /* isNotAWord */,
+ false /* isBlacklistEntry */);
+ // The PtNode for the first word may have moved by the above insertion,
+ // if word1 and word2 share a common stem that happens not to have been
+ // a cutting point until now. In this case, we need to refresh ptNode.
+ ptNode0 = findWordInTree(mRootNodeArray, word0);
+ }
+ ptNode0.addBigram(word1, probabilityInfo);
+ } else {
+ throw new RuntimeException("First word of bigram not found " + word0);
+ }
+ }
+
+ /**
+ * Add a word to this dictionary.
+ *
+ * The shortcuts, if any, have to be in the dictionary already. If they aren't,
+ * an exception is thrown.
+ *
+ * @param word the word, as an int array.
+ * @param probabilityInfo the probability information of the word.
+ * @param shortcutTargets an optional list of shortcut targets for this word (null if none).
+ * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so)
+ * @param isBlacklistEntry true if this is a blacklisted word, false otherwise
+ */
+ private void add(final int[] word, final ProbabilityInfo probabilityInfo,
+ final ArrayList<WeightedString> shortcutTargets,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
+ assert(probabilityInfo.mProbability <= FormatSpec.MAX_TERMINAL_FREQUENCY);
+ if (word.length >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
+ MakedictLog.w("Ignoring a word that is too long: word.length = " + word.length);
+ return;
+ }
+
+ PtNodeArray currentNodeArray = mRootNodeArray;
+ int charIndex = 0;
+
+ PtNode currentPtNode = null;
+ int differentCharIndex = 0; // Set by the loop to the index of the char that differs
+ int nodeIndex = findIndexOfChar(mRootNodeArray, word[charIndex]);
+ while (CHARACTER_NOT_FOUND_INDEX != nodeIndex) {
+ currentPtNode = currentNodeArray.mData.get(nodeIndex);
+ differentCharIndex = compareCharArrays(currentPtNode.mChars, word, charIndex);
+ if (ARRAYS_ARE_EQUAL != differentCharIndex
+ && differentCharIndex < currentPtNode.mChars.length) break;
+ if (null == currentPtNode.mChildren) break;
+ charIndex += currentPtNode.mChars.length;
+ if (charIndex >= word.length) break;
+ currentNodeArray = currentPtNode.mChildren;
+ nodeIndex = findIndexOfChar(currentNodeArray, word[charIndex]);
+ }
+
+ if (CHARACTER_NOT_FOUND_INDEX == nodeIndex) {
+ // No node at this point to accept the word. Create one.
+ final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]);
+ final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length),
+ shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord,
+ isBlacklistEntry);
+ currentNodeArray.mData.add(insertionIndex, newPtNode);
+ if (DBG) checkStack(currentNodeArray);
+ } else {
+ // There is a word with a common prefix.
+ if (differentCharIndex == currentPtNode.mChars.length) {
+ if (charIndex + differentCharIndex >= word.length) {
+ // The new word is a prefix of an existing word, but the node on which it
+ // should end already exists as is. Since the old PtNode was not a terminal,
+ // make it one by filling in its frequency and other attributes
+ currentPtNode.update(probabilityInfo, shortcutTargets, null, isNotAWord,
+ isBlacklistEntry);
+ } else {
+ // The new word matches the full old word and extends past it.
+ // We only have to create a new node and add it to the end of this.
+ final PtNode newNode = new PtNode(
+ Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length),
+ shortcutTargets, null /* bigrams */, probabilityInfo,
+ isNotAWord, isBlacklistEntry);
+ currentPtNode.mChildren = new PtNodeArray();
+ currentPtNode.mChildren.mData.add(newNode);
+ }
+ } else {
+ if (0 == differentCharIndex) {
+ // Exact same word. Update the frequency if higher. This will also add the
+ // new shortcuts to the existing shortcut list if it already exists.
+ currentPtNode.update(probabilityInfo, shortcutTargets, null,
+ currentPtNode.mIsNotAWord && isNotAWord,
+ currentPtNode.mIsBlacklistEntry || isBlacklistEntry);
+ } else {
+ // Partial prefix match only. We have to replace the current node with a node
+ // containing the current prefix and create two new ones for the tails.
+ PtNodeArray newChildren = new PtNodeArray();
+ final PtNode newOldWord = new PtNode(
+ Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex,
+ currentPtNode.mChars.length), currentPtNode.mShortcutTargets,
+ currentPtNode.mBigrams, currentPtNode.mProbabilityInfo,
+ currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry,
+ currentPtNode.mChildren);
+ newChildren.mData.add(newOldWord);
+
+ final PtNode newParent;
+ if (charIndex + differentCharIndex >= word.length) {
+ newParent = new PtNode(
+ Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
+ shortcutTargets, null /* bigrams */, probabilityInfo,
+ isNotAWord, isBlacklistEntry, newChildren);
+ } else {
+ newParent = new PtNode(
+ Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
+ null /* shortcutTargets */, null /* bigrams */,
+ null /* probabilityInfo */, false /* isNotAWord */,
+ false /* isBlacklistEntry */, newChildren);
+ final PtNode newWord = new PtNode(Arrays.copyOfRange(word,
+ charIndex + differentCharIndex, word.length),
+ shortcutTargets, null /* bigrams */, probabilityInfo,
+ isNotAWord, isBlacklistEntry);
+ final int addIndex = word[charIndex + differentCharIndex]
+ > currentPtNode.mChars[differentCharIndex] ? 1 : 0;
+ newChildren.mData.add(addIndex, newWord);
+ }
+ currentNodeArray.mData.set(nodeIndex, newParent);
+ }
+ if (DBG) checkStack(currentNodeArray);
+ }
+ }
+ }
+
+ private static int ARRAYS_ARE_EQUAL = 0;
+
+ /**
+ * Custom comparison of two int arrays taken to contain character codes.
+ *
+ * This method compares the two arrays passed as an argument in a lexicographic way,
+ * with an offset in the dst string.
+ * This method does NOT test for the first character. It is taken to be equal.
+ * I repeat: this method starts the comparison at 1 <> dstOffset + 1.
+ * The index where the strings differ is returned. ARRAYS_ARE_EQUAL = 0 is returned if the
+ * strings are equal. This works BECAUSE we don't look at the first character.
+ *
+ * @param src the left-hand side string of the comparison.
+ * @param dst the right-hand side string of the comparison.
+ * @param dstOffset the offset in the right-hand side string.
+ * @return the index at which the strings differ, or ARRAYS_ARE_EQUAL = 0 if they don't.
+ */
+ private static int compareCharArrays(final int[] src, final int[] dst, int dstOffset) {
+ // We do NOT test the first char, because we come from a method that already
+ // tested it.
+ for (int i = 1; i < src.length; ++i) {
+ if (dstOffset + i >= dst.length) return i;
+ if (src[i] != dst[dstOffset + i]) return i;
+ }
+ if (dst.length > src.length) return src.length;
+ return ARRAYS_ARE_EQUAL;
+ }
+
+ /**
+ * Helper class that compares and sorts two PtNodes according to their
+ * first element only. I repeat: ONLY the first element is considered, the rest
+ * is ignored.
+ * This comparator imposes orderings that are inconsistent with equals.
+ */
+ static private final class PtNodeComparator implements java.util.Comparator<PtNode> {
+ @Override
+ public int compare(PtNode p1, PtNode p2) {
+ if (p1.mChars[0] == p2.mChars[0]) return 0;
+ return p1.mChars[0] < p2.mChars[0] ? -1 : 1;
+ }
+ }
+ final static private PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator();
+
+ /**
+ * Finds the insertion index of a character within a node array.
+ */
+ private static int findInsertionIndex(final PtNodeArray nodeArray, int character) {
+ final ArrayList<PtNode> data = nodeArray.mData;
+ final PtNode reference = new PtNode(new int[] { character },
+ null /* shortcutTargets */, null /* bigrams */, null /* probabilityInfo */,
+ false /* isNotAWord */, false /* isBlacklistEntry */);
+ int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR);
+ return result >= 0 ? result : -result - 1;
+ }
+
+ /**
+ * Find the index of a char in a node array, if it exists.
+ *
+ * @param nodeArray the node array to search in.
+ * @param character the character to search for.
+ * @return the position of the character if it's there, or CHARACTER_NOT_FOUND_INDEX = -1 else.
+ */
+ private static int findIndexOfChar(final PtNodeArray nodeArray, int character) {
+ final int insertionIndex = findInsertionIndex(nodeArray, character);
+ if (nodeArray.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND_INDEX;
+ return character == nodeArray.mData.get(insertionIndex).mChars[0] ? insertionIndex
+ : CHARACTER_NOT_FOUND_INDEX;
+ }
+
+ /**
+ * Helper method to find a word in a given branch.
+ */
+ public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) {
+ int index = 0;
+ final StringBuilder checker = DBG ? new StringBuilder() : null;
+ final int[] codePoints = getCodePoints(string);
+
+ PtNode currentPtNode;
+ do {
+ int indexOfGroup = findIndexOfChar(nodeArray, codePoints[index]);
+ if (CHARACTER_NOT_FOUND_INDEX == indexOfGroup) return null;
+ currentPtNode = nodeArray.mData.get(indexOfGroup);
+
+ if (codePoints.length - index < currentPtNode.mChars.length) return null;
+ int newIndex = index;
+ while (newIndex < codePoints.length && newIndex - index < currentPtNode.mChars.length) {
+ if (currentPtNode.mChars[newIndex - index] != codePoints[newIndex]) return null;
+ newIndex++;
+ }
+ index = newIndex;
+
+ if (DBG) {
+ checker.append(new String(currentPtNode.mChars, 0, currentPtNode.mChars.length));
+ }
+ if (index < codePoints.length) {
+ nodeArray = currentPtNode.mChildren;
+ }
+ } while (null != nodeArray && index < codePoints.length);
+
+ if (index < codePoints.length) return null;
+ if (!currentPtNode.isTerminal()) return null;
+ if (DBG && !string.equals(checker.toString())) return null;
+ return currentPtNode;
+ }
+
+ /**
+ * Helper method to find out whether a word is in the dict or not.
+ */
+ public boolean hasWord(final String s) {
+ if (null == s || "".equals(s)) {
+ throw new RuntimeException("Can't search for a null or empty string");
+ }
+ return null != findWordInTree(mRootNodeArray, s);
+ }
+
+ /**
+ * Recursively count the number of PtNodes in a given branch of the trie.
+ *
+ * @param nodeArray the parent node.
+ * @return the number of PtNodes in all the branch under this node.
+ */
+ public static int countPtNodes(final PtNodeArray nodeArray) {
+ final int nodeSize = nodeArray.mData.size();
+ int size = nodeSize;
+ for (int i = nodeSize - 1; i >= 0; --i) {
+ PtNode ptNode = nodeArray.mData.get(i);
+ if (null != ptNode.mChildren)
+ size += countPtNodes(ptNode.mChildren);
+ }
+ return size;
+ }
+
+ /**
+ * Iterator to walk through a dictionary.
+ *
+ * This is purely for convenience.
+ */
+ public static final class DictionaryIterator implements Iterator<WordProperty> {
+ private static final class Position {
+ public Iterator<PtNode> pos;
+ public int length;
+ public Position(ArrayList<PtNode> ptNodes) {
+ pos = ptNodes.iterator();
+ length = 0;
+ }
+ }
+ final StringBuilder mCurrentString;
+ final LinkedList<Position> mPositions;
+
+ public DictionaryIterator(ArrayList<PtNode> ptRoot) {
+ mCurrentString = new StringBuilder();
+ mPositions = new LinkedList<>();
+ final Position rootPos = new Position(ptRoot);
+ mPositions.add(rootPos);
+ }
+
+ @Override
+ public boolean hasNext() {
+ for (Position p : mPositions) {
+ if (p.pos.hasNext()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public WordProperty next() {
+ Position currentPos = mPositions.getLast();
+ mCurrentString.setLength(currentPos.length);
+
+ do {
+ if (currentPos.pos.hasNext()) {
+ final PtNode currentPtNode = currentPos.pos.next();
+ currentPos.length = mCurrentString.length();
+ for (int i : currentPtNode.mChars) {
+ mCurrentString.append(Character.toChars(i));
+ }
+ if (null != currentPtNode.mChildren) {
+ currentPos = new Position(currentPtNode.mChildren.mData);
+ currentPos.length = mCurrentString.length();
+ mPositions.addLast(currentPos);
+ }
+ if (currentPtNode.isTerminal()) {
+ return new WordProperty(mCurrentString.toString(),
+ currentPtNode.mProbabilityInfo,
+ currentPtNode.mShortcutTargets, currentPtNode.mBigrams,
+ currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry);
+ }
+ } else {
+ mPositions.removeLast();
+ currentPos = mPositions.getLast();
+ mCurrentString.setLength(mPositions.getLast().length);
+ }
+ } while (true);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Unsupported yet");
+ }
+
+ }
+
+ /**
+ * Method to return an iterator.
+ *
+ * This method enables Java's enhanced for loop. With this you can have a FusionDictionary x
+ * and say : for (Word w : x) {}
+ */
+ @Override
+ public Iterator<WordProperty> iterator() {
+ return new DictionaryIterator(mRootNodeArray.mData);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java b/tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java
new file mode 100644
index 000000000..7eccff2b4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+/**
+ * Wrapper to redirect log events to the right output medium.
+ */
+public class MakedictLog {
+ public static final boolean DBG = true;
+
+ private static void print(String message) {
+ System.out.println(message);
+ }
+
+ public static void d(String message) {
+ print(message);
+ }
+
+ public static void i(String message) {
+ print(message);
+ }
+
+ public static void w(String message) {
+ print(message);
+ }
+
+ public static void e(String message) {
+ print(message);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java b/tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
new file mode 100644
index 000000000..70e24cc98
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+/**
+ * A not-yet-resolved attribute.
+ *
+ * An attribute is either a bigram or a shortcut.
+ * All instances of this class are always immutable.
+ */
+public final class PendingAttribute {
+ public final int mFrequency;
+ public final int mAddress;
+ public PendingAttribute(final int frequency, final int address) {
+ mFrequency = frequency;
+ mAddress = address;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java b/tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
new file mode 100644
index 000000000..862e8c101
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import java.util.ArrayList;
+
+/**
+ * Raw PtNode info straight out of a file. This will contain numbers for addresses.
+ */
+public final class PtNodeInfo {
+ public final int mOriginalAddress;
+ public final int mEndAddress;
+ public final int mFlags;
+ public final int[] mCharacters;
+ public final ProbabilityInfo mProbabilityInfo;
+ public final int mChildrenAddress;
+ public final ArrayList<WeightedString> mShortcutTargets;
+ public final ArrayList<PendingAttribute> mBigrams;
+
+ public PtNodeInfo(final int originalAddress, final int endAddress, final int flags,
+ final int[] characters, final ProbabilityInfo probabilityInfo,
+ final int childrenAddress, final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<PendingAttribute> bigrams) {
+ mOriginalAddress = originalAddress;
+ mEndAddress = endAddress;
+ mFlags = flags;
+ mCharacters = characters;
+ mProbabilityInfo = probabilityInfo;
+ mChildrenAddress = childrenAddress;
+ mShortcutTargets = shortcutTargets;
+ mBigrams = bigrams;
+ }
+
+ public boolean isTerminal() {
+ return mProbabilityInfo != null;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java
deleted file mode 100644
index aeb8552bd..000000000
--- a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Random;
-
-/**
- * Unit tests for SparseTable.
- */
-@LargeTest
-public class SparseTableTests extends AndroidTestCase {
- private static final String TAG = SparseTableTests.class.getSimpleName();
-
- private final Random mRandom;
- private final ArrayList<Integer> mRandomIndex;
-
- private static final int DEFAULT_SIZE = 10000;
- private static final int BLOCK_SIZE = 8;
-
- public SparseTableTests() {
- this(System.currentTimeMillis(), DEFAULT_SIZE);
- }
-
- public SparseTableTests(final long seed, final int tableSize) {
- super();
- Log.d(TAG, "Seed for test is " + seed + ", size is " + tableSize);
- mRandom = new Random(seed);
- mRandomIndex = new ArrayList<Integer>(tableSize);
- for (int i = 0; i < tableSize; ++i) {
- mRandomIndex.add(SparseTable.NOT_EXIST);
- }
- }
-
- public void testSet() {
- final SparseTable table = new SparseTable(16, BLOCK_SIZE, 1);
- table.set(0, 3, 6);
- table.set(0, 8, 16);
- for (int i = 0; i < 16; ++i) {
- if (i == 3 || i == 8) {
- assertEquals(i * 2, table.get(0, i));
- } else {
- assertEquals(SparseTable.NOT_EXIST, table.get(0, i));
- }
- }
- }
-
- private void generateRandomIndex(final int size, final int prop) {
- for (int i = 0; i < size; ++i) {
- if (mRandom.nextInt(100) < prop) {
- mRandomIndex.set(i, mRandom.nextInt());
- } else {
- mRandomIndex.set(i, SparseTable.NOT_EXIST);
- }
- }
- }
-
- private void runTestRandomSet() {
- final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, 1);
- int elementCount = 0;
- for (int i = 0; i < DEFAULT_SIZE; ++i) {
- if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) {
- table.set(0, i, mRandomIndex.get(i));
- elementCount++;
- }
- }
-
- Log.d(TAG, "table size = " + table.getLookupTableSize() + " + "
- + table.getContentTableSize());
- Log.d(TAG, "the table has " + elementCount + " elements");
- for (int i = 0; i < DEFAULT_SIZE; ++i) {
- assertEquals(table.get(0, i), (int)mRandomIndex.get(i));
- }
-
- // flush and reload
- OutputStream lookupOutStream = null;
- OutputStream contentOutStream = null;
- try {
- final File lookupIndexFile = File.createTempFile("testRandomSet", ".small");
- final File contentFile = File.createTempFile("testRandomSet", ".big");
- lookupOutStream = new FileOutputStream(lookupIndexFile);
- contentOutStream = new FileOutputStream(contentFile);
- table.write(lookupOutStream, new OutputStream[] { contentOutStream });
- lookupOutStream.flush();
- contentOutStream.flush();
- final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile,
- new File[] { contentFile }, BLOCK_SIZE);
- for (int i = 0; i < DEFAULT_SIZE; ++i) {
- assertEquals(table.get(0, i), newTable.get(0, i));
- }
- } catch (IOException e) {
- Log.d(TAG, "IOException while flushing and realoding", e);
- } finally {
- if (lookupOutStream != null) {
- try {
- lookupOutStream.close();
- } catch (IOException e) {
- Log.d(TAG, "IOException while closing the stream", e);
- }
- }
- if (contentOutStream != null) {
- try {
- contentOutStream.close();
- } catch (IOException e) {
- Log.d(TAG, "IOException while closing contentStream.", e);
- }
- }
- }
- }
-
- public void testRandomSet() {
- for (int i = 0; i <= 100; i += 10) {
- generateRandomIndex(DEFAULT_SIZE, i);
- runTestRandomSet();
- }
- }
-
- public void testMultipleContents() {
- final int numOfContents = 5;
- generateRandomIndex(DEFAULT_SIZE, 20);
- final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, numOfContents);
- for (int i = 0; i < mRandomIndex.size(); ++i) {
- if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) {
- for (int j = 0; j < numOfContents; ++j) {
- table.set(j, i, mRandomIndex.get(i));
- }
- }
- }
-
- OutputStream lookupOutStream = null;
- OutputStream[] contentsOutStream = new OutputStream[numOfContents];
- try {
- final File lookupIndexFile = File.createTempFile("testMultipleContents", "small");
- lookupOutStream = new FileOutputStream(lookupIndexFile);
- final File[] contentFiles = new File[numOfContents];
- for (int i = 0; i < numOfContents; ++i) {
- contentFiles[i] = File.createTempFile("testMultipleContents", "big" + i);
- contentsOutStream[i] = new FileOutputStream(contentFiles[i]);
- }
- table.write(lookupOutStream, contentsOutStream);
- lookupOutStream.flush();
- for (int i = 0; i < numOfContents; ++i) {
- contentsOutStream[i].flush();
- }
- final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile, contentFiles,
- BLOCK_SIZE);
- for (int i = 0; i < numOfContents; ++i) {
- for (int j = 0; j < DEFAULT_SIZE; ++j) {
- assertEquals(table.get(i, j), newTable.get(i, j));
- }
- }
- } catch (IOException e) {
- Log.d(TAG, "IOException while flushing and reloading", e);
- } finally {
- if (lookupOutStream != null) {
- try {
- lookupOutStream.close();
- } catch (IOException e) {
- Log.d(TAG, "IOException while closing the stream", e);
- }
- }
- for (int i = 0; i < numOfContents; ++i) {
- if (contentsOutStream[i] != null) {
- try {
- contentsOutStream[i].close();
- } catch (IOException e) {
- Log.d(TAG, "IOException while closing the stream.", e);
- }
- }
- }
- }
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
new file mode 100644
index 000000000..65b84d5f7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * An implementation of DictDecoder for version 2 binary dictionary.
+ */
+// TODO: Separate logics that are used only for testing.
+@UsedForTesting
+public class Ver2DictDecoder extends AbstractDictDecoder {
+ /**
+ * A utility class for reading a PtNode.
+ */
+ protected static class PtNodeReader {
+ private static ProbabilityInfo readProbabilityInfo(final DictBuffer dictBuffer) {
+ // Ver2 dicts don't contain historical information.
+ return new ProbabilityInfo(dictBuffer.readUnsignedByte());
+ }
+
+ protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) {
+ return dictBuffer.readUnsignedByte();
+ }
+
+ protected static int readChildrenAddress(final DictBuffer dictBuffer,
+ final int ptNodeFlags) {
+ switch (ptNodeFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+ return dictBuffer.readUnsignedByte();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+ return dictBuffer.readUnsignedShort();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+ return dictBuffer.readUnsignedInt24();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+ default:
+ return FormatSpec.NO_CHILDREN_ADDRESS;
+ }
+ }
+
+ // Reads shortcuts and returns the read length.
+ protected static int readShortcut(final DictBuffer dictBuffer,
+ final ArrayList<WeightedString> shortcutTargets) {
+ final int pointerBefore = dictBuffer.position();
+ dictBuffer.readUnsignedShort(); // skip the size
+ while (true) {
+ final int targetFlags = dictBuffer.readUnsignedByte();
+ final String word = CharEncoding.readString(dictBuffer);
+ shortcutTargets.add(new WeightedString(word,
+ targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+ if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return dictBuffer.position() - pointerBefore;
+ }
+
+ protected static int readBigramAddresses(final DictBuffer dictBuffer,
+ final ArrayList<PendingAttribute> bigrams, final int baseAddress) {
+ int readLength = 0;
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+ final int bigramFlags = dictBuffer.readUnsignedByte();
+ ++readLength;
+ final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE)
+ ? 1 : -1;
+ int bigramAddress = baseAddress + readLength;
+ switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
+ bigramAddress += sign * dictBuffer.readUnsignedByte();
+ readLength += 1;
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
+ bigramAddress += sign * dictBuffer.readUnsignedShort();
+ readLength += 2;
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
+ bigramAddress += sign * dictBuffer.readUnsignedInt24();
+ readLength += 3;
+ break;
+ default:
+ throw new RuntimeException("Has bigrams with no address");
+ }
+ bigrams.add(new PendingAttribute(
+ bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
+ bigramAddress));
+ if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return readLength;
+ }
+ }
+
+ protected final File mDictionaryBinaryFile;
+ protected final long mOffset;
+ protected final long mLength;
+ // TODO: Remove mBufferFactory and mDictBuffer from this class members because they are now
+ // used only for testing.
+ private final DictionaryBufferFactory mBufferFactory;
+ protected DictBuffer mDictBuffer;
+
+ @UsedForTesting
+ /* package */ Ver2DictDecoder(final File file, final long offset, final long length,
+ final int factoryFlag) {
+ mDictionaryBinaryFile = file;
+ mOffset = offset;
+ mLength = length;
+ mDictBuffer = null;
+ if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
+ mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+ } else if ((factoryFlag & MASK_DICTBUFFER) == USE_BYTEARRAY) {
+ mBufferFactory = new DictionaryBufferFromByteArrayFactory();
+ } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) {
+ mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory();
+ } else {
+ mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+ }
+ }
+
+ /* package */ Ver2DictDecoder(final File file, final long offset, final long length,
+ final DictionaryBufferFactory factory) {
+ mDictionaryBinaryFile = file;
+ mOffset = offset;
+ mLength = length;
+ mBufferFactory = factory;
+ }
+
+ @Override
+ public void openDictBuffer() throws FileNotFoundException, IOException {
+ mDictBuffer = mBufferFactory.getDictionaryBuffer(mDictionaryBinaryFile);
+ }
+
+ @Override
+ public boolean isDictBufferOpen() {
+ return mDictBuffer != null;
+ }
+
+ /* package */ DictBuffer getDictBuffer() {
+ return mDictBuffer;
+ }
+
+ @UsedForTesting
+ /* package */ DictBuffer openAndGetDictBuffer() throws FileNotFoundException, IOException {
+ openDictBuffer();
+ return getDictBuffer();
+ }
+
+ @Override
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
+ // dictType is not being used in dicttool. Passing an empty string.
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(
+ mDictionaryBinaryFile.getAbsolutePath(), mOffset, mLength,
+ true /* useFullEditDistance */, null /* locale */, "" /* dictType */,
+ false /* isUpdatable */);
+ final DictionaryHeader header = binaryDictionary.getHeader();
+ binaryDictionary.close();
+ if (header == null) {
+ throw new IOException("Cannot read the dictionary header.");
+ }
+ if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) {
+ throw new UnsupportedFormatException("File header has a wrong version : "
+ + header.mFormatOptions.mVersion);
+ }
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
+ }
+ // Advance buffer reading position to the head of dictionary body.
+ setPosition(header.mBodyOffset);
+ return header;
+ }
+
+ // TODO: Make this buffer multi thread safe.
+ private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
+ @Override
+ public PtNodeInfo readPtNode(final int ptNodePos) {
+ int addressPointer = ptNodePos;
+ final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+ addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
+ final int characters[];
+ if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
+ int index = 0;
+ int character = CharEncoding.readChar(mDictBuffer);
+ addressPointer += CharEncoding.getCharSize(character);
+ while (FormatSpec.INVALID_CHARACTER != character) {
+ // FusionDictionary is making sure that the length of the word is smaller than
+ // MAX_WORD_LENGTH.
+ // So we'll never write past the end of mCharacterBuffer.
+ mCharacterBuffer[index++] = character;
+ character = CharEncoding.readChar(mDictBuffer);
+ addressPointer += CharEncoding.getCharSize(character);
+ }
+ characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
+ } else {
+ final int character = CharEncoding.readChar(mDictBuffer);
+ addressPointer += CharEncoding.getCharSize(character);
+ characters = new int[] { character };
+ }
+ final ProbabilityInfo probabilityInfo;
+ if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
+ probabilityInfo = PtNodeReader.readProbabilityInfo(mDictBuffer);
+ addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ } else {
+ probabilityInfo = null;
+ }
+ int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags);
+ if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ childrenAddress += addressPointer;
+ }
+ addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags);
+ final ArrayList<WeightedString> shortcutTargets;
+ if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
+ // readShortcut will add shortcuts to shortcutTargets.
+ shortcutTargets = new ArrayList<>();
+ addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets);
+ } else {
+ shortcutTargets = null;
+ }
+
+ final ArrayList<PendingAttribute> bigrams;
+ if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
+ bigrams = new ArrayList<>();
+ addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams,
+ addressPointer);
+ if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+ throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
+ + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
+ }
+ } else {
+ bigrams = null;
+ }
+ return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, probabilityInfo,
+ childrenAddress, shortcutTargets, bigrams);
+ }
+
+ @Override
+ public FusionDictionary readDictionaryBinary(final boolean deleteDictIfBroken)
+ throws FileNotFoundException, IOException, UnsupportedFormatException {
+ // dictType is not being used in dicttool. Passing an empty string.
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(
+ mDictionaryBinaryFile.getAbsolutePath(), 0 /* offset */,
+ mDictionaryBinaryFile.length() /* length */, true /* useFullEditDistance */,
+ null /* locale */, "" /* dictType */, false /* isUpdatable */);
+ final DictionaryHeader header = readHeader();
+ final FusionDictionary fusionDict =
+ new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions);
+ int token = 0;
+ final ArrayList<WordProperty> wordProperties = new ArrayList<>();
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ binaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ if (wordProperty == null) {
+ binaryDictionary.close();
+ if (deleteDictIfBroken) {
+ mDictionaryBinaryFile.delete();
+ }
+ return null;
+ }
+ wordProperties.add(wordProperty);
+ token = result.mNextToken;
+ } while (token != 0);
+
+ // Insert unigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mIsBlacklistEntry) {
+ fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets,
+ wordProperty.mIsNotAWord);
+ } else {
+ fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo,
+ wordProperty.mShortcutTargets, wordProperty.mIsNotAWord);
+ }
+ }
+ // Insert bigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mBigrams == null) {
+ continue;
+ }
+ final String word0 = wordProperty.mWord;
+ for (final WeightedString bigram : wordProperty.mBigrams) {
+ fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo);
+ }
+ }
+ binaryDictionary.close();
+ return fusionDict;
+ }
+
+ @Override
+ public void setPosition(int newPos) {
+ mDictBuffer.position(newPos);
+ }
+
+ @Override
+ public int getPosition() {
+ return mDictBuffer.position();
+ }
+
+ @Override
+ public int readPtNodeCount() {
+ return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
index 9611599b9..3882c2c55 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
@@ -32,10 +32,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
/**
- * Unit tests for Ver3DictDecoder
+ * Unit tests for Ver2DictDecoder
*/
-public class Ver3DictDecoderTests extends AndroidTestCase {
- private static final String TAG = Ver3DictDecoderTests.class.getSimpleName();
+public class Ver2DictDecoderTests extends AndroidTestCase {
+ private static final String TAG = Ver2DictDecoderTests.class.getSimpleName();
private final byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
@@ -58,7 +58,6 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
}
}
- @SuppressWarnings("null")
public void runTestOpenBuffer(final String testName, final DictionaryBufferFactory factory) {
File testFile = null;
try {
@@ -68,7 +67,8 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
}
assertNotNull(testFile);
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+ final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(),
+ factory);
try {
dictDecoder.openDictBuffer();
} catch (Exception e) {
@@ -101,7 +101,6 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
new DictionaryBufferFromWritableByteBufferFactory());
}
- @SuppressWarnings("null")
public void runTestGetBuffer(final String testName, final DictionaryBufferFactory factory) {
File testFile = null;
try {
@@ -110,7 +109,8 @@ public class Ver3DictDecoderTests extends AndroidTestCase {
Log.e(TAG, "IOException while the creating temporary file", e);
}
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+ final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(),
+ factory);
// the default return value of getBuffer() must be null.
assertNull("the default return value of getBuffer() is not null",
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
new file mode 100644
index 000000000..a286190cb
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An implementation of DictEncoder for version 2 binary dictionary.
+ */
+@UsedForTesting
+public class Ver2DictEncoder implements DictEncoder {
+
+ private final File mDictFile;
+ private OutputStream mOutStream;
+ private byte[] mBuffer;
+ private int mPosition;
+
+ @UsedForTesting
+ public Ver2DictEncoder(final File dictFile) {
+ mDictFile = dictFile;
+ mOutStream = null;
+ mBuffer = null;
+ }
+
+ // This constructor is used only by BinaryDictOffdeviceUtilsTests.
+ // If you want to use this in the production code, you should consider keeping consistency of
+ // the interface of Ver3DictDecoder by using factory.
+ @UsedForTesting
+ public Ver2DictEncoder(final OutputStream outStream) {
+ mDictFile = null;
+ mOutStream = outStream;
+ }
+
+ private void openStream() throws FileNotFoundException {
+ mOutStream = new FileOutputStream(mDictFile);
+ }
+
+ private void close() throws IOException {
+ if (mOutStream != null) {
+ mOutStream.close();
+ mOutStream = null;
+ }
+ }
+
+ @Override
+ public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+ throws IOException, UnsupportedFormatException {
+ if (formatOptions.mVersion > FormatSpec.VERSION2) {
+ throw new UnsupportedFormatException(
+ "The given format options has wrong version number : "
+ + formatOptions.mVersion);
+ }
+
+ if (mOutStream == null) {
+ openStream();
+ }
+ BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions);
+
+ // Addresses are limited to 3 bytes, but since addresses can be relative to each node
+ // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding
+ // the order of the PtNode arrays becomes a quite complicated problem, because though the
+ // dictionary itself does not have a size limit, each node array must still be within 16MB
+ // of all its children and parents. As long as this is ensured, the dictionary file may
+ // grow to any size.
+
+ // Leave the choice of the optimal node order to the flattenTree function.
+ MakedictLog.i("Flattening the tree...");
+ ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
+
+ MakedictLog.i("Computing addresses...");
+ BinaryDictEncoderUtils.computeAddresses(dict, flatNodes);
+ MakedictLog.i("Checking PtNode array...");
+ if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
+
+ // Create a buffer that matches the final dictionary size.
+ final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
+ final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
+ mBuffer = new byte[bufferSize];
+
+ MakedictLog.i("Writing file...");
+
+ for (PtNodeArray nodeArray : flatNodes) {
+ BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray);
+ }
+ if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes);
+ mOutStream.write(mBuffer, 0, mPosition);
+
+ MakedictLog.i("Done");
+ close();
+ }
+
+ @Override
+ public void setPosition(final int position) {
+ if (mBuffer == null || position < 0 || position >= mBuffer.length) return;
+ mPosition = position;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPosition;
+ }
+
+ @Override
+ public void writePtNodeCount(final int ptNodeCount) {
+ final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
+ if (countSize != 1 && countSize != 2) {
+ throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
+ }
+ final int encodedPtNodeCount = (countSize == 2) ?
+ (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount;
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, encodedPtNodeCount,
+ countSize);
+ }
+
+ private void writePtNodeFlags(final PtNode ptNode) {
+ final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition,
+ BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos),
+ FormatSpec.PTNODE_FLAGS_SIZE);
+ }
+
+ private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) {
+ mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition);
+ if (hasSeveralChars) {
+ mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
+ }
+ }
+
+ private void writeFrequency(final int frequency) {
+ if (frequency >= 0) {
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, frequency,
+ FormatSpec.PTNODE_FREQUENCY_SIZE);
+ }
+ }
+
+ private void writeChildrenPosition(final PtNode ptNode) {
+ final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
+ mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
+ childrenPos);
+ }
+
+ /**
+ * Write a shortcut attributes list to mBuffer.
+ *
+ * @param shortcuts the shortcut attributes list.
+ */
+ private void writeShortcuts(final ArrayList<WeightedString> shortcuts) {
+ if (null == shortcuts || shortcuts.isEmpty()) return;
+
+ final int indexOfShortcutByteSize = mPosition;
+ mPosition += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+ final Iterator<WeightedString> shortcutIterator = shortcuts.iterator();
+ while (shortcutIterator.hasNext()) {
+ final WeightedString target = shortcutIterator.next();
+ final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
+ shortcutIterator.hasNext(),
+ target.getProbability());
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, shortcutFlags,
+ FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+ final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord);
+ mPosition += shortcutShift;
+ }
+ final int shortcutByteSize = mPosition - indexOfShortcutByteSize;
+ if (shortcutByteSize > FormatSpec.MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE) {
+ throw new RuntimeException("Shortcut list too large");
+ }
+ BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, indexOfShortcutByteSize, shortcutByteSize,
+ FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
+ }
+
+ /**
+ * Write a bigram attributes list to mBuffer.
+ *
+ * @param bigrams the bigram attributes list.
+ * @param dict the dictionary the node array is a part of (for relative offsets).
+ */
+ private void writeBigrams(final ArrayList<WeightedString> bigrams,
+ final FusionDictionary dict) {
+ if (bigrams == null) return;
+
+ final Iterator<WeightedString> bigramIterator = bigrams.iterator();
+ while (bigramIterator.hasNext()) {
+ final WeightedString bigram = bigramIterator.next();
+ final PtNode target =
+ FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
+ final int addressOfBigram = target.mCachedAddressAfterUpdate;
+ final int unigramFrequencyForThisWord = target.getProbability();
+ final int offset = addressOfBigram
+ - (mPosition + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+ final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(),
+ offset, bigram.getProbability(), unigramFrequencyForThisWord, bigram.mWord);
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, bigramFlags,
+ FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+ mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
+ Math.abs(offset));
+ }
+ }
+
+ @Override
+ public void writeForwardLinkAddress(final int forwardLinkAddress) {
+ mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, forwardLinkAddress,
+ FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+ }
+
+ @Override
+ public void writePtNode(final PtNode ptNode, final FusionDictionary dict) {
+ writePtNodeFlags(ptNode);
+ writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
+ writeFrequency(ptNode.getProbability());
+ writeChildrenPosition(ptNode);
+ writeShortcuts(ptNode.mShortcutTargets);
+ writeBigrams(ptNode.mBigrams, dict);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
new file mode 100644
index 000000000..5e8417ed6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.utils.FileUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * An implementation of binary dictionary decoder for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictDecoder extends AbstractDictDecoder {
+ final File mDictDirectory;
+
+ @UsedForTesting
+ /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) {
+ this(dictDirectory, null /* factory */);
+ }
+
+ @UsedForTesting
+ /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) {
+ mDictDirectory = dictDirectory;
+
+ }
+
+ @Override
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
+ // dictType is not being used in dicttool. Passing an empty string.
+ final BinaryDictionary binaryDictionary= new BinaryDictionary(
+ mDictDirectory.getAbsolutePath(), 0 /* offset */, 0 /* length */,
+ true /* useFullEditDistance */, null /* locale */,
+ "" /* dictType */, true /* isUpdatable */);
+ final DictionaryHeader header = binaryDictionary.getHeader();
+ binaryDictionary.close();
+ if (header == null) {
+ throw new IOException("Cannot read the dictionary header.");
+ }
+ return header;
+ }
+
+ @Override
+ public FusionDictionary readDictionaryBinary(final boolean deleteDictIfBroken)
+ throws FileNotFoundException, IOException, UnsupportedFormatException {
+ // dictType is not being used in dicttool. Passing an empty string.
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(
+ mDictDirectory.getAbsolutePath(), 0 /* offset */, 0 /* length */,
+ true /* useFullEditDistance */, null /* locale */,
+ "" /* dictType */, true /* isUpdatable */);
+ final DictionaryHeader header = readHeader();
+ final FusionDictionary fusionDict =
+ new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions);
+ int token = 0;
+ final ArrayList<WordProperty> wordProperties = new ArrayList<>();
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ binaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ if (wordProperty == null) {
+ binaryDictionary.close();
+ if (deleteDictIfBroken) {
+ FileUtils.deleteRecursively(mDictDirectory);
+ }
+ return null;
+ }
+ wordProperties.add(wordProperty);
+ token = result.mNextToken;
+ } while (token != 0);
+
+ // Insert unigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mIsBlacklistEntry) {
+ fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets,
+ wordProperty.mIsNotAWord);
+ } else {
+ fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo,
+ wordProperty.mShortcutTargets, wordProperty.mIsNotAWord);
+ }
+ }
+ // Insert bigrams into the fusion dictionary.
+ for (final WordProperty wordProperty : wordProperties) {
+ if (wordProperty.mBigrams == null) {
+ continue;
+ }
+ final String word0 = wordProperty.mWord;
+ for (final WeightedString bigram : wordProperty.mBigrams) {
+ fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo);
+ }
+ }
+ binaryDictionary.close();
+ return fusionDict;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
new file mode 100644
index 000000000..76eaef431
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
+import com.android.inputmethod.latin.utils.LocaleUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * An implementation of DictEncoder for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictEncoder implements DictEncoder {
+ private final File mDictPlacedDir;
+
+ @UsedForTesting
+ public Ver4DictEncoder(final File dictPlacedDir) {
+ mDictPlacedDir = dictPlacedDir;
+ }
+
+ // TODO: This builds a FusionDictionary first and iterates it to add words to the binary
+ // dictionary. However, it is possible to just add words directly to the binary dictionary
+ // instead.
+ // In the long run, when we stop supporting version 2, FusionDictionary will become deprecated
+ // and we can remove it. Then we'll be able to just call BinaryDictionary directly.
+ @Override
+ public void writeDictionary(FusionDictionary dict, FormatOptions formatOptions)
+ throws IOException, UnsupportedFormatException {
+ if (formatOptions.mVersion != FormatSpec.VERSION4) {
+ throw new UnsupportedFormatException("File header has a wrong version number : "
+ + formatOptions.mVersion);
+ }
+ if (!mDictPlacedDir.isDirectory()) {
+ throw new UnsupportedFormatException("Given path is not a directory.");
+ }
+ if (!BinaryDictionaryUtils.createEmptyDictFile(mDictPlacedDir.getAbsolutePath(),
+ FormatSpec.VERSION4, LocaleUtils.constructLocaleFromString(
+ dict.mOptions.mAttributes.get(DictionaryHeader.DICTIONARY_LOCALE_KEY)),
+ dict.mOptions.mAttributes)) {
+ throw new IOException("Cannot create dictionary file : "
+ + mDictPlacedDir.getAbsolutePath());
+ }
+ final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(),
+ 0l, mDictPlacedDir.length(), true /* useFullEditDistance */,
+ LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get(
+ DictionaryHeader.DICTIONARY_LOCALE_KEY)),
+ Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */,
+ true /* isUpdatable */);
+ if (!binaryDict.isValidDictionary()) {
+ // Somehow createEmptyDictFile returned true, but the file was not created correctly
+ throw new IOException("Cannot create dictionary file");
+ }
+ for (final WordProperty wordProperty : dict) {
+ // TODO: switch to addMultipleDictionaryEntries when they support shortcuts
+ if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) {
+ if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(),
+ null /* shortcutTarget */, 0 /* shortcutProbability */,
+ wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
+ wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
+ MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord);
+ }
+ } else {
+ for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+ if (!binaryDict.addUnigramEntry(wordProperty.mWord,
+ wordProperty.getProbability(),
+ shortcutTarget.mWord, shortcutTarget.getProbability(),
+ wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
+ wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
+ MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord
+ + ", shortcutTarget: " + shortcutTarget.mWord);
+ return;
+ }
+ }
+ }
+ if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
+ if (!binaryDict.flushWithGC()) {
+ MakedictLog.e("Cannot flush dict with GC.");
+ return;
+ }
+ }
+ }
+ for (final WordProperty word0Property : dict) {
+ if (null == word0Property.mBigrams) continue;
+ for (final WeightedString word1 : word0Property.mBigrams) {
+ final PrevWordsInfo prevWordsInfo =
+ new PrevWordsInfo(new PrevWordsInfo.WordInfo(word0Property.mWord));
+ if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord,
+ word1.getProbability(), 0 /* timestamp */)) {
+ MakedictLog.e("Cannot add n-gram entry for "
+ + prevWordsInfo + " -> " + word1.mWord);
+ return;
+ }
+ if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
+ if (!binaryDict.flushWithGC()) {
+ MakedictLog.e("Cannot flush dict with GC.");
+ return;
+ }
+ }
+ }
+ }
+ if (!binaryDict.flushWithGC()) {
+ MakedictLog.e("Cannot flush dict with GC.");
+ return;
+ }
+ binaryDict.close();
+ }
+
+ @Override
+ public void setPosition(int position) {
+ }
+
+ @Override
+ public int getPosition() {
+ return 0;
+ }
+
+ @Override
+ public void writePtNodeCount(int ptNodeCount) {
+ }
+
+ @Override
+ public void writeForwardLinkAddress(int forwardLinkAddress) {
+ }
+
+ @Override
+ public void writePtNode(PtNode ptNode, FusionDictionary dict) {
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
new file mode 100644
index 000000000..565fadb2a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.DictionaryFacilitator;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+/**
+ * Unit tests for contextual dictionary
+ */
+@LargeTest
+public class ContextualDictionaryTests extends AndroidTestCase {
+ private static final String TAG = ContextualDictionaryTests.class.getSimpleName();
+
+ private static final Locale LOCALE_EN_US = new Locale("en", "US");
+
+ private DictionaryFacilitator getDictionaryFacilitator() {
+ final ArrayList<String> dictTypes = new ArrayList<>();
+ dictTypes.add(Dictionary.TYPE_CONTEXTUAL);
+ final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator();
+ dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes,
+ new HashMap<String, File>(), new HashMap<String, Map<String, String>>());
+ return dictionaryFacilitator;
+ }
+
+ public void testAddPhrase() {
+ final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator();
+ final String[] phrase = new String[] {"a", "b", "c", "d"};
+ final int probability = 100;
+ final int bigramProbabilityForWords = 150;
+ final int bigramProbabilityForPhrases = 200;
+ dictionaryFacilitator.addPhraseToContextualDictionary(
+ phrase, probability, bigramProbabilityForWords, bigramProbabilityForPhrases);
+ final ExpandableBinaryDictionary contextualDictionary =
+ dictionaryFacilitator.getSubDictForTesting(Dictionary.TYPE_CONTEXTUAL);
+ contextualDictionary.waitAllTasksForTests();
+ // Word
+ assertTrue(contextualDictionary.isInDictionary("a"));
+ assertTrue(contextualDictionary.isInDictionary("b"));
+ assertTrue(contextualDictionary.isInDictionary("c"));
+ assertTrue(contextualDictionary.isInDictionary("d"));
+ // Phrase
+ assertTrue(contextualDictionary.isInDictionary("a b c d"));
+ assertTrue(contextualDictionary.isInDictionary("b c d"));
+ assertTrue(contextualDictionary.isInDictionary("c d"));
+ assertFalse(contextualDictionary.isInDictionary("a b c"));
+ assertFalse(contextualDictionary.isInDictionary("abcd"));
+ // TODO: Add tests for probability.
+ // TODO: Add tests for n-grams.
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
new file mode 100644
index 000000000..0f2f9814b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.DictionaryFacilitator;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback;
+import com.android.inputmethod.latin.makedict.CodePointUtils;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+/**
+ * Unit tests for personalization dictionary
+ */
+@LargeTest
+public class PersonalizationDictionaryTests extends AndroidTestCase {
+ private static final String TAG = PersonalizationDictionaryTests.class.getSimpleName();
+
+ private static final Locale LOCALE_EN_US = new Locale("en", "US");
+ private static final String DUMMY_PACKAGE_NAME = "test.package.name";
+ private static final long TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS = 120;
+
+ private DictionaryFacilitator getDictionaryFacilitator() {
+ final ArrayList<String> dictTypes = new ArrayList<>();
+ dictTypes.add(Dictionary.TYPE_MAIN);
+ dictTypes.add(Dictionary.TYPE_PERSONALIZATION);
+ final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator();
+ dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes,
+ new HashMap<String, File>(), new HashMap<String, Map<String, String>>());
+ return dictionaryFacilitator;
+ }
+
+ public void testAddManyTokens() {
+ final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator();
+ dictionaryFacilitator.clearPersonalizationDictionary();
+ final int dataChunkCount = 20;
+ final int wordCountInOneChunk = 2000;
+ final Random random = new Random(System.currentTimeMillis());
+ final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER;
+
+ final SpacingAndPunctuations spacingAndPunctuations =
+ new SpacingAndPunctuations(getContext().getResources());
+
+ final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
+ System.currentTimeMillis());
+
+ for (int i = 0; i < dataChunkCount; i++) {
+ final ArrayList<String> tokens = new ArrayList<>();
+ for (int j = 0; j < wordCountInOneChunk; j++) {
+ tokens.add(CodePointUtils.generateWord(random, codePointSet));
+ }
+ final PersonalizationDataChunk personalizationDataChunk = new PersonalizationDataChunk(
+ true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME);
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final AddMultipleDictionaryEntriesCallback callback =
+ new AddMultipleDictionaryEntriesCallback() {
+ @Override
+ public void onFinished() {
+ countDownLatch.countDown();
+ }
+ };
+ dictionaryFacilitator.addEntriesToPersonalizationDictionary(personalizationDataChunk,
+ spacingAndPunctuations, callback);
+ try {
+ countDownLatch.await(TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS,
+ TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e);
+ }
+ }
+ dictionaryFacilitator.flushPersonalizationDictionary();
+ try {
+ dictionaryFacilitator.waitForLoadingDictionariesForTesting(
+ TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e);
+ }
+ final String dictName = ExpandableBinaryDictionary.getDictName(
+ PersonalizationDictionary.NAME, LOCALE_EN_US, null /* dictFile */);
+ final File dictFile = ExpandableBinaryDictionary.getDictFile(
+ getContext(), dictName, null /* dictFile */);
+
+ final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, 0 /* size */,
+ true /* useFullEditDistance */, LOCALE_EN_US, Dictionary.TYPE_PERSONALIZATION,
+ true /* isUpdatable */);
+ assertTrue(binaryDictionary.isValidDictionary());
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 7c1decb71..f87f3b494 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -16,20 +16,23 @@
package com.android.inputmethod.latin.personalization;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
-import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
+import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.FileUtils;
import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Random;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -38,25 +41,57 @@ import java.util.concurrent.TimeUnit;
@LargeTest
public class UserHistoryDictionaryTests extends AndroidTestCase {
private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
- private SharedPreferences mPrefs;
private static final String[] CHARACTERS = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
};
- private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000;
- private static final int WAIT_TERMINATING_IN_MILLISECONDS = 100;
+ private int mCurrentTime = 0;
@Override
- public void setUp() {
- mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ protected void setUp() throws Exception {
+ super.setUp();
+ resetCurrentTimeForTestMode();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ stopTestModeInNativeCode();
+ super.tearDown();
+ }
+
+ private void resetCurrentTimeForTestMode() {
+ mCurrentTime = 0;
+ setCurrentTimeForTestMode(mCurrentTime);
+ }
+
+ private void forcePassingShortTime() {
+ // 3 days.
+ final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3);
+ mCurrentTime += timeToElapse;
+ setCurrentTimeForTestMode(mCurrentTime);
+ }
+
+ private void forcePassingLongTime() {
+ // 365 days.
+ final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(365);
+ mCurrentTime += timeToElapse;
+ setCurrentTimeForTestMode(mCurrentTime);
+ }
+
+ private static int setCurrentTimeForTestMode(final int currentTime) {
+ return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime);
+ }
+
+ private static int stopTestModeInNativeCode() {
+ return BinaryDictionaryUtils.setCurrentTimeForTest(-1);
}
/**
* Generates a random word.
*/
- private String generateWord(final int value) {
+ private static String generateWord(final int value) {
final int lengthOfChars = CHARACTERS.length;
StringBuilder builder = new StringBuilder();
long lvalue = Math.abs((long)value);
@@ -67,19 +102,21 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
return builder.toString();
}
- private List<String> generateWords(final int number, final Random random) {
- final Set<String> wordSet = CollectionUtils.newHashSet();
+ private static List<String> generateWords(final int number, final Random random) {
+ final HashSet<String> wordSet = new HashSet<>();
while (wordSet.size() < number) {
wordSet.add(generateWord(random.nextInt()));
}
- return new ArrayList<String>(wordSet);
+ return new ArrayList<>(wordSet);
}
- private void addToDict(final UserHistoryDictionary dict, final List<String> words) {
- String prevWord = null;
+ private static void addToDict(final UserHistoryDictionary dict, final List<String> words) {
+ PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
for (String word : words) {
- dict.addToDictionary(prevWord, word, true);
- prevWord = word;
+ UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true,
+ (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
+ DistracterFilter.EMPTY_DISTRACTER_FILTER);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word));
}
}
@@ -87,22 +124,18 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
* @param checkContents if true, checks whether written words are actually in the dictionary
* or not.
*/
- private void addAndWriteRandomWords(final String testFilenameSuffix, final int numberOfWords,
+ private void addAndWriteRandomWords(final Locale locale, final int numberOfWords,
final Random random, final boolean checkContents) {
final List<String> words = generateWords(numberOfWords, random);
- final UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffix /* locale */, mPrefs);
+ final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
+ mContext, locale);
// Add random words to the user history dictionary.
addToDict(dict, words);
if (checkContents) {
- try {
- Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- }
+ dict.waitAllTasksForTests();
for (int i = 0; i < numberOfWords; ++i) {
final String word = words.get(i);
- assertTrue(dict.isInDictionaryForTests(word));
+ assertTrue(dict.isInDictionary(word));
}
}
// write to file.
@@ -111,57 +144,48 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
/**
* Clear all entries in the user history dictionary.
- * @param testFilenameSuffix file name suffix used for testing.
+ * @param locale dummy locale for testing.
*/
- private void clearHistory(final String testFilenameSuffix) {
- final UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffix /* locale */, mPrefs);
- dict.clearAndFlushDictionary();
+ private void clearHistory(final Locale locale) {
+ final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
+ mContext, locale);
+ dict.waitAllTasksForTests();
+ dict.clear();
dict.close();
+ dict.waitAllTasksForTests();
}
/**
* Shut down executer and wait until all operations of user history are done.
- * @param testFilenameSuffix file name suffix used for testing.
+ * @param locale dummy locale for testing.
*/
- private void waitForWriting(final String testFilenameSuffix) {
- try {
- final UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffix, mPrefs);
- dict.shutdownExecutorForTests();
- while (!dict.isTerminatedForTests()) {
- Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
- }
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: ", e);
- }
+ private void waitForWriting(final Locale locale) {
+ final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
+ mContext, locale);
+ dict.waitAllTasksForTests();
}
public void testRandomWords() {
Log.d(TAG, "This test can be used for profiling.");
Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
- final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
- final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
- + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis());
+ final String dictName = ExpandableBinaryDictionary.getDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final File dictFile = ExpandableBinaryDictionary.getDictFile(
+ mContext, dictName, null /* dictFile */);
final int numberOfWords = 1000;
final Random random = new Random(123456);
try {
- clearHistory(testFilenameSuffix);
- addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
+ clearHistory(dummyLocale);
+ addAndWriteRandomWords(dummyLocale, numberOfWords, random,
true /* checksContents */);
} finally {
Log.d(TAG, "waiting for writing ...");
- waitForWriting(testFilenameSuffix);
- final File dictFile = new File(getContext().getFilesDir(), fileName);
- if (dictFile != null) {
- assertTrue(dictFile.exists());
- assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
- dictFile.delete();
- }
+ waitForWriting(dummyLocale);
+ assertTrue("check exisiting of " + dictFile, dictFile.exists());
+ FileUtils.deleteRecursively(dictFile);
}
}
@@ -171,17 +195,18 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
final int numberOfWordsInsertedForEachLanguageSwitch = 100;
final File dictFiles[] = new File[numberOfLanguages];
- final String testFilenameSuffixes[] = new String[numberOfLanguages];
+ final Locale dummyLocales[] = new Locale[numberOfLanguages];
try {
final Random random = new Random(123456);
// Create filename suffixes for this test.
for (int i = 0; i < numberOfLanguages; i++) {
- testFilenameSuffixes[i] = "testSwitchingLanguages" + i;
- final String fileName = UserHistoryDictionary.NAME + "." +
- testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
- dictFiles[i] = new File(getContext().getFilesDir(), fileName);
- clearHistory(testFilenameSuffixes[i]);
+ dummyLocales[i] = new Locale("test_switching_languages" + i);
+ final String dictName = ExpandableBinaryDictionary.getDictName(
+ UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */);
+ dictFiles[i] = ExpandableBinaryDictionary.getDictFile(
+ mContext, dictName, null /* dictFile */);
+ clearHistory(dummyLocales[i]);
}
final long start = System.currentTimeMillis();
@@ -189,7 +214,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
for (int i = 0; i < numberOfLanguageSwitching; i++) {
final int index = i % numberOfLanguages;
// Switch languages to testFilenameSuffixes[index].
- addAndWriteRandomWords(testFilenameSuffixes[index],
+ addAndWriteRandomWords(dummyLocales[index],
numberOfWordsInsertedForEachLanguageSwitch, random,
false /* checksContents */);
}
@@ -200,40 +225,63 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
} finally {
Log.d(TAG, "waiting for writing ...");
for (int i = 0; i < numberOfLanguages; i++) {
- waitForWriting(testFilenameSuffixes[i]);
+ waitForWriting(dummyLocales[i]);
}
- for (final File file : dictFiles) {
- if (file != null) {
- assertTrue(file.exists());
- assertTrue(file.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
- file.delete();
- }
+ for (final File dictFile : dictFiles) {
+ assertTrue("check exisiting of " + dictFile, dictFile.exists());
+ FileUtils.deleteRecursively(dictFile);
}
}
}
public void testAddManyWords() {
- final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
- final int numberOfWords =
- ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
- 10000 : 1000;
+ final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis());
+ final String dictName = ExpandableBinaryDictionary.getDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final File dictFile = ExpandableBinaryDictionary.getDictFile(
+ mContext, dictName, null /* dictFile */);
+ final int numberOfWords = 10000;
final Random random = new Random(123456);
- clearHistory(testFilenameSuffix);
+ clearHistory(dummyLocale);
try {
- addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
- true /* checksContents */);
+ addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */);
} finally {
Log.d(TAG, "waiting for writing ...");
- waitForWriting(testFilenameSuffix);
- final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
- + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
- final File dictFile = new File(getContext().getFilesDir(), fileName);
- if (dictFile != null) {
- assertTrue(dictFile.exists());
- assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
- dictFile.delete();
- }
+ waitForWriting(dummyLocale);
+ assertTrue("check exisiting of " + dictFile, dictFile.exists());
+ FileUtils.deleteRecursively(dictFile);
}
}
+ public void testDecaying() {
+ final Locale dummyLocale = new Locale("test_decaying" + System.currentTimeMillis());
+ final int numberOfWords = 5000;
+ final Random random = new Random(123456);
+ resetCurrentTimeForTestMode();
+ clearHistory(dummyLocale);
+ final List<String> words = generateWords(numberOfWords, random);
+ final UserHistoryDictionary dict =
+ PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale);
+ dict.waitAllTasksForTests();
+ PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
+ for (final String word : words) {
+ UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime,
+ DistracterFilter.EMPTY_DISTRACTER_FILTER);
+ prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word));
+ dict.waitAllTasksForTests();
+ assertTrue(dict.isInDictionary(word));
+ }
+ forcePassingShortTime();
+ dict.runGCIfRequired();
+ dict.waitAllTasksForTests();
+ for (final String word : words) {
+ assertTrue(dict.isInDictionary(word));
+ }
+ forcePassingLongTime();
+ dict.runGCIfRequired();
+ dict.waitAllTasksForTests();
+ for (final String word : words) {
+ assertFalse(dict.isInDictionary(word));
+ }
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
new file mode 100644
index 000000000..2cc22fae4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.settings;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.utils.RunInLocale;
+
+import junit.framework.AssertionFailedError;
+
+import java.util.Locale;
+
+@SmallTest
+public class SpacingAndPunctuationsTests extends AndroidTestCase {
+ private static final int ARMENIAN_FULL_STOP = '\u0589';
+ private static final int ARMENIAN_COMMA = '\u055D';
+
+ private int mScreenMetrics;
+
+ private boolean isPhone() {
+ return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
+ || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+ }
+
+ private boolean isTablet() {
+ return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET
+ || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET;
+ }
+
+ private SpacingAndPunctuations ENGLISH;
+ private SpacingAndPunctuations FRENCH;
+ private SpacingAndPunctuations GERMAN;
+ private SpacingAndPunctuations ARMENIAN;
+ private SpacingAndPunctuations THAI;
+ private SpacingAndPunctuations KHMER;
+ private SpacingAndPunctuations LAO;
+ private SpacingAndPunctuations ARABIC;
+ private SpacingAndPunctuations PERSIAN;
+ private SpacingAndPunctuations HEBREW;
+
+ private SpacingAndPunctuations UNITED_STATES;
+ private SpacingAndPunctuations UNITED_KINGDOM;
+ private SpacingAndPunctuations CANADA_FRENCH;
+ private SpacingAndPunctuations SWISS_GERMAN;
+ private SpacingAndPunctuations INDIA_ENGLISH;
+ private SpacingAndPunctuations ARMENIA_ARMENIAN;
+ private SpacingAndPunctuations CAMBODIA_KHMER;
+ private SpacingAndPunctuations LAOS_LAO;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics);
+
+ // Language only
+ ENGLISH = getSpacingAndPunctuations(Locale.ENGLISH);
+ FRENCH = getSpacingAndPunctuations(Locale.FRENCH);
+ GERMAN = getSpacingAndPunctuations(Locale.GERMAN);
+ THAI = getSpacingAndPunctuations(new Locale("th"));
+ ARMENIAN = getSpacingAndPunctuations(new Locale("hy"));
+ KHMER = getSpacingAndPunctuations(new Locale("km"));
+ LAO = getSpacingAndPunctuations(new Locale("lo"));
+ ARABIC = getSpacingAndPunctuations(new Locale("ar"));
+ PERSIAN = getSpacingAndPunctuations(new Locale("fa"));
+ HEBREW = getSpacingAndPunctuations(new Locale("iw"));
+
+ // Language and Country
+ UNITED_STATES = getSpacingAndPunctuations(Locale.US);
+ UNITED_KINGDOM = getSpacingAndPunctuations(Locale.UK);
+ CANADA_FRENCH = getSpacingAndPunctuations(Locale.CANADA_FRENCH);
+ SWISS_GERMAN = getSpacingAndPunctuations(new Locale("de", "CH"));
+ INDIA_ENGLISH = getSpacingAndPunctuations(new Locale("en", "IN"));
+ ARMENIA_ARMENIAN = getSpacingAndPunctuations(new Locale("hy", "AM"));
+ CAMBODIA_KHMER = getSpacingAndPunctuations(new Locale("km", "KH"));
+ LAOS_LAO = getSpacingAndPunctuations(new Locale("lo", "LA"));
+ }
+
+ private SpacingAndPunctuations getSpacingAndPunctuations(final Locale locale) {
+ final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ return job.runInLocale(getContext().getResources(), locale);
+ }
+
+ private static void testingStandardWordSeparator(final SpacingAndPunctuations sp) {
+ assertTrue("Tab", sp.isWordSeparator('\t'));
+ assertTrue("Newline", sp.isWordSeparator('\n'));
+ assertTrue("Space", sp.isWordSeparator(' '));
+ assertTrue("Exclamation", sp.isWordSeparator('!'));
+ assertTrue("Quotation", sp.isWordSeparator('"'));
+ assertFalse("Number", sp.isWordSeparator('#'));
+ assertFalse("Dollar", sp.isWordSeparator('$'));
+ assertFalse("Percent", sp.isWordSeparator('%'));
+ assertTrue("Ampersand", sp.isWordSeparator('&'));
+ assertFalse("Apostrophe", sp.isWordSeparator('\''));
+ assertTrue("L Paren", sp.isWordSeparator('('));
+ assertTrue("R Paren", sp.isWordSeparator(')'));
+ assertTrue("Asterisk", sp.isWordSeparator('*'));
+ assertTrue("Plus", sp.isWordSeparator('+'));
+ assertTrue("Comma", sp.isWordSeparator(','));
+ assertFalse("Minus", sp.isWordSeparator('-'));
+ assertTrue("Period", sp.isWordSeparator('.'));
+ assertTrue("Slash", sp.isWordSeparator('/'));
+ assertTrue("Colon", sp.isWordSeparator(':'));
+ assertTrue("Semicolon", sp.isWordSeparator(';'));
+ assertTrue("L Angle", sp.isWordSeparator('<'));
+ assertTrue("Equal", sp.isWordSeparator('='));
+ assertTrue("R Angle", sp.isWordSeparator('>'));
+ assertTrue("Question", sp.isWordSeparator('?'));
+ assertFalse("Atmark", sp.isWordSeparator('@'));
+ assertTrue("L S Bracket", sp.isWordSeparator('['));
+ assertFalse("B Slash", sp.isWordSeparator('\\'));
+ assertTrue("R S Bracket", sp.isWordSeparator(']'));
+ assertFalse("Circumflex", sp.isWordSeparator('^'));
+ assertTrue("Underscore", sp.isWordSeparator('_'));
+ assertFalse("Grave", sp.isWordSeparator('`'));
+ assertTrue("L C Brace", sp.isWordSeparator('{'));
+ assertTrue("V Line", sp.isWordSeparator('|'));
+ assertTrue("R C Brace", sp.isWordSeparator('}'));
+ assertFalse("Tilde", sp.isWordSeparator('~'));
+ }
+
+ public void testWordSeparator() {
+ testingStandardWordSeparator(ENGLISH);
+ testingStandardWordSeparator(FRENCH);
+ testingStandardWordSeparator(CANADA_FRENCH);
+ testingStandardWordSeparator(ARMENIA_ARMENIAN);
+ assertTrue(ARMENIA_ARMENIAN.isWordSeparator(ARMENIAN_FULL_STOP));
+ assertTrue(ARMENIA_ARMENIAN.isWordSeparator(ARMENIAN_COMMA));
+ // TODO: We should fix these.
+ testingStandardWordSeparator(ARMENIAN);
+ assertFalse(ARMENIAN.isWordSeparator(ARMENIAN_FULL_STOP));
+ assertFalse(ARMENIAN.isWordSeparator(ARMENIAN_COMMA));
+ }
+
+ private static void testingStandardWordConnector(final SpacingAndPunctuations sp) {
+ assertFalse("Tab", sp.isWordConnector('\t'));
+ assertFalse("Newline", sp.isWordConnector('\n'));
+ assertFalse("Space", sp.isWordConnector(' '));
+ assertFalse("Exclamation", sp.isWordConnector('!'));
+ assertFalse("Quotation", sp.isWordConnector('"'));
+ assertFalse("Number", sp.isWordConnector('#'));
+ assertFalse("Dollar", sp.isWordConnector('$'));
+ assertFalse("Percent", sp.isWordConnector('%'));
+ assertFalse("Ampersand", sp.isWordConnector('&'));
+ assertTrue("Apostrophe", sp.isWordConnector('\''));
+ assertFalse("L Paren", sp.isWordConnector('('));
+ assertFalse("R Paren", sp.isWordConnector(')'));
+ assertFalse("Asterisk", sp.isWordConnector('*'));
+ assertFalse("Plus", sp.isWordConnector('+'));
+ assertFalse("Comma", sp.isWordConnector(','));
+ assertTrue("Minus", sp.isWordConnector('-'));
+ assertFalse("Period", sp.isWordConnector('.'));
+ assertFalse("Slash", sp.isWordConnector('/'));
+ assertFalse("Colon", sp.isWordConnector(':'));
+ assertFalse("Semicolon", sp.isWordConnector(';'));
+ assertFalse("L Angle", sp.isWordConnector('<'));
+ assertFalse("Equal", sp.isWordConnector('='));
+ assertFalse("R Angle", sp.isWordConnector('>'));
+ assertFalse("Question", sp.isWordConnector('?'));
+ assertFalse("Atmark", sp.isWordConnector('@'));
+ assertFalse("L S Bracket", sp.isWordConnector('['));
+ assertFalse("B Slash", sp.isWordConnector('\\'));
+ assertFalse("R S Bracket", sp.isWordConnector(']'));
+ assertFalse("Circumflex", sp.isWordConnector('^'));
+ assertFalse("Underscore", sp.isWordConnector('_'));
+ assertFalse("Grave", sp.isWordConnector('`'));
+ assertFalse("L C Brace", sp.isWordConnector('{'));
+ assertFalse("V Line", sp.isWordConnector('|'));
+ assertFalse("R C Brace", sp.isWordConnector('}'));
+ assertFalse("Tilde", sp.isWordConnector('~'));
+
+ }
+
+ public void testWordConnector() {
+ testingStandardWordConnector(ENGLISH);
+ testingStandardWordConnector(FRENCH);
+ testingStandardWordConnector(CANADA_FRENCH);
+ testingStandardWordConnector(ARMENIA_ARMENIAN);
+ }
+
+ private static void testingCommonPrecededBySpace(final SpacingAndPunctuations sp) {
+ assertFalse("Tab", sp.isUsuallyPrecededBySpace('\t'));
+ assertFalse("Newline", sp.isUsuallyPrecededBySpace('\n'));
+ assertFalse("Space", sp.isUsuallyPrecededBySpace(' '));
+ //assertFalse("Exclamation", sp.isUsuallyPrecededBySpace('!'));
+ assertFalse("Quotation", sp.isUsuallyPrecededBySpace('"'));
+ assertFalse("Number", sp.isUsuallyPrecededBySpace('#'));
+ assertFalse("Dollar", sp.isUsuallyPrecededBySpace('$'));
+ assertFalse("Percent", sp.isUsuallyPrecededBySpace('%'));
+ assertTrue("Ampersand", sp.isUsuallyPrecededBySpace('&'));
+ assertFalse("Apostrophe", sp.isUsuallyPrecededBySpace('\''));
+ assertTrue("L Paren", sp.isUsuallyPrecededBySpace('('));
+ assertFalse("R Paren", sp.isUsuallyPrecededBySpace(')'));
+ assertFalse("Asterisk", sp.isUsuallyPrecededBySpace('*'));
+ assertFalse("Plus", sp.isUsuallyPrecededBySpace('+'));
+ assertFalse("Comma", sp.isUsuallyPrecededBySpace(','));
+ assertFalse("Minus", sp.isUsuallyPrecededBySpace('-'));
+ assertFalse("Period", sp.isUsuallyPrecededBySpace('.'));
+ assertFalse("Slash", sp.isUsuallyPrecededBySpace('/'));
+ //assertFalse("Colon", sp.isUsuallyPrecededBySpace(':'));
+ //assertFalse("Semicolon", sp.isUsuallyPrecededBySpace(';'));
+ assertFalse("L Angle", sp.isUsuallyPrecededBySpace('<'));
+ assertFalse("Equal", sp.isUsuallyPrecededBySpace('='));
+ assertFalse("R Angle", sp.isUsuallyPrecededBySpace('>'));
+ //assertFalse("Question", sp.isUsuallyPrecededBySpace('?'));
+ assertFalse("Atmark", sp.isUsuallyPrecededBySpace('@'));
+ assertTrue("L S Bracket", sp.isUsuallyPrecededBySpace('['));
+ assertFalse("B Slash", sp.isUsuallyPrecededBySpace('\\'));
+ assertFalse("R S Bracket", sp.isUsuallyPrecededBySpace(']'));
+ assertFalse("Circumflex", sp.isUsuallyPrecededBySpace('^'));
+ assertFalse("Underscore", sp.isUsuallyPrecededBySpace('_'));
+ assertFalse("Grave", sp.isUsuallyPrecededBySpace('`'));
+ assertTrue("L C Brace", sp.isUsuallyPrecededBySpace('{'));
+ assertFalse("V Line", sp.isUsuallyPrecededBySpace('|'));
+ assertFalse("R C Brace", sp.isUsuallyPrecededBySpace('}'));
+ assertFalse("Tilde", sp.isUsuallyPrecededBySpace('~'));
+ }
+
+ private static void testingStandardPrecededBySpace(final SpacingAndPunctuations sp) {
+ testingCommonPrecededBySpace(sp);
+ assertFalse("Exclamation", sp.isUsuallyPrecededBySpace('!'));
+ assertFalse("Colon", sp.isUsuallyPrecededBySpace(':'));
+ assertFalse("Semicolon", sp.isUsuallyPrecededBySpace(';'));
+ assertFalse("Question", sp.isUsuallyPrecededBySpace('?'));
+ }
+
+ public void testIsUsuallyPrecededBySpace() {
+ testingStandardPrecededBySpace(ENGLISH);
+ testingCommonPrecededBySpace(FRENCH);
+ assertTrue("Exclamation", FRENCH.isUsuallyPrecededBySpace('!'));
+ assertTrue("Colon", FRENCH.isUsuallyPrecededBySpace(':'));
+ assertTrue("Semicolon", FRENCH.isUsuallyPrecededBySpace(';'));
+ assertTrue("Question", FRENCH.isUsuallyPrecededBySpace('?'));
+ testingCommonPrecededBySpace(CANADA_FRENCH);
+ assertFalse("Exclamation", CANADA_FRENCH.isUsuallyPrecededBySpace('!'));
+ assertTrue("Colon", CANADA_FRENCH.isUsuallyPrecededBySpace(':'));
+ assertFalse("Semicolon", CANADA_FRENCH.isUsuallyPrecededBySpace(';'));
+ assertFalse("Question", CANADA_FRENCH.isUsuallyPrecededBySpace('?'));
+ testingStandardPrecededBySpace(ARMENIA_ARMENIAN);
+ }
+
+ private static void testingStandardFollowedBySpace(final SpacingAndPunctuations sp) {
+ assertFalse("Tab", sp.isUsuallyFollowedBySpace('\t'));
+ assertFalse("Newline", sp.isUsuallyFollowedBySpace('\n'));
+ assertFalse("Space", sp.isUsuallyFollowedBySpace(' '));
+ assertTrue("Exclamation", sp.isUsuallyFollowedBySpace('!'));
+ assertFalse("Quotation", sp.isUsuallyFollowedBySpace('"'));
+ assertFalse("Number", sp.isUsuallyFollowedBySpace('#'));
+ assertFalse("Dollar", sp.isUsuallyFollowedBySpace('$'));
+ assertFalse("Percent", sp.isUsuallyFollowedBySpace('%'));
+ assertTrue("Ampersand", sp.isUsuallyFollowedBySpace('&'));
+ assertFalse("Apostrophe", sp.isUsuallyFollowedBySpace('\''));
+ assertFalse("L Paren", sp.isUsuallyFollowedBySpace('('));
+ assertTrue("R Paren", sp.isUsuallyFollowedBySpace(')'));
+ assertFalse("Asterisk", sp.isUsuallyFollowedBySpace('*'));
+ assertFalse("Plus", sp.isUsuallyFollowedBySpace('+'));
+ assertTrue("Comma", sp.isUsuallyFollowedBySpace(','));
+ assertFalse("Minus", sp.isUsuallyFollowedBySpace('-'));
+ assertTrue("Period", sp.isUsuallyFollowedBySpace('.'));
+ assertFalse("Slash", sp.isUsuallyFollowedBySpace('/'));
+ assertTrue("Colon", sp.isUsuallyFollowedBySpace(':'));
+ assertTrue("Semicolon", sp.isUsuallyFollowedBySpace(';'));
+ assertFalse("L Angle", sp.isUsuallyFollowedBySpace('<'));
+ assertFalse("Equal", sp.isUsuallyFollowedBySpace('='));
+ assertFalse("R Angle", sp.isUsuallyFollowedBySpace('>'));
+ assertTrue("Question", sp.isUsuallyFollowedBySpace('?'));
+ assertFalse("Atmark", sp.isUsuallyFollowedBySpace('@'));
+ assertFalse("L S Bracket", sp.isUsuallyFollowedBySpace('['));
+ assertFalse("B Slash", sp.isUsuallyFollowedBySpace('\\'));
+ assertTrue("R S Bracket", sp.isUsuallyFollowedBySpace(']'));
+ assertFalse("Circumflex", sp.isUsuallyFollowedBySpace('^'));
+ assertFalse("Underscore", sp.isUsuallyFollowedBySpace('_'));
+ assertFalse("Grave", sp.isUsuallyFollowedBySpace('`'));
+ assertFalse("L C Brace", sp.isUsuallyFollowedBySpace('{'));
+ assertFalse("V Line", sp.isUsuallyFollowedBySpace('|'));
+ assertTrue("R C Brace", sp.isUsuallyFollowedBySpace('}'));
+ assertFalse("Tilde", sp.isUsuallyFollowedBySpace('~'));
+ }
+
+ public void testIsUsuallyFollowedBySpace() {
+ testingStandardFollowedBySpace(ENGLISH);
+ testingStandardFollowedBySpace(FRENCH);
+ testingStandardFollowedBySpace(CANADA_FRENCH);
+ testingStandardFollowedBySpace(ARMENIA_ARMENIAN);
+ assertTrue(ARMENIA_ARMENIAN.isUsuallyFollowedBySpace(ARMENIAN_FULL_STOP));
+ assertTrue(ARMENIA_ARMENIAN.isUsuallyFollowedBySpace(ARMENIAN_COMMA));
+ }
+
+ private static void testingStandardSentenceSeparator(final SpacingAndPunctuations sp) {
+ assertFalse("Tab", sp.isUsuallyFollowedBySpace('\t'));
+ assertFalse("Newline", sp.isUsuallyFollowedBySpace('\n'));
+ assertFalse("Space", sp.isUsuallyFollowedBySpace(' '));
+ assertFalse("Exclamation", sp.isUsuallyFollowedBySpace('!'));
+ assertFalse("Quotation", sp.isUsuallyFollowedBySpace('"'));
+ assertFalse("Number", sp.isUsuallyFollowedBySpace('#'));
+ assertFalse("Dollar", sp.isUsuallyFollowedBySpace('$'));
+ assertFalse("Percent", sp.isUsuallyFollowedBySpace('%'));
+ assertFalse("Ampersand", sp.isUsuallyFollowedBySpace('&'));
+ assertFalse("Apostrophe", sp.isUsuallyFollowedBySpace('\''));
+ assertFalse("L Paren", sp.isUsuallyFollowedBySpace('('));
+ assertFalse("R Paren", sp.isUsuallyFollowedBySpace(')'));
+ assertFalse("Asterisk", sp.isUsuallyFollowedBySpace('*'));
+ assertFalse("Plus", sp.isUsuallyFollowedBySpace('+'));
+ assertFalse("Comma", sp.isUsuallyFollowedBySpace(','));
+ assertFalse("Minus", sp.isUsuallyFollowedBySpace('-'));
+ assertTrue("Period", sp.isUsuallyFollowedBySpace('.'));
+ assertFalse("Slash", sp.isUsuallyFollowedBySpace('/'));
+ assertFalse("Colon", sp.isUsuallyFollowedBySpace(':'));
+ assertFalse("Semicolon", sp.isUsuallyFollowedBySpace(';'));
+ assertFalse("L Angle", sp.isUsuallyFollowedBySpace('<'));
+ assertFalse("Equal", sp.isUsuallyFollowedBySpace('='));
+ assertFalse("R Angle", sp.isUsuallyFollowedBySpace('>'));
+ assertFalse("Question", sp.isUsuallyFollowedBySpace('?'));
+ assertFalse("Atmark", sp.isUsuallyFollowedBySpace('@'));
+ assertFalse("L S Bracket", sp.isUsuallyFollowedBySpace('['));
+ assertFalse("B Slash", sp.isUsuallyFollowedBySpace('\\'));
+ assertFalse("R S Bracket", sp.isUsuallyFollowedBySpace(']'));
+ assertFalse("Circumflex", sp.isUsuallyFollowedBySpace('^'));
+ assertFalse("Underscore", sp.isUsuallyFollowedBySpace('_'));
+ assertFalse("Grave", sp.isUsuallyFollowedBySpace('`'));
+ assertFalse("L C Brace", sp.isUsuallyFollowedBySpace('{'));
+ assertFalse("V Line", sp.isUsuallyFollowedBySpace('|'));
+ assertFalse("R C Brace", sp.isUsuallyFollowedBySpace('}'));
+ assertFalse("Tilde", sp.isUsuallyFollowedBySpace('~'));
+ }
+
+ public void isSentenceSeparator() {
+ testingStandardSentenceSeparator(ENGLISH);
+ try {
+ testingStandardSentenceSeparator(ARMENIA_ARMENIAN);
+ fail("Armenian Sentence Separator");
+ } catch (final AssertionFailedError e) {
+ assertEquals("Period", e.getMessage());
+ }
+ assertTrue(ARMENIA_ARMENIAN.isSentenceSeparator(ARMENIAN_FULL_STOP));
+ assertFalse(ARMENIA_ARMENIAN.isSentenceSeparator(ARMENIAN_COMMA));
+ }
+
+ public void testLanguageHasSpace() {
+ assertTrue(ENGLISH.mCurrentLanguageHasSpaces);
+ assertTrue(FRENCH.mCurrentLanguageHasSpaces);
+ assertTrue(GERMAN.mCurrentLanguageHasSpaces);
+ assertFalse(THAI.mCurrentLanguageHasSpaces);
+ assertFalse(CAMBODIA_KHMER.mCurrentLanguageHasSpaces);
+ assertFalse(LAOS_LAO.mCurrentLanguageHasSpaces);
+ // TODO: We should fix these.
+ assertTrue(KHMER.mCurrentLanguageHasSpaces);
+ assertTrue(LAO.mCurrentLanguageHasSpaces);
+ }
+
+ public void testUsesAmericanTypography() {
+ assertTrue(ENGLISH.mUsesAmericanTypography);
+ assertTrue(UNITED_STATES.mUsesAmericanTypography);
+ assertTrue(UNITED_KINGDOM.mUsesAmericanTypography);
+ assertTrue(INDIA_ENGLISH.mUsesAmericanTypography);
+ assertFalse(FRENCH.mUsesAmericanTypography);
+ assertFalse(GERMAN.mUsesAmericanTypography);
+ assertFalse(SWISS_GERMAN.mUsesAmericanTypography);
+ }
+
+ public void testUsesGermanRules() {
+ assertFalse(ENGLISH.mUsesGermanRules);
+ assertFalse(FRENCH.mUsesGermanRules);
+ assertTrue(GERMAN.mUsesGermanRules);
+ assertTrue(SWISS_GERMAN.mUsesGermanRules);
+ }
+
+ // Punctuations for phone.
+ private static final String[] PUNCTUATION_LABELS_PHONE = {
+ "!", "?", ",", ":", ";", "\"", "(", ")", "'", "-", "/", "@", "_"
+ };
+ private static final String[] PUNCTUATION_WORDS_PHONE_LTR = PUNCTUATION_LABELS_PHONE;
+ private static final String[] PUNCTUATION_WORDS_PHONE_HEBREW = {
+ "!", "?", ",", ":", ";", "\"", ")", "(", "'", "-", "/", "@", "_"
+ };
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+060C: "،" ARABIC COMMA
+ // U+061B: "؛" ARABIC SEMICOLON
+ private static final String[] PUNCTUATION_LABELS_PHONE_ARABIC_PERSIAN = {
+ "!", "\u061F", "\u060C", ":", "\u061B", "\"", "(", ")", "'", "-", "/", "@", "_"
+ };
+ private static final String[] PUNCTUATION_WORDS_PHONE_ARABIC_PERSIAN = {
+ "!", "\u061F", "\u060C", ":", "\u061B", "\"", ")", "(", "'", "-", "/", "@", "_"
+ };
+
+ // Punctuations for tablet.
+ private static final String[] PUNCTUATION_LABELS_TABLET = {
+ ":", ";", "\"", "(", ")", "'", "-", "/", "@", "_"
+ };
+ private static final String[] PUNCTUATION_WORDS_TABLET_LTR = PUNCTUATION_LABELS_TABLET;
+ private static final String[] PUNCTUATION_WORDS_TABLET_HEBREW = {
+ ":", ";", "\"", ")", "(", "'", "-", "/", "@", "_"
+ };
+ private static final String[] PUNCTUATION_LABELS_TABLET_ARABIC_PERSIAN = {
+ "!", "\u061F", ":", "\u061B", "\"", "'", "(", ")", "-", "/", "@", "_"
+ };
+ private static final String[] PUNCTUATION_WORDS_TABLET_ARABIC_PERSIAN = {
+ "!", "\u061F", ":", "\u061B", "\"", "'", ")", "(", "-", "/", "@", "_"
+ };
+
+ private static void testingStandardPunctuationSuggestions(final SpacingAndPunctuations sp,
+ final String[] punctuationLabels, final String[] punctuationWords) {
+ final SuggestedWords suggestedWords = sp.mSuggestPuncList;
+ assertFalse("typedWordValid", suggestedWords.mTypedWordValid);
+ assertFalse("willAutoCorrect", suggestedWords.mWillAutoCorrect);
+ assertTrue("isPunctuationSuggestions", suggestedWords.isPunctuationSuggestions());
+ assertFalse("isObsoleteSuggestions", suggestedWords.mIsObsoleteSuggestions);
+ assertFalse("isPrediction", suggestedWords.mIsPrediction);
+ assertEquals("size", punctuationLabels.length, suggestedWords.size());
+ for (int index = 0; index < suggestedWords.size(); index++) {
+ assertEquals("punctuation label at " + index,
+ punctuationLabels[index], suggestedWords.getLabel(index));
+ assertEquals("punctuation word at " + index,
+ punctuationWords[index], suggestedWords.getWord(index));
+ }
+ }
+
+ public void testPhonePunctuationSuggestions() {
+ if (!isPhone()) {
+ return;
+ }
+ testingStandardPunctuationSuggestions(ENGLISH,
+ PUNCTUATION_LABELS_PHONE, PUNCTUATION_WORDS_PHONE_LTR);
+ testingStandardPunctuationSuggestions(FRENCH,
+ PUNCTUATION_LABELS_PHONE, PUNCTUATION_WORDS_PHONE_LTR);
+ testingStandardPunctuationSuggestions(GERMAN,
+ PUNCTUATION_LABELS_PHONE, PUNCTUATION_WORDS_PHONE_LTR);
+ testingStandardPunctuationSuggestions(ARABIC,
+ PUNCTUATION_LABELS_PHONE_ARABIC_PERSIAN, PUNCTUATION_WORDS_PHONE_ARABIC_PERSIAN);
+ testingStandardPunctuationSuggestions(PERSIAN,
+ PUNCTUATION_LABELS_PHONE_ARABIC_PERSIAN, PUNCTUATION_WORDS_PHONE_ARABIC_PERSIAN);
+ testingStandardPunctuationSuggestions(HEBREW,
+ PUNCTUATION_LABELS_PHONE, PUNCTUATION_WORDS_PHONE_HEBREW);
+ }
+
+ public void testTabletPunctuationSuggestions() {
+ if (!isTablet()) {
+ return;
+ }
+ testingStandardPunctuationSuggestions(ENGLISH,
+ PUNCTUATION_LABELS_TABLET, PUNCTUATION_WORDS_TABLET_LTR);
+ testingStandardPunctuationSuggestions(FRENCH,
+ PUNCTUATION_LABELS_TABLET, PUNCTUATION_WORDS_TABLET_LTR);
+ testingStandardPunctuationSuggestions(GERMAN,
+ PUNCTUATION_LABELS_TABLET, PUNCTUATION_WORDS_TABLET_LTR);
+ testingStandardPunctuationSuggestions(ARABIC,
+ PUNCTUATION_LABELS_TABLET_ARABIC_PERSIAN, PUNCTUATION_WORDS_TABLET_ARABIC_PERSIAN);
+ testingStandardPunctuationSuggestions(PERSIAN,
+ PUNCTUATION_LABELS_TABLET_ARABIC_PERSIAN, PUNCTUATION_WORDS_TABLET_ARABIC_PERSIAN);
+ testingStandardPunctuationSuggestions(HEBREW,
+ PUNCTUATION_LABELS_TABLET, PUNCTUATION_WORDS_TABLET_HEBREW);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
index 995d7f07b..2272d6ba0 100644
--- a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
+++ b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java
@@ -39,7 +39,7 @@ public class AndroidSpellCheckerServiceTest extends InputTestsBase {
// it yields 5).
assertTrue(suggestions.length >= 2);
// We also assume the top suggestion should be "this".
- assertEquals("", "this", suggestions[0]);
+ assertEquals("Test basic spell checking", "this", suggestions[0]);
}
public void testRussianSpellchecker() {
@@ -62,4 +62,21 @@ public class AndroidSpellCheckerServiceTest extends InputTestsBase {
// Russian dictionary.
assertEquals("", "года", suggestions[0]);
}
+
+ public void testSpellcheckWithPeriods() {
+ changeLanguage("en_US");
+ mEditText.setText("I'm.sure ");
+ mEditText.setSelection(mEditText.getText().length());
+ mEditText.onAttachedToWindow();
+ sleep(1000);
+ runMessages();
+ sleep(1000);
+
+ final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class);
+ // If no span, the following will crash
+ final String[] suggestions = span.getSuggestions();
+ // The first suggestion should be "I'm sure".
+ assertEquals("Test spell checking of mistyped period for space", "I'm sure",
+ suggestions[0]);
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java
new file mode 100644
index 000000000..91c9c3775
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.Context;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+
+import java.util.Locale;
+
+import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue
+ .UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
+
+@SmallTest
+public class AdditionalSubtypeUtilsTests extends AndroidTestCase {
+
+ /**
+ * Predictable subtype ID for en_US dvorak layout. This is actually a hash code calculated as
+ * follows.
+ * <code>
+ * final boolean isAuxiliary = false;
+ * final boolean overrideImplicitlyEnabledSubtype = false;
+ * final int SUBTYPE_ID_EN_US_DVORAK = Arrays.hashCode(new Object[] {
+ * "en_US",
+ * "keyboard",
+ * "KeyboardLayoutSet=dvorak"
+ * + ",AsciiCapable"
+ * + ",UntranslatableReplacementStringInSubtypeName=Dvorak"
+ * + ",EmojiCapable"
+ * + ",isAdditionalSubtype",
+ * isAuxiliary,
+ * overrideImplicitlyEnabledSubtype });
+ * </code>
+ */
+ private static int SUBTYPE_ID_EN_US_DVORAK = 0xb3c0cc56;
+ private static String EXTRA_VALUE_EN_US_DVORAK_ICS =
+ "KeyboardLayoutSet=dvorak" +
+ ",AsciiCapable" +
+ ",isAdditionalSubtype";
+ private static String EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN =
+ "KeyboardLayoutSet=dvorak" +
+ ",AsciiCapable" +
+ ",UntranslatableReplacementStringInSubtypeName=Dvorak" +
+ ",isAdditionalSubtype";
+ private static String EXTRA_VALUE_EN_US_DVORAK_KITKAT =
+ "KeyboardLayoutSet=dvorak" +
+ ",AsciiCapable" +
+ ",UntranslatableReplacementStringInSubtypeName=Dvorak" +
+ ",EmojiCapable" +
+ ",isAdditionalSubtype";
+
+ /**
+ * Predictable subtype ID for azerty layout. This is actually a hash code calculated as follows.
+ * <code>
+ * final boolean isAuxiliary = false;
+ * final boolean overrideImplicitlyEnabledSubtype = false;
+ * final int SUBTYPE_ID_ZZ_AZERTY = Arrays.hashCode(new Object[] {
+ * "zz",
+ * "keyboard",
+ * "KeyboardLayoutSet=azerty"
+ * + ",AsciiCapable"
+ * + ",EmojiCapable"
+ * + ",isAdditionalSubtype",
+ * isAuxiliary,
+ * overrideImplicitlyEnabledSubtype });
+ * </code>
+ */
+ private static int SUBTYPE_ID_ZZ_AZERTY = 0x5b6be697;
+ private static String EXTRA_VALUE_ZZ_AZERTY_ICS =
+ "KeyboardLayoutSet=azerty" +
+ ",AsciiCapable" +
+ ",isAdditionalSubtype";
+ private static String EXTRA_VALUE_ZZ_AZERTY_KITKAT =
+ "KeyboardLayoutSet=azerty" +
+ ",AsciiCapable" +
+ ",EmojiCapable" +
+ ",isAdditionalSubtype";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Context context = getContext();
+ SubtypeLocaleUtils.init(context);
+ }
+
+ private static void assertEnUsDvorak(InputMethodSubtype subtype) {
+ assertEquals("en_US", subtype.getLocale());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ assertEquals(EXTRA_VALUE_EN_US_DVORAK_KITKAT, subtype.getExtraValue());
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ assertEquals(EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN, subtype.getExtraValue());
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ assertEquals(EXTRA_VALUE_EN_US_DVORAK_ICS, subtype.getExtraValue());
+ }
+ assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE));
+ assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype));
+ // TODO: Enable following test
+ // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ // assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype));
+ // }
+ assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE));
+ assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE));
+ assertEquals("dvorak", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET));
+ assertEquals("Dvorak", subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME));
+ assertEquals(KEYBOARD_MODE, subtype.getMode());
+ assertEquals(SUBTYPE_ID_EN_US_DVORAK, subtype.hashCode());
+ }
+
+ private static void assertAzerty(InputMethodSubtype subtype) {
+ assertEquals("zz", subtype.getLocale());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ assertEquals(EXTRA_VALUE_ZZ_AZERTY_KITKAT, subtype.getExtraValue());
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ assertEquals(EXTRA_VALUE_ZZ_AZERTY_ICS, subtype.getExtraValue());
+ }
+ assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE));
+ assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype));
+ // TODO: Enable following test
+ // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ // assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype));
+ // }
+ assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE));
+ assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE));
+ assertEquals("azerty", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET));
+ assertFalse(subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME));
+ assertEquals(KEYBOARD_MODE, subtype.getMode());
+ assertEquals(SUBTYPE_ID_ZZ_AZERTY, subtype.hashCode());
+ }
+
+ public void testRestorable() {
+ final InputMethodSubtype EN_UK_DVORAK =
+ AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.US.toString(), "dvorak");
+ final InputMethodSubtype ZZ_AZERTY =
+ AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+ assertEnUsDvorak(EN_UK_DVORAK);
+ assertAzerty(ZZ_AZERTY);
+
+ // Make sure the subtype can be stored and restored in a deterministic manner.
+ final InputMethodSubtype[] subtypes = { EN_UK_DVORAK, ZZ_AZERTY };
+ final String prefSubtype = AdditionalSubtypeUtils.createPrefSubtypes(subtypes);
+ final InputMethodSubtype[] restoredSubtypes =
+ AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
+ assertEquals(2, restoredSubtypes.length);
+ final InputMethodSubtype restored_EN_UK_DVORAK = restoredSubtypes[0];
+ final InputMethodSubtype restored_ZZ_AZERTY = restoredSubtypes[1];
+
+ assertEnUsDvorak(restored_EN_UK_DVORAK);
+ assertAzerty(restored_ZZ_AZERTY);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java
index 7fd167977..1501e942a 100644
--- a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java
@@ -45,27 +45,27 @@ public class AsyncResultHolderTests extends AndroidTestCase {
}
public void testGetWithoutSet() {
- final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+ final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>();
final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
assertEquals(DEFAULT_VALUE, resultValue);
}
public void testGetBeforeSet() {
- final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+ final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>();
setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS);
final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
assertEquals(DEFAULT_VALUE, resultValue);
}
public void testGetAfterSet() {
- final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+ final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>();
holder.set(SET_VALUE);
final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
assertEquals(SET_VALUE, resultValue);
}
public void testGetBeforeTimeout() {
- final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+ final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>();
setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS);
final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
assertEquals(SET_VALUE, resultValue);
diff --git a/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java b/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java
deleted file mode 100644
index b311f5d37..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.LineNumberReader;
-import java.io.StringReader;
-
-@SmallTest
-public class Base64ReaderTests extends AndroidTestCase {
- private static final String EMPTY_STRING = "";
- private static final String INCOMPLETE_CHAR1 = "Q";
- // Encode 'A'.
- private static final String INCOMPLETE_CHAR2 = "QQ";
- // Encode 'A', 'B'
- private static final String INCOMPLETE_CHAR3 = "QUI";
- // Encode 'A', 'B', 'C'
- private static final String COMPLETE_CHAR4 = "QUJD";
- private static final String ALL_BYTE_PATTERN =
- "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj\n"
- + "JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH\n"
- + "SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr\n"
- + "bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
- + "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz\n"
- + "tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX\n"
- + "2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7\n"
- + "/P3+/w==";
-
- public void test0CharInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(EMPTY_STRING)));
- try {
- reader.readUint8();
- fail("0 char");
- } catch (final EOFException e) {
- assertEquals("0 char", 0, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test1CharInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR1)));
- try {
- reader.readUint8();
- fail("1 char");
- } catch (final EOFException e) {
- assertEquals("1 char", 0, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test2CharsInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR2)));
- try {
- final int v1 = reader.readUint8();
- assertEquals("2 chars pos 0", 'A', v1);
- reader.readUint8();
- fail("2 chars");
- } catch (final EOFException e) {
- assertEquals("2 chars", 1, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test3CharsInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR3)));
- try {
- final int v1 = reader.readUint8();
- assertEquals("3 chars pos 0", 'A', v1);
- final int v2 = reader.readUint8();
- assertEquals("3 chars pos 1", 'B', v2);
- reader.readUint8();
- fail("3 chars");
- } catch (final EOFException e) {
- assertEquals("3 chars", 2, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test4CharsInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(COMPLETE_CHAR4)));
- try {
- final int v1 = reader.readUint8();
- assertEquals("4 chars pos 0", 'A', v1);
- final int v2 = reader.readUint8();
- assertEquals("4 chars pos 1", 'B', v2);
- final int v3 = reader.readUint8();
- assertEquals("4 chars pos 2", 'C', v3);
- reader.readUint8();
- fail("4 chars");
- } catch (final EOFException e) {
- assertEquals("4 chars", 3, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void testAllBytePatternInt8() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(ALL_BYTE_PATTERN)));
- try {
- for (int i = 0; i <= 0xff; i++) {
- final int v = reader.readUint8();
- assertEquals("value: all byte pattern: pos " + i, i, v);
- assertEquals("count: all byte pattern: pos " + i, i + 1, reader.getByteCount());
- }
- } catch (final EOFException e) {
- assertEquals("all byte pattern", 256, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test0CharInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(EMPTY_STRING)));
- try {
- reader.readInt16();
- fail("0 char");
- } catch (final EOFException e) {
- assertEquals("0 char", 0, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test1CharInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR1)));
- try {
- reader.readInt16();
- fail("1 char");
- } catch (final EOFException e) {
- assertEquals("1 char", 0, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test2CharsInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR2)));
- try {
- reader.readInt16();
- fail("2 chars");
- } catch (final EOFException e) {
- assertEquals("2 chars", 1, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test3CharsInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(INCOMPLETE_CHAR3)));
- try {
- final short v1 = reader.readInt16();
- assertEquals("3 chars pos 0", 'A' << 8 | 'B', v1);
- reader.readInt16();
- fail("3 chars");
- } catch (final EOFException e) {
- assertEquals("3 chars", 2, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void test4CharsInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(COMPLETE_CHAR4)));
- try {
- final short v1 = reader.readInt16();
- assertEquals("4 chars pos 0", 'A' << 8 | 'B', v1);
- reader.readInt16();
- fail("4 chars");
- } catch (final EOFException e) {
- assertEquals("4 chars", 3, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-
- public void testAllBytePatternInt16() {
- final Base64Reader reader = new Base64Reader(
- new LineNumberReader(new StringReader(ALL_BYTE_PATTERN)));
- try {
- for (int i = 0; i <= 0xff; i += 2) {
- final short v = reader.readInt16();
- final short expected = (short)(i << 8 | (i + 1));
- assertEquals("value: all byte pattern: pos " + i, expected, v);
- assertEquals("count: all byte pattern: pos " + i, i + 2, reader.getByteCount());
- }
- } catch (final EOFException e) {
- assertEquals("all byte pattern", 256, reader.getByteCount());
- } catch (final IOException e) {
- fail("IOException: " + e);
- }
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
new file mode 100644
index 000000000..a333ee9bc
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+public class BinaryDictionaryUtilsTests extends AndroidTestCase {
+ private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+ private static final String TEST_LOCALE = "test";
+
+ private File createEmptyDictionaryAndGetFile(final String dictId,
+ final int formatVersion) throws IOException {
+ if (formatVersion == FormatSpec.VERSION4) {
+ return createEmptyVer4DictionaryAndGetFile(dictId);
+ } else {
+ throw new IOException("Dictionary format version " + formatVersion
+ + " is not supported.");
+ }
+ }
+
+ private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException {
+ final File file = getDictFile(dictId);
+ FileUtils.deleteRecursively(file);
+ Map<String, String> attributeMap = new HashMap<>();
+ attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId);
+ attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
+ String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+ attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4,
+ LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) {
+ return file;
+ } else {
+ throw new IOException("Empty dictionary " + file.getAbsolutePath()
+ + " cannot be created.");
+ }
+ }
+
+ private File getDictFile(final String dictId) {
+ return new File(getContext().getCacheDir(), dictId + TEST_DICT_FILE_EXTENSION);
+ }
+
+ public void testRenameDictionary() {
+ final int formatVersion = FormatSpec.VERSION4;
+ File dictFile0 = null;
+ try {
+ dictFile0 = createEmptyDictionaryAndGetFile("MoveFromDictionary", formatVersion);
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ }
+ final File dictFile1 = getDictFile("MoveToDictionary");
+ FileUtils.deleteRecursively(dictFile1);
+ assertTrue(BinaryDictionaryUtils.renameDict(dictFile0, dictFile1));
+ assertFalse(dictFile0.exists());
+ assertTrue(dictFile1.exists());
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile1.getAbsolutePath(),
+ 0 /* offset */, dictFile1.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ assertTrue(binaryDictionary.isValidDictionary());
+ assertTrue(binaryDictionary.getFormatVersion() == formatVersion);
+ binaryDictionary.close();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java b/tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
new file mode 100644
index 000000000..2028298f2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+
+/**
+ * This class provides an implementation for the FusionDictionary buffer interface that is backed
+ * by a simpled byte array. It allows to create a binary dictionary in memory.
+ */
+public final class ByteArrayDictBuffer implements DictBuffer {
+ private byte[] mBuffer;
+ private int mPosition;
+
+ public ByteArrayDictBuffer(final byte[] buffer) {
+ mBuffer = buffer;
+ mPosition = 0;
+ }
+
+ @Override
+ public int readUnsignedByte() {
+ return mBuffer[mPosition++] & 0xFF;
+ }
+
+ @Override
+ public int readUnsignedShort() {
+ final int retval = readUnsignedByte();
+ return (retval << 8) + readUnsignedByte();
+ }
+
+ @Override
+ public int readUnsignedInt24() {
+ final int retval = readUnsignedShort();
+ return (retval << 8) + readUnsignedByte();
+ }
+
+ @Override
+ public int readInt() {
+ final int retval = readUnsignedShort();
+ return (retval << 16) + readUnsignedShort();
+ }
+
+ @Override
+ public int position() {
+ return mPosition;
+ }
+
+ @Override
+ public void position(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public void put(final byte b) {
+ mBuffer[mPosition++] = b;
+ }
+
+ @Override
+ public int limit() {
+ return mBuffer.length - 1;
+ }
+
+ @Override
+ public int capacity() {
+ return mBuffer.length;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
index 1fd5c989a..c746c8345 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
@@ -16,75 +16,113 @@
package com.android.inputmethod.latin.utils;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
+import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.LocaleUtils;
+
import java.util.Locale;
@SmallTest
public class CapsModeUtilsTests extends AndroidTestCase {
private static void onePathForCaps(final CharSequence cs, final int expectedResult,
- final int mask, final SettingsValues sv, final boolean hasSpaceBefore) {
- int oneTimeResult = expectedResult & mask;
+ final int mask, final SpacingAndPunctuations sp, final boolean hasSpaceBefore) {
+ final int oneTimeResult = expectedResult & mask;
assertEquals("After >" + cs + "<", oneTimeResult,
- CapsModeUtils.getCapsMode(cs, mask, sv, hasSpaceBefore));
+ CapsModeUtils.getCapsMode(cs, mask, sp, hasSpaceBefore));
}
private static void allPathsForCaps(final CharSequence cs, final int expectedResult,
- final SettingsValues sv, final boolean hasSpaceBefore) {
+ final SpacingAndPunctuations sp, final boolean hasSpaceBefore) {
final int c = TextUtils.CAP_MODE_CHARACTERS;
final int w = TextUtils.CAP_MODE_WORDS;
final int s = TextUtils.CAP_MODE_SENTENCES;
- onePathForCaps(cs, expectedResult, c | w | s, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, w | s, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, c | s, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, c | w, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, c, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, w, sv, hasSpaceBefore);
- onePathForCaps(cs, expectedResult, s, sv, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, c | w | s, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, w | s, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, c | s, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, c | w, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, c, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, w, sp, hasSpaceBefore);
+ onePathForCaps(cs, expectedResult, s, sp, hasSpaceBefore);
}
public void testGetCapsMode() {
final int c = TextUtils.CAP_MODE_CHARACTERS;
final int w = TextUtils.CAP_MODE_WORDS;
final int s = TextUtils.CAP_MODE_SENTENCES;
- SettingsValues sv = SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
- allPathsForCaps("", c | w | s, sv, false);
- allPathsForCaps("Word", c, sv, false);
- allPathsForCaps("Word.", c, sv, false);
- allPathsForCaps("Word ", c | w, sv, false);
- allPathsForCaps("Word. ", c | w | s, sv, false);
- allPathsForCaps("Word..", c, sv, false);
- allPathsForCaps("Word.. ", c | w | s, sv, false);
- allPathsForCaps("Word... ", c | w | s, sv, false);
- allPathsForCaps("Word ... ", c | w | s, sv, false);
- allPathsForCaps("Word . ", c | w, sv, false);
- allPathsForCaps("In the U.S ", c | w, sv, false);
- allPathsForCaps("In the U.S. ", c | w, sv, false);
- allPathsForCaps("Some stuff (e.g. ", c | w, sv, false);
- allPathsForCaps("In the U.S.. ", c | w | s, sv, false);
- allPathsForCaps("\"Word.\" ", c | w | s, sv, false);
- allPathsForCaps("\"Word\". ", c | w | s, sv, false);
- allPathsForCaps("\"Word\" ", c | w, sv, false);
+ final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(final Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ final Resources res = getContext().getResources();
+ SpacingAndPunctuations sp = job.runInLocale(res, Locale.ENGLISH);
+ allPathsForCaps("", c | w | s, sp, false);
+ allPathsForCaps("Word", c, sp, false);
+ allPathsForCaps("Word.", c, sp, false);
+ allPathsForCaps("Word ", c | w, sp, false);
+ allPathsForCaps("Word. ", c | w | s, sp, false);
+ allPathsForCaps("Word..", c, sp, false);
+ allPathsForCaps("Word.. ", c | w | s, sp, false);
+ allPathsForCaps("Word... ", c | w | s, sp, false);
+ allPathsForCaps("Word ... ", c | w | s, sp, false);
+ allPathsForCaps("Word . ", c | w, sp, false);
+ allPathsForCaps("In the U.S ", c | w, sp, false);
+ allPathsForCaps("In the U.S. ", c | w, sp, false);
+ allPathsForCaps("Some stuff (e.g. ", c | w, sp, false);
+ allPathsForCaps("In the U.S.. ", c | w | s, sp, false);
+ allPathsForCaps("\"Word.\" ", c | w | s, sp, false);
+ allPathsForCaps("\"Word\". ", c | w | s, sp, false);
+ allPathsForCaps("\"Word\" ", c | w, sp, false);
// Test for phantom space
- allPathsForCaps("Word", c | w, sv, true);
- allPathsForCaps("Word.", c | w | s, sv, true);
+ allPathsForCaps("Word", c | w, sp, true);
+ allPathsForCaps("Word.", c | w | s, sp, true);
// Tests after some whitespace
- allPathsForCaps("Word\n", c | w | s, sv, false);
- allPathsForCaps("Word\n", c | w | s, sv, true);
- allPathsForCaps("Word\n ", c | w | s, sv, true);
- allPathsForCaps("Word.\n", c | w | s, sv, false);
- allPathsForCaps("Word.\n", c | w | s, sv, true);
- allPathsForCaps("Word.\n ", c | w | s, sv, true);
+ allPathsForCaps("Word\n", c | w | s, sp, false);
+ allPathsForCaps("Word\n", c | w | s, sp, true);
+ allPathsForCaps("Word\n ", c | w | s, sp, true);
+ allPathsForCaps("Word.\n", c | w | s, sp, false);
+ allPathsForCaps("Word.\n", c | w | s, sp, true);
+ allPathsForCaps("Word.\n ", c | w | s, sp, true);
+
+ sp = job.runInLocale(res, Locale.FRENCH);
+ allPathsForCaps("\"Word.\" ", c | w, sp, false);
+ allPathsForCaps("\"Word\". ", c | w | s, sp, false);
+ allPathsForCaps("\"Word\" ", c | w, sp, false);
+
+ // Test special case for German. German does not capitalize at the start of a
+ // line when the previous line starts with a comma. It does in other cases.
+ sp = job.runInLocale(res, Locale.GERMAN);
+ allPathsForCaps("Liebe Sara,\n", c | w, sp, false);
+ allPathsForCaps("Liebe Sara,\n", c | w, sp, true);
+ allPathsForCaps("Liebe Sara, \n ", c | w, sp, false);
+ allPathsForCaps("Liebe Sara \n ", c | w | s, sp, false);
+ allPathsForCaps("Liebe Sara.\n ", c | w | s, sp, false);
+ sp = job.runInLocale(res, Locale.ENGLISH);
+ allPathsForCaps("Liebe Sara,\n", c | w | s, sp, false);
+ allPathsForCaps("Liebe Sara,\n", c | w | s, sp, true);
+ allPathsForCaps("Liebe Sara, \n ", c | w | s, sp, false);
+ allPathsForCaps("Liebe Sara \n ", c | w | s, sp, false);
+ allPathsForCaps("Liebe Sara.\n ", c | w | s, sp, false);
- sv = SettingsValues.makeDummySettingsValuesForTest(Locale.FRENCH);
- allPathsForCaps("\"Word.\" ", c | w, sv, false);
- allPathsForCaps("\"Word\". ", c | w | s, sv, false);
- allPathsForCaps("\"Word\" ", c | w, sv, false);
+ // Test armenian period
+ sp = job.runInLocale(res, LocaleUtils.constructLocaleFromString("hy_AM"));
+ assertTrue("Period is not sentence separator in Armenian",
+ !sp.isSentenceSeparator('.'));
+ assertTrue("Sentence separator is Armenian period in Armenian",
+ sp.isSentenceSeparator(0x589));
+ // No space : capitalize only if MODE_CHARACTERS
+ allPathsForCaps("Word", c, sp, false);
+ allPathsForCaps("Word.", c, sp, false);
+ // Space, but no armenian period : capitalize if MODE_WORDS but not SENTENCES
+ allPathsForCaps("Word. ", c | w, sp, false);
+ // Armenian period : capitalize if MODE_SENTENCES
+ allPathsForCaps("Word\u0589 ", c | w | s, sp, false);
}
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java
deleted file mode 100644
index a0fa8fe4b..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.inputmethod.latin.utils.CsvUtils.CsvParseException;
-
-import java.util.Arrays;
-
-@SmallTest
-public class CsvUtilsTests extends AndroidTestCase {
- public void testUnescape() {
- assertEquals("", CsvUtils.unescapeField(""));
- assertEquals("text", CsvUtils.unescapeField("text")); // text
- assertEquals("", CsvUtils.unescapeField("\"\"")); // ""
- assertEquals("\"", CsvUtils.unescapeField("\"\"\"\"")); // """" -> "
- assertEquals("text", CsvUtils.unescapeField("\"text\"")); // "text" -> text
- assertEquals("\"text", CsvUtils.unescapeField("\"\"\"text\"")); // """text" -> "text
- assertEquals("text\"", CsvUtils.unescapeField("\"text\"\"\"")); // "text""" -> text"
- assertEquals("te\"xt", CsvUtils.unescapeField("\"te\"\"xt\"")); // "te""xt" -> te"xt
- assertEquals("\"text\"",
- CsvUtils.unescapeField("\"\"\"text\"\"\"")); // """text""" -> "text"
- assertEquals("t\"e\"x\"t",
- CsvUtils.unescapeField("\"t\"\"e\"\"x\"\"t\"")); // "t""e""x""t" -> t"e"x"t
- }
-
- public void testUnescapeException() {
- try {
- final String text = CsvUtils.unescapeField("\""); // "
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"\"\""); // """
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"\"\"\"\""); // """""
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"text"); // "text
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("text\""); // text"
- fail("Raw quote in text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("te\"xt"); // te"xt
- fail("Raw quote in text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"\"text"); // ""text
- fail("Raw quote in quoted text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("text\"\""); // text""
- fail("Escaped quote in text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Escaped quote in text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("te\"\"xt"); // te""xt
- fail("Escaped quote in text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Escaped quote in text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"\"text\""); // ""text"
- fail("Raw quote in quoted text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"text\"\""); // "text""
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"te\"xt\""); // "te"xt"
- fail("Raw quote in quoted text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\"b,c"); // "b,c
- fail("Unterminated quote: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String text = CsvUtils.unescapeField("\",\"a\""); // ","a"
- fail("Raw quote in quoted text: text=" + text);
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- }
-
- private static <T> void assertArrayEquals(final T[] expected, final T[] actual) {
- if (expected == actual) {
- return;
- }
- if (expected == null || actual == null) {
- assertEquals(Arrays.toString(expected), Arrays.toString(actual));
- return;
- }
- if (expected.length != actual.length) {
- assertEquals("[length]", Arrays.toString(expected), Arrays.toString(actual));
- return;
- }
- for (int i = 0; i < expected.length; i++) {
- final T e = expected[i];
- final T a = actual[i];
- if (e == a) {
- continue;
- }
- assertEquals("["+i+"]", expected[i], actual[i]);
- }
- }
-
- public void testSplit() {
- assertArrayEquals(new String[]{""}, CsvUtils.split(""));
- assertArrayEquals(new String[]{" "}, CsvUtils.split(" "));
- assertArrayEquals(new String[]{"text"}, CsvUtils.split("text"));
- assertArrayEquals(new String[]{" a b "}, CsvUtils.split(" a b "));
-
- assertArrayEquals(new String[]{"", ""}, CsvUtils.split(","));
- assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(",,"));
- assertArrayEquals(new String[]{" ", " "}, CsvUtils.split(" , "));
- assertArrayEquals(new String[]{" ", " ", " "}, CsvUtils.split(" , , "));
- assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split("a,b"));
- assertArrayEquals(new String[]{" a ", " b "}, CsvUtils.split(" a , b "));
-
- assertArrayEquals(new String[]{"text"},
- CsvUtils.split("\"text\"")); // "text"
- assertArrayEquals(new String[]{" text "},
- CsvUtils.split("\" text \"")); // "_text_"
-
- assertArrayEquals(new String[]{""},
- CsvUtils.split("\"\"")); // ""
- assertArrayEquals(new String[]{"\""},
- CsvUtils.split("\"\"\"\"")); // """"
- assertArrayEquals(new String[]{"", ""},
- CsvUtils.split("\"\",\"\"")); // "",""
- assertArrayEquals(new String[]{"\",\""},
- CsvUtils.split("\"\"\",\"\"\"")); // ""","""
- assertArrayEquals(new String[]{"\"", "\""},
- CsvUtils.split("\"\"\"\",\"\"\"\"")); // """",""""
- assertArrayEquals(new String[]{"\"", "\",\""},
- CsvUtils.split("\"\"\"\",\"\"\",\"\"\"")); // """",""","""
- assertArrayEquals(new String[]{"\",\"", "\""},
- CsvUtils.split("\"\"\",\"\"\",\"\"\"\"")); // """,""",""""
-
- assertArrayEquals(new String[]{" a ", " b , c "},
- CsvUtils.split(" a ,\" b , c \"")); // _a_,"_b_,_c_"
- assertArrayEquals(new String[]{" a ", " b , c ", " d "},
- CsvUtils.split(" a ,\" b , c \", d ")); // _a_,"_b_,_c_",_d_
- }
-
- public void testSplitException() {
- try {
- final String[] fields = CsvUtils.split(" \"text\" "); // _"text"_
- fail("Raw quote in text: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in text", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split(" \" text \" "); // _"_text_"_
- fail("Raw quote in text: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in text", success.getMessage());
- }
-
- try {
- final String[] fields = CsvUtils.split("a,\"b,"); // a,",b
- fail("Unterminated quote: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split("a,\"\"\",b"); // a,""",b
- fail("Unterminated quote: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split("a,\"\"\"\"\",b"); // a,""""",b
- fail("Unterminated quote: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split("a,\"b,c"); // a,"b,c
- fail("Unterminated quote: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Unterminated quote", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split("a,\",\"b,c"); // a,","b,c
- fail("Raw quote in quoted text: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- try {
- final String[] fields = CsvUtils.split("a,\",\"b\",\",c"); // a,","b",",c
- fail("Raw quote in quoted text: fields=" + Arrays.toString(fields));
- } catch (final CsvParseException success) {
- assertEquals("Raw quote in quoted text", success.getMessage());
- }
- }
-
- public void testSplitWithTrimSpaces() {
- final int trimSpaces = CsvUtils.SPLIT_FLAGS_TRIM_SPACES;
- assertArrayEquals(new String[]{""}, CsvUtils.split(trimSpaces, ""));
- assertArrayEquals(new String[]{""}, CsvUtils.split(trimSpaces, " "));
- assertArrayEquals(new String[]{"text"}, CsvUtils.split(trimSpaces, "text"));
- assertArrayEquals(new String[]{"a b"}, CsvUtils.split(trimSpaces, " a b "));
-
- assertArrayEquals(new String[]{"", ""}, CsvUtils.split(trimSpaces, ","));
- assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(trimSpaces, ",,"));
- assertArrayEquals(new String[]{"", ""}, CsvUtils.split(trimSpaces, " , "));
- assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(trimSpaces, " , , "));
- assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split(trimSpaces, "a,b"));
- assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split(trimSpaces, " a , b "));
-
- assertArrayEquals(new String[]{"text"},
- CsvUtils.split(trimSpaces, "\"text\"")); // "text"
- assertArrayEquals(new String[]{"text"},
- CsvUtils.split(trimSpaces, " \"text\" ")); // _"text"_
- assertArrayEquals(new String[]{" text "},
- CsvUtils.split(trimSpaces, "\" text \"")); // "_text_"
- assertArrayEquals(new String[]{" text "},
- CsvUtils.split(trimSpaces, " \" text \" ")); // _"_text_"_
- assertArrayEquals(new String[]{"a", "b"},
- CsvUtils.split(trimSpaces, " \"a\" , \"b\" ")); // _"a"_,_"b"_
-
- assertArrayEquals(new String[]{""},
- CsvUtils.split(trimSpaces, " \"\" ")); // _""_
- assertArrayEquals(new String[]{"\""},
- CsvUtils.split(trimSpaces, " \"\"\"\" ")); // _""""_
- assertArrayEquals(new String[]{"", ""},
- CsvUtils.split(trimSpaces, " \"\" , \"\" ")); // _""_,_""_
- assertArrayEquals(new String[]{"\" , \""},
- CsvUtils.split(trimSpaces, " \"\"\" , \"\"\" ")); // _"""_,_"""_
- assertArrayEquals(new String[]{"\"", "\""},
- CsvUtils.split(trimSpaces, " \"\"\"\" , \"\"\"\" ")); // _""""_,_""""_
- assertArrayEquals(new String[]{"\"", "\" , \""},
- CsvUtils.split(trimSpaces, " \"\"\"\" , \"\"\" , \"\"\" ")); // _""""_,_"""_,_"""_
- assertArrayEquals(new String[]{"\" , \"", "\""},
- CsvUtils.split(trimSpaces, " \"\"\" , \"\"\" , \"\"\"\" ")); // _"""_,_"""_,_""""_
-
- assertArrayEquals(new String[]{"a", " b , c "},
- CsvUtils.split(trimSpaces, " a , \" b , c \" ")); // _a_,_"_b_,_c_"_
- assertArrayEquals(new String[]{"a", " b , c ", "d"},
- CsvUtils.split(trimSpaces, " a, \" b , c \" , d ")); // _a,_"_b_,_c_"_,_d_
- }
-
- public void testEscape() {
- assertEquals("", CsvUtils.escapeField("", false));
- assertEquals("plain", CsvUtils.escapeField("plain", false));
- assertEquals(" ", CsvUtils.escapeField(" ", false));
- assertEquals(" ", CsvUtils.escapeField(" ", false));
- assertEquals("a space", CsvUtils.escapeField("a space", false));
- assertEquals(" space-at-start", CsvUtils.escapeField(" space-at-start", false));
- assertEquals("space-at-end ", CsvUtils.escapeField("space-at-end ", false));
- assertEquals("a lot of spaces", CsvUtils.escapeField("a lot of spaces", false));
- assertEquals("\",\"", CsvUtils.escapeField(",", false));
- assertEquals("\",,\"", CsvUtils.escapeField(",,", false));
- assertEquals("\"a,comma\"", CsvUtils.escapeField("a,comma", false));
- assertEquals("\",comma-at-begin\"", CsvUtils.escapeField(",comma-at-begin", false));
- assertEquals("\"comma-at-end,\"", CsvUtils.escapeField("comma-at-end,", false));
- assertEquals("\",,a,lot,,,of,commas,,\"",
- CsvUtils.escapeField(",,a,lot,,,of,commas,,", false));
- assertEquals("\"a comma,and a space\"", CsvUtils.escapeField("a comma,and a space", false));
- assertEquals("\"\"\"\"", CsvUtils.escapeField("\"", false)); // " -> """"
- assertEquals("\"\"\"\"\"\"", CsvUtils.escapeField("\"\"", false)); // "" -> """"""
- assertEquals("\"\"\"\"\"\"\"\"", CsvUtils.escapeField("\"\"\"", false)); // """ -> """"""""
- assertEquals("\"\"\"text\"\"\"",
- CsvUtils.escapeField("\"text\"", false)); // "text" -> """text"""
- assertEquals("\"text has \"\" in middle\"",
- CsvUtils.escapeField("text has \" in middle", false));
- assertEquals("\"\"\"quote,at begin\"", CsvUtils.escapeField("\"quote,at begin", false));
- assertEquals("\"quote at,end\"\"\"", CsvUtils.escapeField("quote at,end\"", false));
- assertEquals("\"\"\"quote at begin\"", CsvUtils.escapeField("\"quote at begin", false));
- assertEquals("\"quote at end\"\"\"", CsvUtils.escapeField("quote at end\"", false));
- }
-
- public void testEscapeWithAlwaysQuoted() {
- assertEquals("\"\"", CsvUtils.escapeField("", true));
- assertEquals("\"plain\"", CsvUtils.escapeField("plain", true));
- assertEquals("\" \"", CsvUtils.escapeField(" ", true));
- assertEquals("\" \"", CsvUtils.escapeField(" ", true));
- assertEquals("\"a space\"", CsvUtils.escapeField("a space", true));
- assertEquals("\" space-at-start\"", CsvUtils.escapeField(" space-at-start", true));
- assertEquals("\"space-at-end \"", CsvUtils.escapeField("space-at-end ", true));
- assertEquals("\"a lot of spaces\"", CsvUtils.escapeField("a lot of spaces", true));
- assertEquals("\",\"", CsvUtils.escapeField(",", true));
- assertEquals("\",,\"", CsvUtils.escapeField(",,", true));
- assertEquals("\"a,comma\"", CsvUtils.escapeField("a,comma", true));
- assertEquals("\",comma-at-begin\"", CsvUtils.escapeField(",comma-at-begin", true));
- assertEquals("\"comma-at-end,\"", CsvUtils.escapeField("comma-at-end,", true));
- assertEquals("\",,a,lot,,,of,commas,,\"",
- CsvUtils.escapeField(",,a,lot,,,of,commas,,", true));
- assertEquals("\"a comma,and a space\"", CsvUtils.escapeField("a comma,and a space", true));
- assertEquals("\"\"\"\"", CsvUtils.escapeField("\"", true)); // " -> """"
- assertEquals("\"\"\"\"\"\"", CsvUtils.escapeField("\"\"", true)); // "" -> """"""
- assertEquals("\"\"\"\"\"\"\"\"", CsvUtils.escapeField("\"\"\"", true)); // """ -> """"""""
- assertEquals("\"\"\"text\"\"\"",
- CsvUtils.escapeField("\"text\"", true)); // "text" -> """text"""
- assertEquals("\"text has \"\" in middle\"",
- CsvUtils.escapeField("text has \" in middle", true));
- assertEquals("\"\"\"quote,at begin\"", CsvUtils.escapeField("\"quote,at begin", true));
- assertEquals("\"quote at,end\"\"\"", CsvUtils.escapeField("quote at,end\"", true));
- assertEquals("\"\"\"quote at begin\"", CsvUtils.escapeField("\"quote at begin", true));
- assertEquals("\"quote at end\"\"\"", CsvUtils.escapeField("quote at end\"", true));
- }
-
- public void testJoinWithoutColumnPositions() {
- assertEquals("", CsvUtils.join());
- assertEquals("", CsvUtils.join(""));
- assertEquals(",", CsvUtils.join("", ""));
-
- assertEquals("text, text,text ",
- CsvUtils.join("text", " text", "text "));
- assertEquals("\"\"\"\",\"\"\"\"\"\",\"\"\"text\"\"\"",
- CsvUtils.join("\"", "\"\"", "\"text\""));
- assertEquals("a b,\"c,d\",\"e\"\"f\"",
- CsvUtils.join("a b", "c,d", "e\"f"));
- }
-
- public void testJoinWithoutColumnPositionsWithExtraSpace() {
- final int extraSpace = CsvUtils.JOIN_FLAGS_EXTRA_SPACE;
- assertEquals("", CsvUtils.join(extraSpace));
- assertEquals("", CsvUtils.join(extraSpace, ""));
- assertEquals(", ", CsvUtils.join(extraSpace, "", ""));
-
- assertEquals("text, text, text ",
- CsvUtils.join(extraSpace, "text", " text", "text "));
- // ","","text" -> """","""""","""text"""
- assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"",
- CsvUtils.join(extraSpace, "\"", "\"\"", "\"text\""));
- assertEquals("a b, \"c,d\", \"e\"\"f\"",
- CsvUtils.join(extraSpace, "a b", "c,d", "e\"f"));
- }
-
- public void testJoinWithoutColumnPositionsWithExtraSpaceAndAlwaysQuoted() {
- final int extrSpaceAndQuoted =
- CsvUtils.JOIN_FLAGS_EXTRA_SPACE | CsvUtils.JOIN_FLAGS_ALWAYS_QUOTED;
- assertEquals("", CsvUtils.join(extrSpaceAndQuoted));
- assertEquals("\"\"", CsvUtils.join(extrSpaceAndQuoted, ""));
- assertEquals("\"\", \"\"", CsvUtils.join(extrSpaceAndQuoted, "", ""));
-
- assertEquals("\"text\", \" text\", \"text \"",
- CsvUtils.join(extrSpaceAndQuoted, "text", " text", "text "));
- // ","","text" -> """", """""", """text"""
- assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"",
- CsvUtils.join(extrSpaceAndQuoted, "\"", "\"\"", "\"text\""));
- assertEquals("\"a b\", \"c,d\", \"e\"\"f\"",
- CsvUtils.join(extrSpaceAndQuoted, "a b", "c,d", "e\"f"));
- }
-
- public void testJoinWithColumnPositions() {
- final int noFlags = CsvUtils.JOIN_FLAGS_NONE;
- assertEquals("", CsvUtils.join(noFlags, new int[]{}));
- assertEquals(" ", CsvUtils.join(noFlags, new int[]{3}, ""));
- assertEquals(" ,", CsvUtils.join(noFlags, new int[]{1}, "", ""));
- assertEquals(", ", CsvUtils.join(noFlags, new int[]{0, 3}, "", ""));
-
- assertEquals("text, text, text ",
- CsvUtils.join(noFlags, new int[]{0, 8, 15}, "text", " text", "text "));
- // ","","text" -> """", """""","""text"""
- assertEquals("\"\"\"\", \"\"\"\"\"\",\"\"\"text\"\"\"",
- CsvUtils.join(noFlags, new int[]{0, 8, 15}, "\"", "\"\"", "\"text\""));
- assertEquals("a b, \"c,d\", \"e\"\"f\"",
- CsvUtils.join(noFlags, new int[]{0, 8, 15}, "a b", "c,d", "e\"f"));
- }
-
- public void testJoinWithColumnPositionsWithExtraSpace() {
- final int extraSpace = CsvUtils.JOIN_FLAGS_EXTRA_SPACE;
- assertEquals("", CsvUtils.join(extraSpace, new int[]{}));
- assertEquals(" ", CsvUtils.join(extraSpace, new int[]{3}, ""));
- assertEquals(" , ", CsvUtils.join(extraSpace, new int[]{1}, "", ""));
- assertEquals(", ", CsvUtils.join(extraSpace, new int[]{0, 3}, "", ""));
-
- assertEquals("text, text, text ",
- CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "text", " text", "text "));
- // ","","text" -> """", """""", """text"""
- assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"",
- CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "\"", "\"\"", "\"text\""));
- assertEquals("a b, \"c,d\", \"e\"\"f\"",
- CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "a b", "c,d", "e\"f"));
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java
new file mode 100644
index 000000000..6e716074c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+
+import java.util.Locale;
+
+@SmallTest
+public class DictionaryInfoUtilsTests extends AndroidTestCase {
+ public void testLooksValidForDictionaryInsertion() {
+ final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(final Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ final Resources res = getContext().getResources();
+ final SpacingAndPunctuations sp = job.runInLocale(res, Locale.ENGLISH);
+ assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("aochaueo", sp));
+ assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("", sp));
+ assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("ao-ch'aueo", sp));
+ assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("2908743256", sp));
+ assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("31aochaueo", sp));
+ assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("akeo raeoch oerch .",
+ sp));
+ assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("!!!", sp));
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/EditDistanceTests.java b/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java
index 0b7fcbbe8..58312264b 100644
--- a/tests/src/com/android/inputmethod/latin/EditDistanceTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java
@@ -14,23 +14,13 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.utils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@SmallTest
public class EditDistanceTests extends AndroidTestCase {
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
/*
* dist(kitten, sitting) == 3
*
@@ -39,7 +29,7 @@ public class EditDistanceTests extends AndroidTestCase {
* sitting
*/
public void testExample1() {
- final int dist = BinaryDictionary.editDistance("kitten", "sitting");
+ final int dist = BinaryDictionaryUtils.editDistance("kitten", "sitting");
assertEquals("edit distance between 'kitten' and 'sitting' is 3",
3, dist);
}
@@ -52,26 +42,26 @@ public class EditDistanceTests extends AndroidTestCase {
* S--unday
*/
public void testExample2() {
- final int dist = BinaryDictionary.editDistance("Saturday", "Sunday");
+ final int dist = BinaryDictionaryUtils.editDistance("Saturday", "Sunday");
assertEquals("edit distance between 'Saturday' and 'Sunday' is 3",
3, dist);
}
public void testBothEmpty() {
- final int dist = BinaryDictionary.editDistance("", "");
+ final int dist = BinaryDictionaryUtils.editDistance("", "");
assertEquals("when both string are empty, no edits are needed",
0, dist);
}
public void testFirstArgIsEmpty() {
- final int dist = BinaryDictionary.editDistance("", "aaaa");
+ final int dist = BinaryDictionaryUtils.editDistance("", "aaaa");
assertEquals("when only one string of the arguments is empty,"
+ " the edit distance is the length of the other.",
4, dist);
}
public void testSecoondArgIsEmpty() {
- final int dist = BinaryDictionary.editDistance("aaaa", "");
+ final int dist = BinaryDictionaryUtils.editDistance("aaaa", "");
assertEquals("when only one string of the arguments is empty,"
+ " the edit distance is the length of the other.",
4, dist);
@@ -80,27 +70,27 @@ public class EditDistanceTests extends AndroidTestCase {
public void testSameStrings() {
final String arg1 = "The quick brown fox jumps over the lazy dog.";
final String arg2 = "The quick brown fox jumps over the lazy dog.";
- final int dist = BinaryDictionary.editDistance(arg1, arg2);
+ final int dist = BinaryDictionaryUtils.editDistance(arg1, arg2);
assertEquals("when same strings are passed, distance equals 0.",
0, dist);
}
public void testSameReference() {
final String arg = "The quick brown fox jumps over the lazy dog.";
- final int dist = BinaryDictionary.editDistance(arg, arg);
+ final int dist = BinaryDictionaryUtils.editDistance(arg, arg);
assertEquals("when same string references are passed, the distance equals 0.",
0, dist);
}
public void testNullArg() {
try {
- BinaryDictionary.editDistance(null, "aaa");
+ BinaryDictionaryUtils.editDistance(null, "aaa");
fail("IllegalArgumentException should be thrown.");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
}
try {
- BinaryDictionary.editDistance("aaa", null);
+ BinaryDictionaryUtils.editDistance("aaa", null);
fail("IllegalArgumentException should be thrown.");
} catch (Exception e) {
assertTrue(e instanceof IllegalArgumentException);
diff --git a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java
new file mode 100644
index 000000000..ae2623d12
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit tests for ExecutorUtils.
+ */
+@MediumTest
+public class ExecutorUtilsTests extends AndroidTestCase {
+ private static final String TAG = ExecutorUtilsTests.class.getSimpleName();
+
+ private static final String TEST_EXECUTOR_ID = "test";
+ private static final int NUM_OF_TASKS = 10;
+ private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500;
+
+ public void testExecute() {
+ final ExecutorService executor = ExecutorUtils.getExecutor(TEST_EXECUTOR_ID);
+ final AtomicInteger v = new AtomicInteger(0);
+ for (int i = 0; i < NUM_OF_TASKS; ++i) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ v.incrementAndGet();
+ }
+ });
+ }
+ try {
+ executor.awaitTermination(DELAY_FOR_WAITING_TASKS_MILLISECONDS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Exception while sleeping.", e);
+ }
+
+ assertEquals(NUM_OF_TASKS, v.get());
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java b/tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java
deleted file mode 100644
index 823bd5d7d..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-@SmallTest
-public class ForgettingCurveTests extends AndroidTestCase {
- public void testFcToFreq() {
- for (int i = 0; i < Byte.MAX_VALUE; ++i) {
- final byte fc = (byte)i;
- final int e = UserHistoryForgettingCurveUtils.fcToElapsedTime(fc);
- final int c = UserHistoryForgettingCurveUtils.fcToCount(fc);
- final int l = UserHistoryForgettingCurveUtils.fcToLevel(fc);
- final byte fc2 = UserHistoryForgettingCurveUtils.calcFc(e, c, l);
- assertEquals(fc, fc2);
- }
- byte fc = 0;
- int l;
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < (UserHistoryForgettingCurveUtils.COUNT_MAX + 1); ++j) {
- fc = UserHistoryForgettingCurveUtils.pushCount(fc, true);
- }
- l = UserHistoryForgettingCurveUtils.fcToLevel(fc);
- assertEquals(l, Math.max(1, Math.min(i + 1, 3)));
- }
- fc = 0;
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < (UserHistoryForgettingCurveUtils.COUNT_MAX + 1); ++j) {
- fc = UserHistoryForgettingCurveUtils.pushCount(fc, false);
- }
- l = UserHistoryForgettingCurveUtils.fcToLevel(fc);
- assertEquals(l, Math.min(i + 1, 3));
- }
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < (UserHistoryForgettingCurveUtils.ELAPSED_TIME_MAX + 1); ++j) {
- fc = UserHistoryForgettingCurveUtils.pushElapsedTime(fc);
- }
- l = UserHistoryForgettingCurveUtils.fcToLevel(fc);
- assertEquals(l, Math.max(0, 2 - i));
- }
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
deleted file mode 100644
index e0755483c..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Unit tests for PrioritizedSerialExecutor.
- * TODO: Add more detailed tests to make use of priorities, etc.
- */
-@MediumTest
-public class PrioritizedSerialExecutorTests extends AndroidTestCase {
- private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName();
-
- private static final int NUM_OF_TASKS = 10;
- private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500;
-
- public void testExecute() {
- final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
- final AtomicInteger v = new AtomicInteger(0);
- for (int i = 0; i < NUM_OF_TASKS; ++i) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- v.incrementAndGet();
- }
- });
- }
- try {
- Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
- } catch (InterruptedException e) {
- Log.d(TAG, "Exception while sleeping.", e);
- }
-
- assertEquals(NUM_OF_TASKS, v.get());
- }
-
- public void testExecutePrioritized() {
- final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
- final AtomicInteger v = new AtomicInteger(0);
- for (int i = 0; i < NUM_OF_TASKS; ++i) {
- executor.executePrioritized(new Runnable() {
- @Override
- public void run() {
- v.incrementAndGet();
- }
- });
- }
- try {
- Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
- } catch (InterruptedException e) {
- Log.d(TAG, "Exception while sleeping.", e);
- }
-
- assertEquals(NUM_OF_TASKS, v.get());
- }
-
- public void testExecuteCombined() {
- final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
- final AtomicInteger v = new AtomicInteger(0);
- for (int i = 0; i < NUM_OF_TASKS; ++i) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- v.incrementAndGet();
- }
- });
- }
-
- for (int i = 0; i < NUM_OF_TASKS; ++i) {
- executor.executePrioritized(new Runnable() {
- @Override
- public void run() {
- v.incrementAndGet();
- }
- });
- }
-
- try {
- Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
- } catch (InterruptedException e) {
- Log.d(TAG, "Exception while sleeping.", e);
- }
-
- assertEquals(2 * NUM_OF_TASKS, v.get());
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java
index a52041264..a3f2ce586 100644
--- a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java
@@ -19,31 +19,35 @@ package com.android.inputmethod.latin.utils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.inputmethod.latin.Constants;
+
import java.util.Locale;
@SmallTest
public class RecapitalizeStatusTests extends AndroidTestCase {
+ private static final int[] SPACE = { Constants.CODE_SPACE };
+
public void testTrim() {
final RecapitalizeStatus status = new RecapitalizeStatus();
- status.initialize(30, 40, "abcdefghij", Locale.ENGLISH, " ");
+ status.start(30, 40, "abcdefghij", Locale.ENGLISH, SPACE);
status.trim();
assertEquals("abcdefghij", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(40, status.getNewCursorEnd());
- status.initialize(30, 44, " abcdefghij", Locale.ENGLISH, " ");
+ status.start(30, 44, " abcdefghij", Locale.ENGLISH, SPACE);
status.trim();
assertEquals("abcdefghij", status.getRecapitalizedString());
assertEquals(34, status.getNewCursorStart());
assertEquals(44, status.getNewCursorEnd());
- status.initialize(30, 40, "abcdefgh ", Locale.ENGLISH, " ");
+ status.start(30, 40, "abcdefgh ", Locale.ENGLISH, SPACE);
status.trim();
assertEquals("abcdefgh", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(38, status.getNewCursorEnd());
- status.initialize(30, 45, " abcdefghij ", Locale.ENGLISH, " ");
+ status.start(30, 45, " abcdefghij ", Locale.ENGLISH, SPACE);
status.trim();
assertEquals("abcdefghij", status.getRecapitalizedString());
assertEquals(33, status.getNewCursorStart());
@@ -52,7 +56,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
public void testRotate() {
final RecapitalizeStatus status = new RecapitalizeStatus();
- status.initialize(29, 40, "abcd efghij", Locale.ENGLISH, " ");
+ status.start(29, 40, "abcd efghij", Locale.ENGLISH, SPACE);
status.rotate();
assertEquals("Abcd Efghij", status.getRecapitalizedString());
assertEquals(29, status.getNewCursorStart());
@@ -64,7 +68,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
status.rotate();
assertEquals("Abcd Efghij", status.getRecapitalizedString());
- status.initialize(29, 40, "Abcd Efghij", Locale.ENGLISH, " ");
+ status.start(29, 40, "Abcd Efghij", Locale.ENGLISH, SPACE);
status.rotate();
assertEquals("ABCD EFGHIJ", status.getRecapitalizedString());
assertEquals(29, status.getNewCursorStart());
@@ -76,7 +80,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
status.rotate();
assertEquals("ABCD EFGHIJ", status.getRecapitalizedString());
- status.initialize(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, " ");
+ status.start(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, SPACE);
status.rotate();
assertEquals("abcd efghij", status.getRecapitalizedString());
assertEquals(29, status.getNewCursorStart());
@@ -88,7 +92,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
status.rotate();
assertEquals("abcd efghij", status.getRecapitalizedString());
- status.initialize(29, 39, "AbCDefghij", Locale.ENGLISH, " ");
+ status.start(29, 39, "AbCDefghij", Locale.ENGLISH, SPACE);
status.rotate();
assertEquals("abcdefghij", status.getRecapitalizedString());
assertEquals(29, status.getNewCursorStart());
@@ -102,7 +106,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
status.rotate();
assertEquals("abcdefghij", status.getRecapitalizedString());
- status.initialize(29, 40, "Abcd efghij", Locale.ENGLISH, " ");
+ status.start(29, 40, "Abcd efghij", Locale.ENGLISH, SPACE);
status.rotate();
assertEquals("abcd efghij", status.getRecapitalizedString());
assertEquals(29, status.getNewCursorStart());
@@ -116,7 +120,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
status.rotate();
assertEquals("abcd efghij", status.getRecapitalizedString());
- status.initialize(30, 34, "grüß", Locale.GERMAN, " "); status.rotate();
+ status.start(30, 34, "grüß", Locale.GERMAN, SPACE);
+ status.rotate();
assertEquals("Grüß", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(34, status.getNewCursorEnd());
@@ -133,7 +138,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
assertEquals(30, status.getNewCursorStart());
assertEquals(34, status.getNewCursorEnd());
- status.initialize(30, 33, "œuf", Locale.FRENCH, " "); status.rotate();
+ status.start(30, 33, "œuf", Locale.FRENCH, SPACE);
+ status.rotate();
assertEquals("Œuf", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(33, status.getNewCursorEnd());
@@ -150,7 +156,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
assertEquals(30, status.getNewCursorStart());
assertEquals(33, status.getNewCursorEnd());
- status.initialize(30, 33, "œUf", Locale.FRENCH, " "); status.rotate();
+ status.start(30, 33, "œUf", Locale.FRENCH, SPACE);
+ status.rotate();
assertEquals("œuf", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(33, status.getNewCursorEnd());
@@ -171,7 +178,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase {
assertEquals(30, status.getNewCursorStart());
assertEquals(33, status.getNewCursorEnd());
- status.initialize(30, 35, "école", Locale.FRENCH, " "); status.rotate();
+ status.start(30, 35, "école", Locale.FRENCH, SPACE);
+ status.rotate();
assertEquals("École", status.getRecapitalizedString());
assertEquals(30, status.getNewCursorStart());
assertEquals(35, status.getNewCursorEnd());
diff --git a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
index cad80d5ce..8f58e6873 100644
--- a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
@@ -39,7 +39,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
int[] array2 = null, array3 = null;
final int limit = DEFAULT_CAPACITY * 2 + 10;
for (int i = 0; i < limit; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
assertEquals("length after add " + i, i + 1, src.getLength());
if (i == DEFAULT_CAPACITY) {
array2 = src.getPrimitiveArray();
@@ -56,7 +57,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
}
}
for (int i = 0; i < limit; i++) {
- assertEquals("value at " + i, i, src.get(i));
+ final int value = i;
+ assertEquals("value at " + i, value, src.get(i));
}
}
@@ -64,11 +66,13 @@ public class ResizableIntArrayTests extends AndroidTestCase {
final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
final int limit = DEFAULT_CAPACITY * 10, step = DEFAULT_CAPACITY * 2;
for (int i = 0; i < limit; i += step) {
- src.add(i, i);
+ final int value = i;
+ src.addAt(i, value);
assertEquals("length after add at " + i, i + 1, src.getLength());
}
for (int i = 0; i < limit; i += step) {
- assertEquals("value at " + i, i, src.get(i));
+ final int value = i;
+ assertEquals("value at " + i, value, src.get(i));
}
}
@@ -88,9 +92,10 @@ public class ResizableIntArrayTests extends AndroidTestCase {
}
final int index = DEFAULT_CAPACITY / 2;
- src.add(index, 100);
+ final int valueAddAt = 100;
+ src.addAt(index, valueAddAt);
assertEquals("legth after add at " + index, index + 1, src.getLength());
- assertEquals("value after add at " + index, 100, src.get(index));
+ assertEquals("value after add at " + index, valueAddAt, src.get(index));
assertEquals("value after add at 0", 0, src.get(0));
try {
final int value = src.get(src.getLength());
@@ -104,7 +109,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
final int[] array = src.getPrimitiveArray();
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
assertEquals("length after add " + i, i + 1, src.getLength());
}
@@ -116,7 +122,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
int[] array3 = null;
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
assertEquals("length after add " + i, i + 1, src.getLength());
if (i == smallerLength) {
array3 = src.getPrimitiveArray();
@@ -133,7 +140,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
final int[] array = src.getPrimitiveArray();
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
assertEquals("length after add " + i, i + 1, src.getLength());
}
@@ -144,11 +152,11 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertNotSame("array after larger setLength", array, array2);
assertEquals("array length after larger setLength", largerLength, array2.length);
for (int i = 0; i < largerLength; i++) {
- final int v = src.get(i);
+ final int value = i;
if (i < DEFAULT_CAPACITY) {
- assertEquals("value at " + i, i, v);
+ assertEquals("value at " + i, value, src.get(i));
} else {
- assertEquals("value at " + i, 0, v);
+ assertEquals("value at " + i, 0, src.get(i));
}
}
@@ -159,7 +167,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertSame("array after smaller setLength", array2, array3);
assertEquals("array length after smaller setLength", largerLength, array3.length);
for (int i = 0; i < smallerLength; i++) {
- assertEquals("value at " + i, i, src.get(i));
+ final int value = i;
+ assertEquals("value at " + i, value, src.get(i));
}
}
@@ -167,7 +176,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
final int limit = DEFAULT_CAPACITY * 2 + 10;
for (int i = 0; i < limit; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
}
final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY);
@@ -179,7 +189,8 @@ public class ResizableIntArrayTests extends AndroidTestCase {
public void testCopy() {
final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
- src.add(i);
+ final int value = i;
+ src.add(value);
}
final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY);
@@ -204,119 +215,126 @@ public class ResizableIntArrayTests extends AndroidTestCase {
}
public void testAppend() {
- final int srcLen = DEFAULT_CAPACITY;
- final ResizableIntArray src = new ResizableIntArray(srcLen);
- for (int i = 0; i < srcLen; i++) {
- src.add(i);
+ final int srcLength = DEFAULT_CAPACITY;
+ final ResizableIntArray src = new ResizableIntArray(srcLength);
+ for (int i = 0; i < srcLength; i++) {
+ final int value = i;
+ src.add(value);
}
final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY * 2);
final int[] array = dst.getPrimitiveArray();
- final int dstLen = DEFAULT_CAPACITY / 2;
- for (int i = 0; i < dstLen; i++) {
+ final int dstLength = DEFAULT_CAPACITY / 2;
+ for (int i = 0; i < dstLength; i++) {
final int value = -i - 1;
dst.add(value);
}
final ResizableIntArray dstCopy = new ResizableIntArray(dst.getLength());
dstCopy.copy(dst);
- dst.append(src, 0, 0);
- assertEquals("length after append zero", dstLen, dst.getLength());
+ final int startPos = 0;
+ dst.append(src, startPos, 0 /* length */);
+ assertEquals("length after append zero", dstLength, dst.getLength());
assertSame("array after append zero", array, dst.getPrimitiveArray());
- assertIntArrayEquals("values after append zero",
- dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
+ assertIntArrayEquals("values after append zero", dstCopy.getPrimitiveArray(), startPos,
+ dst.getPrimitiveArray(), startPos, dstLength);
- dst.append(src, 0, srcLen);
- assertEquals("length after append", dstLen + srcLen, dst.getLength());
+ dst.append(src, startPos, srcLength);
+ assertEquals("length after append", dstLength + srcLength, dst.getLength());
assertSame("array after append", array, dst.getPrimitiveArray());
assertTrue("primitive length after append",
- dst.getPrimitiveArray().length >= dstLen + srcLen);
- assertIntArrayEquals("original values after append",
- dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
- assertIntArrayEquals("appended values after append",
- src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen);
+ dst.getPrimitiveArray().length >= dstLength + srcLength);
+ assertIntArrayEquals("original values after append", dstCopy.getPrimitiveArray(), startPos,
+ dst.getPrimitiveArray(), startPos, dstLength);
+ assertIntArrayEquals("appended values after append", src.getPrimitiveArray(), startPos,
+ dst.getPrimitiveArray(), dstLength, srcLength);
- dst.append(src, 0, srcLen);
- assertEquals("length after 2nd append", dstLen + srcLen * 2, dst.getLength());
+ dst.append(src, startPos, srcLength);
+ assertEquals("length after 2nd append", dstLength + srcLength * 2, dst.getLength());
assertNotSame("array after 2nd append", array, dst.getPrimitiveArray());
assertTrue("primitive length after 2nd append",
- dst.getPrimitiveArray().length >= dstLen + srcLen * 2);
+ dst.getPrimitiveArray().length >= dstLength + srcLength * 2);
assertIntArrayEquals("original values after 2nd append",
- dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
+ dstCopy.getPrimitiveArray(), startPos, dst.getPrimitiveArray(), startPos,
+ dstLength);
assertIntArrayEquals("appended values after 2nd append",
- src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen);
+ src.getPrimitiveArray(), startPos, dst.getPrimitiveArray(), dstLength,
+ srcLength);
assertIntArrayEquals("appended values after 2nd append",
- src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen + srcLen, srcLen);
+ src.getPrimitiveArray(), startPos, dst.getPrimitiveArray(), dstLength + srcLength,
+ srcLength);
}
public void testFill() {
- final int srcLen = DEFAULT_CAPACITY;
- final ResizableIntArray src = new ResizableIntArray(srcLen);
- for (int i = 0; i < srcLen; i++) {
- src.add(i);
+ final int srcLength = DEFAULT_CAPACITY;
+ final ResizableIntArray src = new ResizableIntArray(srcLength);
+ for (int i = 0; i < srcLength; i++) {
+ final int value = i;
+ src.add(value);
}
final int[] array = src.getPrimitiveArray();
- final int startPos = srcLen / 3;
- final int length = srcLen / 3;
+ final int startPos = srcLength / 3;
+ final int length = srcLength / 3;
final int endPos = startPos + length;
assertTrue(startPos >= 1);
- final int value = 123;
+ final int fillValue = 123;
try {
- src.fill(value, -1, length);
+ src.fill(fillValue, -1 /* startPos */, length);
fail("fill from -1 shouldn't succeed");
} catch (IllegalArgumentException e) {
// success
}
try {
- src.fill(value, startPos, -1);
+ src.fill(fillValue, startPos, -1 /* length */);
fail("fill negative length shouldn't succeed");
} catch (IllegalArgumentException e) {
// success
}
- src.fill(value, startPos, length);
- assertEquals("length after fill", srcLen, src.getLength());
+ src.fill(fillValue, startPos, length);
+ assertEquals("length after fill", srcLength, src.getLength());
assertSame("array after fill", array, src.getPrimitiveArray());
- for (int i = 0; i < srcLen; i++) {
- final int v = src.get(i);
+ for (int i = 0; i < srcLength; i++) {
+ final int value = i;
if (i >= startPos && i < endPos) {
- assertEquals("new values after fill at " + i, value, v);
+ assertEquals("new values after fill at " + i, fillValue, src.get(i));
} else {
- assertEquals("unmodified values after fill at " + i, i, v);
+ assertEquals("unmodified values after fill at " + i, value, src.get(i));
}
}
- final int length2 = srcLen * 2 - startPos;
+ final int length2 = srcLength * 2 - startPos;
final int largeEnd = startPos + length2;
- assertTrue(largeEnd > srcLen);
- final int value2 = 456;
- src.fill(value2, startPos, length2);
+ assertTrue(largeEnd > srcLength);
+ final int fillValue2 = 456;
+ src.fill(fillValue2, startPos, length2);
assertEquals("length after large fill", largeEnd, src.getLength());
assertNotSame("array after large fill", array, src.getPrimitiveArray());
for (int i = 0; i < largeEnd; i++) {
- final int v = src.get(i);
+ final int value = i;
if (i >= startPos && i < largeEnd) {
- assertEquals("new values after large fill at " + i, value2, v);
+ assertEquals("new values after large fill at " + i, fillValue2, src.get(i));
} else {
- assertEquals("unmodified values after large fill at " + i, i, v);
+ assertEquals("unmodified values after large fill at " + i, value, src.get(i));
}
}
final int startPos2 = largeEnd + length2;
final int endPos2 = startPos2 + length2;
- final int value3 = 789;
- src.fill(value3, startPos2, length2);
+ final int fillValue3 = 789;
+ src.fill(fillValue3, startPos2, length2);
assertEquals("length after disjoint fill", endPos2, src.getLength());
for (int i = 0; i < endPos2; i++) {
- final int v = src.get(i);
+ final int value = i;
if (i >= startPos2 && i < endPos2) {
- assertEquals("new values after disjoint fill at " + i, value3, v);
+ assertEquals("new values after disjoint fill at " + i, fillValue3, src.get(i));
} else if (i >= startPos && i < largeEnd) {
- assertEquals("unmodified values after disjoint fill at " + i, value2, v);
+ assertEquals("unmodified values after disjoint fill at " + i,
+ fillValue2, src.get(i));
} else if (i < startPos) {
- assertEquals("unmodified values after disjoint fill at " + i, i, v);
+ assertEquals("unmodified values after disjoint fill at " + i, value, src.get(i));
} else {
- assertEquals("gap values after disjoint fill at " + i, 0, v);
+ assertEquals("gap values after disjoint fill at " + i, 0, src.get(i));
}
}
}
@@ -346,12 +364,14 @@ public class ResizableIntArrayTests extends AndroidTestCase {
final int limit = DEFAULT_CAPACITY * 10;
final int shiftAmount = 20;
for (int i = 0; i < limit; ++i) {
- src.add(i, i);
+ final int value = i;
+ src.addAt(i, value);
assertEquals("length after add at " + i, i + 1, src.getLength());
}
src.shift(shiftAmount);
for (int i = 0; i < limit - shiftAmount; ++i) {
- assertEquals("value at " + i, i + shiftAmount, src.get(i));
+ final int oldValue = i + shiftAmount;
+ assertEquals("value at " + i, oldValue, src.get(i));
}
}
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java
index 1ae22e307..8e764e40f 100644
--- a/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java
@@ -19,48 +19,15 @@ package com.android.inputmethod.latin.utils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.inputmethod.latin.utils.ResourceUtils.DeviceOverridePatternSyntaxError;
-
import java.util.HashMap;
@SmallTest
public class ResourceUtilsTests extends AndroidTestCase {
- public void testFindDefaultConstant() {
- final String[] nullArray = null;
- final String[] emptyArray = {};
- final String[] array = {
- "HARDWARE=grouper,0.3",
- "HARDWARE=mako,0.4",
- ",defaultValue1",
- "HARDWARE=manta,0.2",
- ",defaultValue2",
- };
-
- try {
- assertNull(ResourceUtils.findDefaultConstant(nullArray));
- assertNull(ResourceUtils.findDefaultConstant(emptyArray));
- assertEquals(ResourceUtils.findDefaultConstant(array), "defaultValue1");
- } catch (final DeviceOverridePatternSyntaxError e) {
- fail(e.getMessage());
- }
-
- final String[] errorArray = {
- "HARDWARE=grouper,0.3",
- "no_comma"
- };
- try {
- final String defaultValue = ResourceUtils.findDefaultConstant(errorArray);
- fail("exception should be thrown: defaultValue=" + defaultValue);
- } catch (final DeviceOverridePatternSyntaxError e) {
- assertEquals("Array element has no comma: no_comma", e.getMessage());
- }
- }
-
public void testFindConstantForKeyValuePairsSimple() {
- final HashMap<String,String> anyKeyValue = CollectionUtils.newHashMap();
+ final HashMap<String,String> anyKeyValue = new HashMap<>();
anyKeyValue.put("anyKey", "anyValue");
final HashMap<String,String> nullKeyValue = null;
- final HashMap<String,String> emptyKeyValue = CollectionUtils.newHashMap();
+ final HashMap<String,String> emptyKeyValue = new HashMap<>();
final String[] nullArray = null;
assertNull(ResourceUtils.findConstantForKeyValuePairs(anyKeyValue, nullArray));
@@ -81,7 +48,7 @@ public class ResourceUtilsTests extends AndroidTestCase {
"HARDWARE=mako,0.5",
};
- final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+ final HashMap<String,String> keyValues = new HashMap<>();
keyValues.put(HARDWARE_KEY, "grouper");
assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "mako");
@@ -121,7 +88,7 @@ public class ResourceUtilsTests extends AndroidTestCase {
"HARDWARE=mantaray:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
};
- final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+ final HashMap<String,String> keyValues = new HashMap<>();
keyValues.put(HARDWARE_KEY, "grouper");
keyValues.put(MODEL_KEY, "Nexus 7");
keyValues.put(MANUFACTURER_KEY, "asus");
@@ -159,7 +126,7 @@ public class ResourceUtilsTests extends AndroidTestCase {
"HARDWARE=manta.*:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
};
- final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
+ final HashMap<String,String> keyValues = new HashMap<>();
keyValues.put(HARDWARE_KEY, "grouper");
keyValues.put(MODEL_KEY, "Nexus 7");
keyValues.put(MANUFACTURER_KEY, "asus");
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
new file mode 100644
index 000000000..fdde34251
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+@SmallTest
+public class SpacebarLanguagetUtilsTests extends AndroidTestCase {
+ // All input method subtypes of LatinIME.
+ private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>();
+
+ private RichInputMethodManager mRichImm;
+ private Resources mRes;
+
+ InputMethodSubtype EN_US;
+ InputMethodSubtype EN_GB;
+ InputMethodSubtype ES_US;
+ InputMethodSubtype FR;
+ InputMethodSubtype FR_CA;
+ InputMethodSubtype FR_CH;
+ InputMethodSubtype DE;
+ InputMethodSubtype DE_CH;
+ InputMethodSubtype ZZ;
+ InputMethodSubtype DE_QWERTY;
+ InputMethodSubtype FR_QWERTZ;
+ InputMethodSubtype EN_US_AZERTY;
+ InputMethodSubtype EN_UK_DVORAK;
+ InputMethodSubtype ES_US_COLEMAK;
+ InputMethodSubtype ZZ_AZERTY;
+ InputMethodSubtype ZZ_PC;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Context context = getContext();
+ RichInputMethodManager.init(context);
+ mRichImm = RichInputMethodManager.getInstance();
+ mRes = context.getResources();
+ SubtypeLocaleUtils.init(context);
+
+ final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme();
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int index = 0; index < subtypeCount; index++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ mSubtypesList.add(subtype);
+ }
+
+ EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.US.toString(), "qwerty");
+ EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.UK.toString(), "qwerty");
+ ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "es_US", "spanish");
+ FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.FRENCH.toString(), "azerty");
+ FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.CANADA_FRENCH.toString(), "qwerty");
+ FR_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "fr_CH", "swiss");
+ DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.GERMAN.toString(), "qwertz");
+ DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "de_CH", "swiss");
+ ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
+ DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.GERMAN.toString(), "qwerty");
+ FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.FRENCH.toString(), "qwertz");
+ EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.US.toString(), "azerty");
+ EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.UK.toString(), "dvorak");
+ ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ "es_US", "colemak");
+ ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+ ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty");
+ }
+
+ public void testAllFullDisplayNameForSpacebar() {
+ for (final InputMethodSubtype subtype : mSubtypesList) {
+ final String subtypeName = SubtypeLocaleUtils
+ .getSubtypeDisplayNameInSystemLocale(subtype);
+ final String spacebarText = SpacebarLanguageUtils.getFullDisplayName(subtype);
+ final String languageName = SubtypeLocaleUtils
+ .getSubtypeLocaleDisplayName(subtype.getLocale());
+ if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+ assertFalse(subtypeName, spacebarText.contains(languageName));
+ } else {
+ assertTrue(subtypeName, spacebarText.contains(languageName));
+ }
+ }
+ }
+
+ public void testAllMiddleDisplayNameForSpacebar() {
+ for (final InputMethodSubtype subtype : mSubtypesList) {
+ final String subtypeName = SubtypeLocaleUtils
+ .getSubtypeDisplayNameInSystemLocale(subtype);
+ final String spacebarText = SpacebarLanguageUtils.getMiddleDisplayName(subtype);
+ if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+ assertEquals(subtypeName,
+ SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype), spacebarText);
+ } else {
+ final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ assertEquals(subtypeName,
+ SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()),
+ spacebarText);
+ }
+ }
+ }
+
+ // InputMethodSubtype's display name for spacebar text in its locale.
+ // isAdditionalSubtype (T=true, F=false)
+ // locale layout | Middle Full
+ // ------ ------- - --------- ----------------------
+ // en_US qwerty F English English (US) exception
+ // en_GB qwerty F English English (UK) exception
+ // es_US spanish F Español Español (EE.UU.) exception
+ // fr azerty F Français Français
+ // fr_CA qwerty F Français Français (Canada)
+ // fr_CH swiss F Français Français (Suisse)
+ // de qwertz F Deutsch Deutsch
+ // de_CH swiss F Deutsch Deutsch (Schweiz)
+ // zz qwerty F QWERTY QWERTY
+ // fr qwertz T Français Français
+ // de qwerty T Deutsch Deutsch
+ // en_US azerty T English English (US)
+ // zz azerty T AZERTY AZERTY
+
+ private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() {
+ @Override
+ protected Void job(final Resources res) {
+ assertEquals("en_US", "English (US)",
+ SpacebarLanguageUtils.getFullDisplayName(EN_US));
+ assertEquals("en_GB", "English (UK)",
+ SpacebarLanguageUtils.getFullDisplayName(EN_GB));
+ assertEquals("es_US", "Español (EE.UU.)",
+ SpacebarLanguageUtils.getFullDisplayName(ES_US));
+ assertEquals("fr", "Français",
+ SpacebarLanguageUtils.getFullDisplayName(FR));
+ assertEquals("fr_CA", "Français (Canada)",
+ SpacebarLanguageUtils.getFullDisplayName(FR_CA));
+ assertEquals("fr_CH", "Français (Suisse)",
+ SpacebarLanguageUtils.getFullDisplayName(FR_CH));
+ assertEquals("de", "Deutsch",
+ SpacebarLanguageUtils.getFullDisplayName(DE));
+ assertEquals("de_CH", "Deutsch (Schweiz)",
+ SpacebarLanguageUtils.getFullDisplayName(DE_CH));
+ assertEquals("zz", "QWERTY",
+ SpacebarLanguageUtils.getFullDisplayName(ZZ));
+
+ assertEquals("en_US", "English",
+ SpacebarLanguageUtils.getMiddleDisplayName(EN_US));
+ assertEquals("en_GB", "English",
+ SpacebarLanguageUtils.getMiddleDisplayName(EN_GB));
+ assertEquals("es_US", "Español",
+ SpacebarLanguageUtils.getMiddleDisplayName(ES_US));
+ assertEquals("fr", "Français",
+ SpacebarLanguageUtils.getMiddleDisplayName(FR));
+ assertEquals("fr_CA", "Français",
+ SpacebarLanguageUtils.getMiddleDisplayName(FR_CA));
+ assertEquals("fr_CH", "Français",
+ SpacebarLanguageUtils.getMiddleDisplayName(FR_CH));
+ assertEquals("de", "Deutsch",
+ SpacebarLanguageUtils.getMiddleDisplayName(DE));
+ assertEquals("de_CH", "Deutsch",
+ SpacebarLanguageUtils.getMiddleDisplayName(DE_CH));
+ assertEquals("zz", "QWERTY",
+ SpacebarLanguageUtils.getMiddleDisplayName(ZZ));
+ return null;
+ }
+ };
+
+ private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() {
+ @Override
+ protected Void job(final Resources res) {
+ assertEquals("fr qwertz", "Français",
+ SpacebarLanguageUtils.getFullDisplayName(FR_QWERTZ));
+ assertEquals("de qwerty", "Deutsch",
+ SpacebarLanguageUtils.getFullDisplayName(DE_QWERTY));
+ assertEquals("en_US azerty", "English (US)",
+ SpacebarLanguageUtils.getFullDisplayName(EN_US_AZERTY));
+ assertEquals("en_UK dvorak", "English (UK)",
+ SpacebarLanguageUtils.getFullDisplayName(EN_UK_DVORAK));
+ assertEquals("es_US colemak", "Español (EE.UU.)",
+ SpacebarLanguageUtils.getFullDisplayName(ES_US_COLEMAK));
+ assertEquals("zz azerty", "AZERTY",
+ SpacebarLanguageUtils.getFullDisplayName(ZZ_AZERTY));
+ assertEquals("zz pc", "PC",
+ SpacebarLanguageUtils.getFullDisplayName(ZZ_PC));
+
+ assertEquals("fr qwertz", "Français",
+ SpacebarLanguageUtils.getMiddleDisplayName(FR_QWERTZ));
+ assertEquals("de qwerty", "Deutsch",
+ SpacebarLanguageUtils.getMiddleDisplayName(DE_QWERTY));
+ assertEquals("en_US azerty", "English",
+ SpacebarLanguageUtils.getMiddleDisplayName(EN_US_AZERTY));
+ assertEquals("en_UK dvorak", "English",
+ SpacebarLanguageUtils.getMiddleDisplayName(EN_UK_DVORAK));
+ assertEquals("es_US colemak", "Español",
+ SpacebarLanguageUtils.getMiddleDisplayName(ES_US_COLEMAK));
+ assertEquals("zz azerty", "AZERTY",
+ SpacebarLanguageUtils.getMiddleDisplayName(ZZ_AZERTY));
+ assertEquals("zz pc", "PC",
+ SpacebarLanguageUtils.getMiddleDisplayName(ZZ_PC));
+ return null;
+ }
+ };
+
+ public void testPredefinedSubtypesForSpacebarInEnglish() {
+ testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
+ }
+
+ public void testAdditionalSubtypeForSpacebarInEnglish() {
+ testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
+ }
+
+ public void testPredefinedSubtypesForSpacebarInFrench() {
+ testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
+ }
+
+ public void testAdditionalSubtypeForSpacebarInFrench() {
+ testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
new file mode 100644
index 000000000..637ae10ee
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+public class StringAndJsonUtilsTests extends AndroidTestCase {
+ public void testContainsInArray() {
+ assertFalse("empty array", StringUtils.containsInArray("key", new String[0]));
+ assertFalse("not in 1 element", StringUtils.containsInArray("key", new String[] {
+ "key1"
+ }));
+ assertFalse("not in 2 elements", StringUtils.containsInArray("key", new String[] {
+ "key1", "key2"
+ }));
+
+ assertTrue("in 1 element", StringUtils.containsInArray("key", new String[] {
+ "key"
+ }));
+ assertTrue("in 2 elements", StringUtils.containsInArray("key", new String[] {
+ "key1", "key"
+ }));
+ }
+
+ public void testContainsInCommaSplittableText() {
+ assertFalse("null", StringUtils.containsInCommaSplittableText("key", null));
+ assertFalse("empty", StringUtils.containsInCommaSplittableText("key", ""));
+ assertFalse("not in 1 element",
+ StringUtils.containsInCommaSplittableText("key", "key1"));
+ assertFalse("not in 2 elements",
+ StringUtils.containsInCommaSplittableText("key", "key1,key2"));
+
+ assertTrue("in 1 element", StringUtils.containsInCommaSplittableText("key", "key"));
+ assertTrue("in 2 elements", StringUtils.containsInCommaSplittableText("key", "key1,key"));
+ }
+
+ public void testRemoveFromCommaSplittableTextIfExists() {
+ assertEquals("null", "", StringUtils.removeFromCommaSplittableTextIfExists("key", null));
+ assertEquals("empty", "", StringUtils.removeFromCommaSplittableTextIfExists("key", ""));
+
+ assertEquals("not in 1 element", "key1",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1"));
+ assertEquals("not in 2 elements", "key1,key2",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key2"));
+
+ assertEquals("in 1 element", "",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key"));
+ assertEquals("in 2 elements at position 1", "key2",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key2"));
+ assertEquals("in 2 elements at position 2", "key1",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key"));
+ assertEquals("in 3 elements at position 2", "key1,key3",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key,key3"));
+
+ assertEquals("in 3 elements at position 1,2,3", "",
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key,key"));
+ assertEquals("in 5 elements at position 2,4", "key1,key3,key5",
+ StringUtils.removeFromCommaSplittableTextIfExists(
+ "key", "key1,key,key3,key,key5"));
+ }
+
+
+ public void testCapitalizeFirstCodePoint() {
+ assertEquals("SSaa",
+ StringUtils.capitalizeFirstCodePoint("ßaa", Locale.GERMAN));
+ assertEquals("Aßa",
+ StringUtils.capitalizeFirstCodePoint("aßa", Locale.GERMAN));
+ assertEquals("Iab",
+ StringUtils.capitalizeFirstCodePoint("iab", Locale.ENGLISH));
+ assertEquals("CAmElCaSe",
+ StringUtils.capitalizeFirstCodePoint("cAmElCaSe", Locale.ENGLISH));
+ assertEquals("İab",
+ StringUtils.capitalizeFirstCodePoint("iab", new Locale("tr")));
+ assertEquals("AİB",
+ StringUtils.capitalizeFirstCodePoint("AİB", new Locale("tr")));
+ assertEquals("A",
+ StringUtils.capitalizeFirstCodePoint("a", Locale.ENGLISH));
+ assertEquals("A",
+ StringUtils.capitalizeFirstCodePoint("A", Locale.ENGLISH));
+ }
+
+ public void testCapitalizeFirstAndDowncaseRest() {
+ assertEquals("SSaa",
+ StringUtils.capitalizeFirstAndDowncaseRest("ßaa", Locale.GERMAN));
+ assertEquals("Aßa",
+ StringUtils.capitalizeFirstAndDowncaseRest("aßa", Locale.GERMAN));
+ assertEquals("Iab",
+ StringUtils.capitalizeFirstAndDowncaseRest("iab", Locale.ENGLISH));
+ assertEquals("Camelcase",
+ StringUtils.capitalizeFirstAndDowncaseRest("cAmElCaSe", Locale.ENGLISH));
+ assertEquals("İab",
+ StringUtils.capitalizeFirstAndDowncaseRest("iab", new Locale("tr")));
+ assertEquals("Aib",
+ StringUtils.capitalizeFirstAndDowncaseRest("AİB", new Locale("tr")));
+ assertEquals("A",
+ StringUtils.capitalizeFirstAndDowncaseRest("a", Locale.ENGLISH));
+ assertEquals("A",
+ StringUtils.capitalizeFirstAndDowncaseRest("A", Locale.ENGLISH));
+ }
+
+ public void testGetCapitalizationType() {
+ assertEquals(StringUtils.CAPITALIZE_NONE,
+ StringUtils.getCapitalizationType("capitalize"));
+ assertEquals(StringUtils.CAPITALIZE_NONE,
+ StringUtils.getCapitalizationType("cApITalize"));
+ assertEquals(StringUtils.CAPITALIZE_NONE,
+ StringUtils.getCapitalizationType("capitalizE"));
+ assertEquals(StringUtils.CAPITALIZE_NONE,
+ StringUtils.getCapitalizationType("__c a piu$@tali56ze"));
+ assertEquals(StringUtils.CAPITALIZE_FIRST,
+ StringUtils.getCapitalizationType("A__c a piu$@tali56ze"));
+ assertEquals(StringUtils.CAPITALIZE_FIRST,
+ StringUtils.getCapitalizationType("Capitalize"));
+ assertEquals(StringUtils.CAPITALIZE_FIRST,
+ StringUtils.getCapitalizationType(" Capitalize"));
+ assertEquals(StringUtils.CAPITALIZE_ALL,
+ StringUtils.getCapitalizationType("CAPITALIZE"));
+ assertEquals(StringUtils.CAPITALIZE_ALL,
+ StringUtils.getCapitalizationType(" PI26LIE"));
+ assertEquals(StringUtils.CAPITALIZE_NONE,
+ StringUtils.getCapitalizationType(""));
+ }
+
+ public void testIsIdenticalAfterUpcaseIsIdenticalAfterDowncase() {
+ assertFalse(StringUtils.isIdenticalAfterUpcase("capitalize"));
+ assertTrue(StringUtils.isIdenticalAfterDowncase("capitalize"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase("cApITalize"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase("cApITalize"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase("capitalizE"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase("capitalizE"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase("__c a piu$@tali56ze"));
+ assertTrue(StringUtils.isIdenticalAfterDowncase("__c a piu$@tali56ze"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase("A__c a piu$@tali56ze"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase("A__c a piu$@tali56ze"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase("Capitalize"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase("Capitalize"));
+ assertFalse(StringUtils.isIdenticalAfterUpcase(" Capitalize"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase(" Capitalize"));
+ assertTrue(StringUtils.isIdenticalAfterUpcase("CAPITALIZE"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase("CAPITALIZE"));
+ assertTrue(StringUtils.isIdenticalAfterUpcase(" PI26LIE"));
+ assertFalse(StringUtils.isIdenticalAfterDowncase(" PI26LIE"));
+ assertTrue(StringUtils.isIdenticalAfterUpcase(""));
+ assertTrue(StringUtils.isIdenticalAfterDowncase(""));
+ }
+
+ private static void checkCapitalize(final String src, final String dst,
+ final int[] sortedSeparators, final Locale locale) {
+ assertEquals(dst, StringUtils.capitalizeEachWord(src, sortedSeparators, locale));
+ assert(src.equals(dst)
+ == StringUtils.isIdenticalAfterCapitalizeEachWord(src, sortedSeparators));
+ }
+
+ private static final int[] SPACE = { Constants.CODE_SPACE };
+ private static final int[] SPACE_PERIOD = StringUtils.toSortedCodePointArray(" .");
+ private static final int[] SENTENCE_SEPARATORS =
+ StringUtils.toSortedCodePointArray(" \n.!?*()&");
+ private static final int[] WORD_SEPARATORS = StringUtils.toSortedCodePointArray(" \n.!?*,();&");
+
+ public void testCapitalizeEachWord() {
+ checkCapitalize("", "", SPACE, Locale.ENGLISH);
+ checkCapitalize("test", "Test", SPACE, Locale.ENGLISH);
+ checkCapitalize(" test", " Test", SPACE, Locale.ENGLISH);
+ checkCapitalize("Test", "Test", SPACE, Locale.ENGLISH);
+ checkCapitalize(" Test", " Test", SPACE, Locale.ENGLISH);
+ checkCapitalize(".Test", ".test", SPACE, Locale.ENGLISH);
+ checkCapitalize(".Test", ".Test", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("test and retest", "Test And Retest", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test and retest", "Test And Retest", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test And Retest", "Test And Retest", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test And.Retest ", "Test And.Retest ", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test And.retest ", "Test And.Retest ", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test And.retest ", "Test And.retest ", SPACE, Locale.ENGLISH);
+ checkCapitalize("Test And.Retest ", "Test And.retest ", SPACE, Locale.ENGLISH);
+ checkCapitalize("test and ietest", "Test And İetest", SPACE_PERIOD, new Locale("tr"));
+ checkCapitalize("test and ietest", "Test And Ietest", SPACE_PERIOD, Locale.ENGLISH);
+ checkCapitalize("Test&Retest", "Test&Retest", SENTENCE_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("Test&retest", "Test&Retest", SENTENCE_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("test&Retest", "Test&Retest", SENTENCE_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("rest\nrecreation! And in the end...",
+ "Rest\nRecreation! And In The End...", WORD_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("lorem ipsum dolor sit amet", "Lorem Ipsum Dolor Sit Amet",
+ WORD_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("Lorem!Ipsum (Dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
+ WORD_SEPARATORS, Locale.ENGLISH);
+ checkCapitalize("Lorem!Ipsum (dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
+ WORD_SEPARATORS, Locale.ENGLISH);
+ }
+
+ public void testLooksLikeURL() {
+ assertTrue(StringUtils.lastPartLooksLikeURL("http://www.google."));
+ assertFalse(StringUtils.lastPartLooksLikeURL("word wo"));
+ assertTrue(StringUtils.lastPartLooksLikeURL("/etc/foo"));
+ assertFalse(StringUtils.lastPartLooksLikeURL("left/right"));
+ assertTrue(StringUtils.lastPartLooksLikeURL("www.goo"));
+ assertTrue(StringUtils.lastPartLooksLikeURL("www."));
+ assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A"));
+ assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A."));
+ assertTrue(StringUtils.lastPartLooksLikeURL("rtsp://foo."));
+ assertTrue(StringUtils.lastPartLooksLikeURL("://"));
+ assertFalse(StringUtils.lastPartLooksLikeURL("abc/"));
+ assertTrue(StringUtils.lastPartLooksLikeURL("abc.def/ghi"));
+ assertFalse(StringUtils.lastPartLooksLikeURL("abc.def"));
+ // TODO: ideally this would not look like a URL, but to keep down the complexity of the
+ // code for now True is acceptable.
+ assertTrue(StringUtils.lastPartLooksLikeURL("abc./def"));
+ // TODO: ideally this would not look like a URL, but to keep down the complexity of the
+ // code for now True is acceptable.
+ assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def"));
+ }
+
+ public void testHexStringUtils() {
+ final byte[] bytes = new byte[] { (byte)0x01, (byte)0x11, (byte)0x22, (byte)0x33,
+ (byte)0x55, (byte)0x88, (byte)0xEE };
+ final String bytesStr = StringUtils.byteArrayToHexString(bytes);
+ final byte[] bytes2 = StringUtils.hexStringToByteArray(bytesStr);
+ for (int i = 0; i < bytes.length; ++i) {
+ assertTrue(bytes[i] == bytes2[i]);
+ }
+ final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2);
+ assertTrue(bytesStr.equals(bytesStr2));
+ }
+
+ public void testJsonUtils() {
+ final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
+ final List<Object> objArray = Arrays.asList(objs);
+ final String str = JsonUtils.listToJsonStr(objArray);
+ final List<Object> newObjArray = JsonUtils.jsonStrToList(str);
+ for (int i = 0; i < objs.length; ++i) {
+ assertEquals(objs[i], newObjArray.get(i));
+ }
+ }
+
+ public void testToCodePointArray() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx";
+ final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h',
+ 0, 0x2002, 0x2003, 0x3000, 'x', 'x'};
+ final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length());
+ assertEquals("toCodePointArray, size matches", codePointArray.length,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < EXPECTED_RESULT.length; ++i) {
+ assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]);
+ }
+ }
+
+ public void testCopyCodePointsAndReturnCodePointCount() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx";
+ final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7,
+ 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+ final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7,
+ 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+
+ int[] codePointArray = new int[50];
+ int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount,
+ EXPECTED_RESULT_DOWNCASE.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT_DOWNCASE[i]);
+ }
+
+ final int JAVA_CHAR_COUNT = 8;
+ final int CODEPOINT_COUNT = 7;
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ CODEPOINT_COUNT);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ boolean exceptionHappened = false;
+ codePointArray = new int[5];
+ try {
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ exceptionHappened = true;
+ }
+ assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small",
+ exceptionHappened);
+ }
+
+ public void testGetTrailingSingleQuotesCount() {
+ assertEquals(0, StringUtils.getTrailingSingleQuotesCount(""));
+ assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'"));
+ assertEquals(5, StringUtils.getTrailingSingleQuotesCount("'''''"));
+ assertEquals(0, StringUtils.getTrailingSingleQuotesCount("a"));
+ assertEquals(0, StringUtils.getTrailingSingleQuotesCount("'this"));
+ assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'word'"));
+ assertEquals(0, StringUtils.getTrailingSingleQuotesCount("I'm"));
+ }
+
+ private static void assertSpanCount(final int expectedCount, final CharSequence cs) {
+ final int actualCount;
+ if (cs instanceof Spanned) {
+ final Spanned spanned = (Spanned) cs;
+ actualCount = spanned.getSpans(0, spanned.length(), Object.class).length;
+ } else {
+ actualCount = 0;
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertSpan(final CharSequence cs, final Object expectedSpan,
+ final int expectedStart, final int expectedEnd, final int expectedFlags) {
+ assertTrue(cs instanceof Spanned);
+ final Spanned spanned = (Spanned) cs;
+ final Object[] actualSpans = spanned.getSpans(0, spanned.length(), Object.class);
+ for (Object actualSpan : actualSpans) {
+ if (actualSpan == expectedSpan) {
+ final int actualStart = spanned.getSpanStart(actualSpan);
+ final int actualEnd = spanned.getSpanEnd(actualSpan);
+ final int actualFlags = spanned.getSpanFlags(actualSpan);
+ assertEquals(expectedStart, actualStart);
+ assertEquals(expectedEnd, actualEnd);
+ assertEquals(expectedFlags, actualFlags);
+ return;
+ }
+ }
+ assertTrue(false);
+ }
+
+ public void testSplitCharSequenceWithSpan() {
+ // text: " a bcd efg hij "
+ // span1: ^^^^^^^
+ // span2: ^^^^^
+ // span3: ^
+ final SpannableString spannableString = new SpannableString(" a bcd efg hij ");
+ final Object span1 = new Object();
+ final Object span2 = new Object();
+ final Object span3 = new Object();
+ final int SPAN1_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+ final int SPAN2_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
+ final int SPAN3_FLAGS = Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+ spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS);
+ spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS);
+ spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS);
+ final CharSequence[] charSequencesFromSpanned = StringUtils.split(
+ spannableString, " ", true /* preserveTrailingEmptySegmengs */);
+ final CharSequence[] charSequencesFromString = StringUtils.split(
+ spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */);
+
+
+ assertEquals(7, charSequencesFromString.length);
+ assertEquals(7, charSequencesFromSpanned.length);
+
+ // text: ""
+ // span1: ^
+ // span2: ^
+ // span3:
+ assertEquals("", charSequencesFromString[0].toString());
+ assertSpanCount(0, charSequencesFromString[0]);
+ assertEquals("", charSequencesFromSpanned[0].toString());
+ assertSpanCount(2, charSequencesFromSpanned[0]);
+ assertSpan(charSequencesFromSpanned[0], span1, 0, 0, SPAN1_FLAGS);
+ assertSpan(charSequencesFromSpanned[0], span2, 0, 0, SPAN2_FLAGS);
+
+ // text: "a"
+ // span1: ^
+ // span2: ^
+ // span3:
+ assertEquals("a", charSequencesFromString[1].toString());
+ assertSpanCount(0, charSequencesFromString[1]);
+ assertEquals("a", charSequencesFromSpanned[1].toString());
+ assertSpanCount(2, charSequencesFromSpanned[1]);
+ assertSpan(charSequencesFromSpanned[1], span1, 0, 1, SPAN1_FLAGS);
+ assertSpan(charSequencesFromSpanned[1], span2, 0, 1, SPAN2_FLAGS);
+
+ // text: "bcd"
+ // span1: ^^^
+ // span2: ^^
+ // span3:
+ assertEquals("bcd", charSequencesFromString[2].toString());
+ assertSpanCount(0, charSequencesFromString[2]);
+ assertEquals("bcd", charSequencesFromSpanned[2].toString());
+ assertSpanCount(2, charSequencesFromSpanned[2]);
+ assertSpan(charSequencesFromSpanned[2], span1, 0, 3, SPAN1_FLAGS);
+ assertSpan(charSequencesFromSpanned[2], span2, 0, 2, SPAN2_FLAGS);
+
+ // text: "efg"
+ // span1:
+ // span2:
+ // span3:
+ assertEquals("efg", charSequencesFromString[3].toString());
+ assertSpanCount(0, charSequencesFromString[3]);
+ assertEquals("efg", charSequencesFromSpanned[3].toString());
+ assertSpanCount(0, charSequencesFromSpanned[3]);
+
+ // text: "hij"
+ // span1:
+ // span2:
+ // span3: ^
+ assertEquals("hij", charSequencesFromString[4].toString());
+ assertSpanCount(0, charSequencesFromString[4]);
+ assertEquals("hij", charSequencesFromSpanned[4].toString());
+ assertSpanCount(1, charSequencesFromSpanned[4]);
+ assertSpan(charSequencesFromSpanned[4], span3, 1, 2, SPAN3_FLAGS);
+
+ // text: ""
+ // span1:
+ // span2:
+ // span3:
+ assertEquals("", charSequencesFromString[5].toString());
+ assertSpanCount(0, charSequencesFromString[5]);
+ assertEquals("", charSequencesFromSpanned[5].toString());
+ assertSpanCount(0, charSequencesFromSpanned[5]);
+
+ // text: ""
+ // span1:
+ // span2:
+ // span3:
+ assertEquals("", charSequencesFromString[6].toString());
+ assertSpanCount(0, charSequencesFromString[6]);
+ assertEquals("", charSequencesFromSpanned[6].toString());
+ assertSpanCount(0, charSequencesFromSpanned[6]);
+ }
+
+ public void testSplitCharSequencePreserveTrailingEmptySegmengs() {
+ assertEquals(1, StringUtils.split("", " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(1, StringUtils.split(new SpannedString(""), " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(1, StringUtils.split("", " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(1, StringUtils.split(new SpannedString(""), " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(0, StringUtils.split(" ", " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(0, StringUtils.split(new SpannedString(" "), " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(2, StringUtils.split(" ", " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(2, StringUtils.split(new SpannedString(" "), " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(3, StringUtils.split("a b c ", " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(3, StringUtils.split(new SpannedString("a b c "), " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(5, StringUtils.split("a b c ", " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(5, StringUtils.split(new SpannedString("a b c "), " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(6, StringUtils.split("a b ", " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(6, StringUtils.split(new SpannedString("a b "), " ",
+ false /* preserveTrailingEmptySegmengs */).length);
+
+ assertEquals(7, StringUtils.split("a b ", " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+ assertEquals(7, StringUtils.split(new SpannedString("a b "), " ",
+ true /* preserveTrailingEmptySegmengs */).length);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
deleted file mode 100644
index 4e396a1cf..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-@SmallTest
-public class StringUtilsTests extends AndroidTestCase {
- public void testContainsInArray() {
- assertFalse("empty array", StringUtils.containsInArray("key", new String[0]));
- assertFalse("not in 1 element", StringUtils.containsInArray("key", new String[] {
- "key1"
- }));
- assertFalse("not in 2 elements", StringUtils.containsInArray("key", new String[] {
- "key1", "key2"
- }));
-
- assertTrue("in 1 element", StringUtils.containsInArray("key", new String[] {
- "key"
- }));
- assertTrue("in 2 elements", StringUtils.containsInArray("key", new String[] {
- "key1", "key"
- }));
- }
-
- public void testContainsInExtraValues() {
- assertFalse("null", StringUtils.containsInCommaSplittableText("key", null));
- assertFalse("empty", StringUtils.containsInCommaSplittableText("key", ""));
- assertFalse("not in 1 element",
- StringUtils.containsInCommaSplittableText("key", "key1"));
- assertFalse("not in 2 elements",
- StringUtils.containsInCommaSplittableText("key", "key1,key2"));
-
- assertTrue("in 1 element", StringUtils.containsInCommaSplittableText("key", "key"));
- assertTrue("in 2 elements", StringUtils.containsInCommaSplittableText("key", "key1,key"));
- }
-
- public void testAppendToExtraValuesIfNotExists() {
- assertEquals("null", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", null));
- assertEquals("empty", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", ""));
-
- assertEquals("not in 1 element", "key1,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1"));
- assertEquals("not in 2 elements", "key1,key2,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key2"));
-
- assertEquals("in 1 element", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key"));
- assertEquals("in 2 elements at position 1", "key,key2",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key,key2"));
- assertEquals("in 2 elements at position 2", "key1,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key"));
- assertEquals("in 3 elements at position 2", "key1,key,key3",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key,key3"));
- }
-
- public void testRemoveFromExtraValuesIfExists() {
- assertEquals("null", "", StringUtils.removeFromCommaSplittableTextIfExists("key", null));
- assertEquals("empty", "", StringUtils.removeFromCommaSplittableTextIfExists("key", ""));
-
- assertEquals("not in 1 element", "key1",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key1"));
- assertEquals("not in 2 elements", "key1,key2",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key2"));
-
- assertEquals("in 1 element", "",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key"));
- assertEquals("in 2 elements at position 1", "key2",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key2"));
- assertEquals("in 2 elements at position 2", "key1",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key"));
- assertEquals("in 3 elements at position 2", "key1,key3",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key,key3"));
-
- assertEquals("in 3 elements at position 1,2,3", "",
- StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key,key"));
- assertEquals("in 5 elements at position 2,4", "key1,key3,key5",
- StringUtils.removeFromCommaSplittableTextIfExists(
- "key", "key1,key,key3,key,key5"));
- }
-
-
- public void testCapitalizeFirstCodePoint() {
- assertEquals("SSaa",
- StringUtils.capitalizeFirstCodePoint("ßaa", Locale.GERMAN));
- assertEquals("Aßa",
- StringUtils.capitalizeFirstCodePoint("aßa", Locale.GERMAN));
- assertEquals("Iab",
- StringUtils.capitalizeFirstCodePoint("iab", Locale.ENGLISH));
- assertEquals("CAmElCaSe",
- StringUtils.capitalizeFirstCodePoint("cAmElCaSe", Locale.ENGLISH));
- assertEquals("İab",
- StringUtils.capitalizeFirstCodePoint("iab", new Locale("tr")));
- assertEquals("AİB",
- StringUtils.capitalizeFirstCodePoint("AİB", new Locale("tr")));
- assertEquals("A",
- StringUtils.capitalizeFirstCodePoint("a", Locale.ENGLISH));
- assertEquals("A",
- StringUtils.capitalizeFirstCodePoint("A", Locale.ENGLISH));
- }
-
- public void testCapitalizeFirstAndDowncaseRest() {
- assertEquals("SSaa",
- StringUtils.capitalizeFirstAndDowncaseRest("ßaa", Locale.GERMAN));
- assertEquals("Aßa",
- StringUtils.capitalizeFirstAndDowncaseRest("aßa", Locale.GERMAN));
- assertEquals("Iab",
- StringUtils.capitalizeFirstAndDowncaseRest("iab", Locale.ENGLISH));
- assertEquals("Camelcase",
- StringUtils.capitalizeFirstAndDowncaseRest("cAmElCaSe", Locale.ENGLISH));
- assertEquals("İab",
- StringUtils.capitalizeFirstAndDowncaseRest("iab", new Locale("tr")));
- assertEquals("Aib",
- StringUtils.capitalizeFirstAndDowncaseRest("AİB", new Locale("tr")));
- assertEquals("A",
- StringUtils.capitalizeFirstAndDowncaseRest("a", Locale.ENGLISH));
- assertEquals("A",
- StringUtils.capitalizeFirstAndDowncaseRest("A", Locale.ENGLISH));
- }
-
- public void testGetCapitalizationType() {
- assertEquals(StringUtils.CAPITALIZE_NONE,
- StringUtils.getCapitalizationType("capitalize"));
- assertEquals(StringUtils.CAPITALIZE_NONE,
- StringUtils.getCapitalizationType("cApITalize"));
- assertEquals(StringUtils.CAPITALIZE_NONE,
- StringUtils.getCapitalizationType("capitalizE"));
- assertEquals(StringUtils.CAPITALIZE_NONE,
- StringUtils.getCapitalizationType("__c a piu$@tali56ze"));
- assertEquals(StringUtils.CAPITALIZE_FIRST,
- StringUtils.getCapitalizationType("A__c a piu$@tali56ze"));
- assertEquals(StringUtils.CAPITALIZE_FIRST,
- StringUtils.getCapitalizationType("Capitalize"));
- assertEquals(StringUtils.CAPITALIZE_FIRST,
- StringUtils.getCapitalizationType(" Capitalize"));
- assertEquals(StringUtils.CAPITALIZE_ALL,
- StringUtils.getCapitalizationType("CAPITALIZE"));
- assertEquals(StringUtils.CAPITALIZE_ALL,
- StringUtils.getCapitalizationType(" PI26LIE"));
- assertEquals(StringUtils.CAPITALIZE_NONE,
- StringUtils.getCapitalizationType(""));
- }
-
- public void testIsIdenticalAfterUpcaseIsIdenticalAfterDowncase() {
- assertFalse(StringUtils.isIdenticalAfterUpcase("capitalize"));
- assertTrue(StringUtils.isIdenticalAfterDowncase("capitalize"));
- assertFalse(StringUtils.isIdenticalAfterUpcase("cApITalize"));
- assertFalse(StringUtils.isIdenticalAfterDowncase("cApITalize"));
- assertFalse(StringUtils.isIdenticalAfterUpcase("capitalizE"));
- assertFalse(StringUtils.isIdenticalAfterDowncase("capitalizE"));
- assertFalse(StringUtils.isIdenticalAfterUpcase("__c a piu$@tali56ze"));
- assertTrue(StringUtils.isIdenticalAfterDowncase("__c a piu$@tali56ze"));
- assertFalse(StringUtils.isIdenticalAfterUpcase("A__c a piu$@tali56ze"));
- assertFalse(StringUtils.isIdenticalAfterDowncase("A__c a piu$@tali56ze"));
- assertFalse(StringUtils.isIdenticalAfterUpcase("Capitalize"));
- assertFalse(StringUtils.isIdenticalAfterDowncase("Capitalize"));
- assertFalse(StringUtils.isIdenticalAfterUpcase(" Capitalize"));
- assertFalse(StringUtils.isIdenticalAfterDowncase(" Capitalize"));
- assertTrue(StringUtils.isIdenticalAfterUpcase("CAPITALIZE"));
- assertFalse(StringUtils.isIdenticalAfterDowncase("CAPITALIZE"));
- assertTrue(StringUtils.isIdenticalAfterUpcase(" PI26LIE"));
- assertFalse(StringUtils.isIdenticalAfterDowncase(" PI26LIE"));
- assertTrue(StringUtils.isIdenticalAfterUpcase(""));
- assertTrue(StringUtils.isIdenticalAfterDowncase(""));
- }
-
- public void testLooksValidForDictionaryInsertion() {
- final SettingsValues settings =
- SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
- assertTrue(StringUtils.looksValidForDictionaryInsertion("aochaueo", settings));
- assertFalse(StringUtils.looksValidForDictionaryInsertion("", settings));
- assertTrue(StringUtils.looksValidForDictionaryInsertion("ao-ch'aueo", settings));
- assertFalse(StringUtils.looksValidForDictionaryInsertion("2908743256", settings));
- assertTrue(StringUtils.looksValidForDictionaryInsertion("31aochaueo", settings));
- assertFalse(StringUtils.looksValidForDictionaryInsertion("akeo raeoch oerch .", settings));
- assertFalse(StringUtils.looksValidForDictionaryInsertion("!!!", settings));
- }
-
- private static void checkCapitalize(final String src, final String dst, final String separators,
- final Locale locale) {
- assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));
- assert(src.equals(dst)
- == StringUtils.isIdenticalAfterCapitalizeEachWord(src, separators));
- }
-
- public void testCapitalizeEachWord() {
- checkCapitalize("", "", " ", Locale.ENGLISH);
- checkCapitalize("test", "Test", " ", Locale.ENGLISH);
- checkCapitalize(" test", " Test", " ", Locale.ENGLISH);
- checkCapitalize("Test", "Test", " ", Locale.ENGLISH);
- checkCapitalize(" Test", " Test", " ", Locale.ENGLISH);
- checkCapitalize(".Test", ".test", " ", Locale.ENGLISH);
- checkCapitalize(".Test", ".Test", " .", Locale.ENGLISH);
- checkCapitalize(".Test", ".Test", ". ", Locale.ENGLISH);
- checkCapitalize("test and retest", "Test And Retest", " .", Locale.ENGLISH);
- checkCapitalize("Test and retest", "Test And Retest", " .", Locale.ENGLISH);
- checkCapitalize("Test And Retest", "Test And Retest", " .", Locale.ENGLISH);
- checkCapitalize("Test And.Retest ", "Test And.Retest ", " .", Locale.ENGLISH);
- checkCapitalize("Test And.retest ", "Test And.Retest ", " .", Locale.ENGLISH);
- checkCapitalize("Test And.retest ", "Test And.retest ", " ", Locale.ENGLISH);
- checkCapitalize("Test And.Retest ", "Test And.retest ", " ", Locale.ENGLISH);
- checkCapitalize("test and ietest", "Test And İetest", " .", new Locale("tr"));
- checkCapitalize("test and ietest", "Test And Ietest", " .", Locale.ENGLISH);
- checkCapitalize("Test&Retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
- checkCapitalize("Test&retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
- checkCapitalize("test&Retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
- checkCapitalize("rest\nrecreation! And in the end...",
- "Rest\nRecreation! And In The End...", " \n.!?*,();&", Locale.ENGLISH);
- checkCapitalize("lorem ipsum dolor sit amet", "Lorem Ipsum Dolor Sit Amet",
- " \n.,!?*()&;", Locale.ENGLISH);
- checkCapitalize("Lorem!Ipsum (Dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
- " \n,.;!?*()&", Locale.ENGLISH);
- checkCapitalize("Lorem!Ipsum (dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
- " \n,.;!?*()&", Locale.ENGLISH);
- }
-
- public void testLooksLikeURL() {
- assertTrue(StringUtils.lastPartLooksLikeURL("http://www.google."));
- assertFalse(StringUtils.lastPartLooksLikeURL("word wo"));
- assertTrue(StringUtils.lastPartLooksLikeURL("/etc/foo"));
- assertFalse(StringUtils.lastPartLooksLikeURL("left/right"));
- assertTrue(StringUtils.lastPartLooksLikeURL("www.goo"));
- assertTrue(StringUtils.lastPartLooksLikeURL("www."));
- assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A"));
- assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A."));
- assertTrue(StringUtils.lastPartLooksLikeURL("rtsp://foo."));
- assertTrue(StringUtils.lastPartLooksLikeURL("://"));
- assertFalse(StringUtils.lastPartLooksLikeURL("abc/"));
- assertTrue(StringUtils.lastPartLooksLikeURL("abc.def/ghi"));
- assertFalse(StringUtils.lastPartLooksLikeURL("abc.def"));
- // TODO: ideally this would not look like a URL, but to keep down the complexity of the
- // code for now True is acceptable.
- assertTrue(StringUtils.lastPartLooksLikeURL("abc./def"));
- // TODO: ideally this would not look like a URL, but to keep down the complexity of the
- // code for now True is acceptable.
- assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def"));
- }
-
- public void testHexStringUtils() {
- final byte[] bytes = new byte[] { (byte)0x01, (byte)0x11, (byte)0x22, (byte)0x33,
- (byte)0x55, (byte)0x88, (byte)0xEE };
- final String bytesStr = StringUtils.byteArrayToHexString(bytes);
- final byte[] bytes2 = StringUtils.hexStringToByteArray(bytesStr);
- for (int i = 0; i < bytes.length; ++i) {
- assertTrue(bytes[i] == bytes2[i]);
- }
- final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2);
- assertTrue(bytesStr.equals(bytesStr2));
- }
-
- public void testJsonStringUtils() {
- final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
- final List<Object> objArray = Arrays.asList(objs);
- final String str = StringUtils.listToJsonStr(objArray);
- final List<Object> newObjArray = StringUtils.jsonStrToList(str);
- for (int i = 0; i < objs.length; ++i) {
- assertEquals(objs[i], newObjArray.get(i));
- }
- }
-}
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index 856b2dbda..ce3df7dd6 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -20,9 +20,9 @@ import android.content.Context;
import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
import java.util.ArrayList;
@@ -30,8 +30,8 @@ import java.util.Locale;
@SmallTest
public class SubtypeLocaleUtilsTests extends AndroidTestCase {
- // Locale to subtypes list.
- private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList();
+ // All input method subtypes of LatinIME.
+ private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>();
private RichInputMethodManager mRichImm;
private Resources mRes;
@@ -41,7 +41,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
InputMethodSubtype ES_US;
InputMethodSubtype FR;
InputMethodSubtype FR_CA;
+ InputMethodSubtype FR_CH;
InputMethodSubtype DE;
+ InputMethodSubtype DE_CH;
InputMethodSubtype ZZ;
InputMethodSubtype DE_QWERTY;
InputMethodSubtype FR_QWERTZ;
@@ -60,6 +62,13 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
mRes = context.getResources();
SubtypeLocaleUtils.init(context);
+ final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme();
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int index = 0; index < subtypeCount; index++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ mSubtypesList.add(subtype);
+ }
+
EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
Locale.US.toString(), "qwerty");
EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
@@ -70,37 +79,41 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
Locale.FRENCH.toString(), "azerty");
FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
Locale.CANADA_FRENCH.toString(), "qwerty");
+ FR_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "fr_CH", "swiss");
DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
Locale.GERMAN.toString(), "qwertz");
+ DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "de_CH", "swiss");
ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
- DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
- Locale.GERMAN.toString(), "qwerty", null);
- FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype(
- Locale.FRENCH.toString(), "qwertz", null);
- EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
- Locale.US.toString(), "azerty", null);
- EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype(
- Locale.UK.toString(), "dvorak", null);
- ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype(
- "es_US", "colemak", null);
- ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
- SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null);
- ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype(
- SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null);
-
+ DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.GERMAN.toString(), "qwerty");
+ FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.FRENCH.toString(), "qwertz");
+ EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.US.toString(), "azerty");
+ EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ Locale.UK.toString(), "dvorak");
+ ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ "es_US", "colemak");
+ ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+ ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty");
}
public void testAllFullDisplayName() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
+ final String subtypeName = SubtypeLocaleUtils
+ .getSubtypeDisplayNameInSystemLocale(subtype);
if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
- final String noLanguage = mRes.getString(R.string.subtype_no_language);
- assertTrue(subtypeName, subtypeName.contains(noLanguage));
+ final String layoutName = SubtypeLocaleUtils
+ .getKeyboardLayoutSetDisplayName(subtype);
+ assertTrue(subtypeName, subtypeName.contains(layoutName));
} else {
- final String languageName =
- SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale());
+ final String languageName = SubtypeLocaleUtils
+ .getSubtypeLocaleDisplayNameInSystemLocale(subtype.getLocale());
assertTrue(subtypeName, subtypeName.contains(languageName));
}
}
@@ -110,10 +123,23 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
assertEquals("en_US", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_US));
assertEquals("en_GB", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_GB));
assertEquals("es_US", "spanish", SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US));
- assertEquals("fr ", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR));
+ assertEquals("fr", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR));
assertEquals("fr_CA", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CA));
- assertEquals("de ", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE));
- assertEquals("zz ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ));
+ assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH));
+ assertEquals("de", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE));
+ assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH));
+ assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ));
+
+ assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY));
+ assertEquals("fr qwertz", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_QWERTZ));
+ assertEquals("en_US azerty", "azerty",
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_US_AZERTY));
+ assertEquals("en_UK dvorak", "dvorak",
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_UK_DVORAK));
+ assertEquals("es_US colemak", "colemak",
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US_COLEMAK));
+ assertEquals("zz azerty", "azerty",
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ_AZERTY));
}
// InputMethodSubtype's display name in system locale (en_US).
@@ -125,7 +151,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
// es_US spanish F Spanish (US) exception
// fr azerty F French
// fr_CA qwerty F French (Canada)
+ // fr_CH swiss F French (Switzerland)
// de qwertz F German
+ // de_CH swiss F German (Switzerland)
// zz qwerty F Alphabet (QWERTY)
// fr qwertz T French (QWERTZ)
// de qwerty T German (QWERTY)
@@ -144,13 +172,17 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB));
assertEquals("es_US", "Spanish (US)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US));
- assertEquals("fr ", "French",
+ assertEquals("fr", "French",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR));
assertEquals("fr_CA", "French (Canada)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA));
- assertEquals("de ", "German",
+ assertEquals("fr_CH", "French (Switzerland)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH));
+ assertEquals("de", "German",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
- assertEquals("zz ", "Alphabet (QWERTY)",
+ assertEquals("de_CH", "German (Switzerland)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
+ assertEquals("zz", "Alphabet (QWERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
return null;
}
@@ -162,17 +194,19 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@Override
protected Void job(final Resources res) {
- assertEquals("fr qwertz", "French (QWERTZ)",
+ assertEquals("fr qwertz", "French (QWERTZ)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ));
- assertEquals("de qwerty", "German (QWERTY)",
+ assertEquals("de qwerty", "German (QWERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY));
assertEquals("en_US azerty", "English (US) (AZERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY));
- assertEquals("en_UK dvorak", "English (UK) (Dvorak)",
+ assertEquals("en_UK dvorak","English (UK) (Dvorak)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK));
- assertEquals("es_US colemak","Spanish (US) (Colemak)",
+ assertEquals("es_US colemak", "Spanish (US) (Colemak)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK));
- assertEquals("zz pc", "Alphabet (PC)",
+ assertEquals("zz azerty", "Alphabet (AZERTY)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY));
+ assertEquals("zz pc", "Alphabet (PC)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC));
return null;
}
@@ -189,14 +223,16 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
// es_US spanish F Espagnol (États-Unis) exception
// fr azerty F Français
// fr_CA qwerty F Français (Canada)
+ // fr_CH swiss F Français (Suisse)
// de qwertz F Allemand
- // zz qwerty F Aucune langue (QWERTY)
+ // de_CH swiss F Allemand (Suisse)
+ // zz qwerty F Alphabet latin (QWERTY)
// fr qwertz T Français (QWERTZ)
// de qwerty T Allemand (QWERTY)
// en_US azerty T Anglais (États-Unis) (AZERTY) exception
// en_UK dvorak T Anglais (Royaume-Uni) (Dvorak) exception
// es_US colemak T Espagnol (États-Unis) (Colemak) exception
- // zz pc T Alphabet (PC)
+ // zz pc T Alphabet latin (PC)
public void testPredefinedSubtypesInFrenchSystemLocale() {
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@@ -208,13 +244,17 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB));
assertEquals("es_US", "Espagnol (États-Unis)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US));
- assertEquals("fr ", "Français",
+ assertEquals("fr", "Français",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR));
assertEquals("fr_CA", "Français (Canada)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA));
- assertEquals("de ", "Allemand",
+ assertEquals("fr_CH", "Français (Suisse)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH));
+ assertEquals("de", "Allemand",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
- assertEquals("zz ", "Alphabet latin (QWERTY)",
+ assertEquals("de_CH", "Allemand (Suisse)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
+ assertEquals("zz", "Alphabet latin (QWERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
return null;
}
@@ -226,17 +266,19 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@Override
protected Void job(final Resources res) {
- assertEquals("fr qwertz", "Français (QWERTZ)",
+ assertEquals("fr qwertz", "Français (QWERTZ)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ));
- assertEquals("de qwerty", "Allemand (QWERTY)",
+ assertEquals("de qwerty", "Allemand (QWERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY));
assertEquals("en_US azerty", "Anglais (États-Unis) (AZERTY)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY));
assertEquals("en_UK dvorak", "Anglais (Royaume-Uni) (Dvorak)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK));
- assertEquals("es_US colemak","Espagnol (États-Unis) (Colemak)",
+ assertEquals("es_US colemak", "Espagnol (États-Unis) (Colemak)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK));
- assertEquals("zz pc", "Alphabet latin (PC)",
+ assertEquals("zz azerty", "Alphabet latin (AZERTY)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY));
+ assertEquals("zz pc", "Alphabet latin (PC)",
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC));
return null;
}
@@ -244,144 +286,26 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
tests.runInLocale(mRes, Locale.FRENCH);
}
- public void testAllFullDisplayNameForSpacebar() {
- for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
- final String spacebarText = SubtypeLocaleUtils.getFullDisplayName(subtype);
- final String languageName =
- SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale());
- if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
- assertFalse(subtypeName, spacebarText.contains(languageName));
- } else {
- assertTrue(subtypeName, spacebarText.contains(languageName));
- }
- }
- }
-
- public void testAllMiddleDisplayNameForSpacebar() {
- for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
- final String spacebarText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
- if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
- assertEquals(subtypeName,
- SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype), spacebarText);
- } else {
- assertEquals(subtypeName,
- SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale()),
- spacebarText);
- }
- }
- }
+ public void testIsRtlLanguage() {
+ // Known Right-to-Left language subtypes.
+ final InputMethodSubtype ARABIC = mRichImm
+ .findSubtypeByLocaleAndKeyboardLayoutSet("ar", "arabic");
+ assertNotNull("Arabic", ARABIC);
+ final InputMethodSubtype FARSI = mRichImm
+ .findSubtypeByLocaleAndKeyboardLayoutSet("fa", "farsi");
+ assertNotNull("Farsi", FARSI);
+ final InputMethodSubtype HEBREW = mRichImm
+ .findSubtypeByLocaleAndKeyboardLayoutSet("iw", "hebrew");
+ assertNotNull("Hebrew", HEBREW);
- public void testAllShortDisplayNameForSpacebar() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
- final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
- final String spacebarText = SubtypeLocaleUtils.getShortDisplayName(subtype);
- final String languageCode = StringUtils.capitalizeFirstCodePoint(
- locale.getLanguage(), locale);
- if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
- assertEquals(subtypeName, "", spacebarText);
+ final String subtypeName = SubtypeLocaleUtils
+ .getSubtypeDisplayNameInSystemLocale(subtype);
+ if (subtype.equals(ARABIC) || subtype.equals(FARSI) || subtype.equals(HEBREW)) {
+ assertTrue(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype));
} else {
- assertEquals(subtypeName, languageCode, spacebarText);
+ assertFalse(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype));
}
}
}
-
- // InputMethodSubtype's display name for spacebar text in its locale.
- // isAdditionalSubtype (T=true, F=false)
- // locale layout | Short Middle Full
- // ------ ------- - ---- --------- ----------------------
- // en_US qwerty F En English English (US) exception
- // en_GB qwerty F En English English (UK) exception
- // es_US spanish F Es Español Español (EE.UU.) exception
- // fr azerty F Fr Français Français
- // fr_CA qwerty F Fr Français Français (Canada)
- // de qwertz F De Deutsch Deutsch
- // zz qwerty F QWERTY QWERTY
- // fr qwertz T Fr Français Français
- // de qwerty T De Deutsch Deutsch
- // en_US azerty T En English English (US)
- // zz azerty T AZERTY AZERTY
-
- private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() {
- @Override
- protected Void job(final Resources res) {
- assertEquals("en_US", "English (US)", SubtypeLocaleUtils.getFullDisplayName(EN_US));
- assertEquals("en_GB", "English (UK)", SubtypeLocaleUtils.getFullDisplayName(EN_GB));
- assertEquals("es_US", "Español (EE.UU.)",
- SubtypeLocaleUtils.getFullDisplayName(ES_US));
- assertEquals("fr ", "Français", SubtypeLocaleUtils.getFullDisplayName(FR));
- assertEquals("fr_CA", "Français (Canada)",
- SubtypeLocaleUtils.getFullDisplayName(FR_CA));
- assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getFullDisplayName(DE));
- assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getFullDisplayName(ZZ));
-
- assertEquals("en_US", "English", SubtypeLocaleUtils.getMiddleDisplayName(EN_US));
- assertEquals("en_GB", "English", SubtypeLocaleUtils.getMiddleDisplayName(EN_GB));
- assertEquals("es_US", "Español", SubtypeLocaleUtils.getMiddleDisplayName(ES_US));
- assertEquals("fr ", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR));
- assertEquals("fr_CA", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CA));
- assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getMiddleDisplayName(DE));
- assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getMiddleDisplayName(ZZ));
-
- assertEquals("en_US", "En", SubtypeLocaleUtils.getShortDisplayName(EN_US));
- assertEquals("en_GB", "En", SubtypeLocaleUtils.getShortDisplayName(EN_GB));
- assertEquals("es_US", "Es", SubtypeLocaleUtils.getShortDisplayName(ES_US));
- assertEquals("fr ", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR));
- assertEquals("fr_CA", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CA));
- assertEquals("de ", "De", SubtypeLocaleUtils.getShortDisplayName(DE));
- assertEquals("zz ", "", SubtypeLocaleUtils.getShortDisplayName(ZZ));
- return null;
- }
- };
-
- private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() {
- @Override
- protected Void job(final Resources res) {
- assertEquals("fr qwertz", "Français",
- SubtypeLocaleUtils.getFullDisplayName(FR_QWERTZ));
- assertEquals("de qwerty", "Deutsch",
- SubtypeLocaleUtils.getFullDisplayName(DE_QWERTY));
- assertEquals("en_US azerty", "English (US)",
- SubtypeLocaleUtils.getFullDisplayName(EN_US_AZERTY));
- assertEquals("zz azerty", "AZERTY",
- SubtypeLocaleUtils.getFullDisplayName(ZZ_AZERTY));
-
- assertEquals("fr qwertz", "Français",
- SubtypeLocaleUtils.getMiddleDisplayName(FR_QWERTZ));
- assertEquals("de qwerty", "Deutsch",
- SubtypeLocaleUtils.getMiddleDisplayName(DE_QWERTY));
- assertEquals("en_US azerty", "English",
- SubtypeLocaleUtils.getMiddleDisplayName(EN_US_AZERTY));
- assertEquals("zz azerty", "AZERTY",
- SubtypeLocaleUtils.getMiddleDisplayName(ZZ_AZERTY));
-
- assertEquals("fr qwertz", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_QWERTZ));
- assertEquals("de qwerty", "De", SubtypeLocaleUtils.getShortDisplayName(DE_QWERTY));
- assertEquals("en_US azerty", "En",
- SubtypeLocaleUtils.getShortDisplayName(EN_US_AZERTY));
- assertEquals("zz azerty", "", SubtypeLocaleUtils.getShortDisplayName(ZZ_AZERTY));
- return null;
- }
- };
-
- public void testPredefinedSubtypesForSpacebarInEnglish() {
- testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
- }
-
- public void testAdditionalSubtypeForSpacebarInEnglish() {
- testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH);
- }
-
- public void testPredefinedSubtypesForSpacebarInFrench() {
- testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
- }
-
- public void testAdditionalSubtypeForSpacebarInFrench() {
- testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH);
- }
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
deleted file mode 100644
index 1944fd332..000000000
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-import com.android.inputmethod.latin.makedict.DictDecoder;
-import com.android.inputmethod.latin.makedict.DictEncoder;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
-import com.android.inputmethod.latin.makedict.Ver3DictDecoder;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
-import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-
-/**
- * Unit tests for UserHistoryDictIOUtils
- */
-@LargeTest
-public class UserHistoryDictIOUtilsTests extends AndroidTestCase
- implements BigramDictionaryInterface {
-
- private static final String TAG = UserHistoryDictIOUtilsTests.class.getSimpleName();
- private static final int UNIGRAM_FREQUENCY = 50;
- private static final int BIGRAM_FREQUENCY = 100;
- private static final ArrayList<String> NOT_HAVE_BIGRAM = new ArrayList<String>();
- private static final FormatSpec.FormatOptions FORMAT_OPTIONS = new FormatSpec.FormatOptions(2);
- private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
-
- /**
- * Return same frequency for all words and bigrams
- */
- @Override
- public int getFrequency(String word1, String word2) {
- if (word1 == null) return UNIGRAM_FREQUENCY;
- return BIGRAM_FREQUENCY;
- }
-
- // Utilities for Testing
-
- private void addWord(final String word,
- final HashMap<String, ArrayList<String> > addedWords) {
- if (!addedWords.containsKey(word)) {
- addedWords.put(word, new ArrayList<String>());
- }
- }
-
- private void addBigram(final String word1, final String word2,
- final HashMap<String, ArrayList<String> > addedWords) {
- addWord(word1, addedWords);
- addWord(word2, addedWords);
- addedWords.get(word1).add(word2);
- }
-
- private void addBigramToBigramList(final String word1, final String word2,
- final HashMap<String, ArrayList<String> > addedWords,
- final UserHistoryDictionaryBigramList bigramList) {
- bigramList.addBigram(null, word1);
- bigramList.addBigram(word1, word2);
-
- addBigram(word1, word2, addedWords);
- }
-
- private void checkWordInFusionDict(final FusionDictionary dict, final String word,
- final ArrayList<String> expectedBigrams) {
- final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
- assertNotNull(ptNode);
- assertTrue(ptNode.isTerminal());
-
- for (final String bigram : expectedBigrams) {
- assertNotNull(ptNode.getBigram(bigram));
- }
- }
-
- private void checkWordsInFusionDict(final FusionDictionary dict,
- final HashMap<String, ArrayList<String> > bigrams) {
- for (final String word : bigrams.keySet()) {
- if (bigrams.containsKey(word)) {
- checkWordInFusionDict(dict, word, bigrams.get(word));
- } else {
- checkWordInFusionDict(dict, word, NOT_HAVE_BIGRAM);
- }
- }
- }
-
- private void checkWordInBigramList(
- final UserHistoryDictionaryBigramList bigramList, final String word,
- final ArrayList<String> expectedBigrams) {
- // check unigram
- final HashMap<String,Byte> unigramMap = bigramList.getBigrams(null);
- assertTrue(unigramMap.containsKey(word));
-
- // check bigrams
- final ArrayList<String> actualBigrams = new ArrayList<String>(
- bigramList.getBigrams(word).keySet());
-
- Collections.sort(expectedBigrams);
- Collections.sort(actualBigrams);
- assertEquals(expectedBigrams, actualBigrams);
- }
-
- private void checkWordsInBigramList(final UserHistoryDictionaryBigramList bigramList,
- final HashMap<String, ArrayList<String> > addedWords) {
- for (final String word : addedWords.keySet()) {
- if (addedWords.containsKey(word)) {
- checkWordInBigramList(bigramList, word, addedWords.get(word));
- } else {
- checkWordInBigramList(bigramList, word, NOT_HAVE_BIGRAM);
- }
- }
- }
-
- private void writeDictToFile(final File file,
- final UserHistoryDictionaryBigramList bigramList) {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS);
- }
-
- private void readDictFromFile(final File file, final OnAddWordListener listener) {
- final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY);
- try {
- dictDecoder.openDictBuffer();
- } catch (FileNotFoundException e) {
- Log.e(TAG, "file not found", e);
- } catch (IOException e) {
- Log.e(TAG, "IOException", e);
- }
- UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
- }
-
- public void testGenerateFusionDictionary() {
- final UserHistoryDictionaryBigramList originalList = new UserHistoryDictionaryBigramList();
-
- final HashMap<String, ArrayList<String> > addedWords =
- new HashMap<String, ArrayList<String>>();
- addBigramToBigramList("this", "is", addedWords, originalList);
- addBigramToBigramList("this", "was", addedWords, originalList);
- addBigramToBigramList("hello", "world", addedWords, originalList);
-
- final FusionDictionary fusionDict =
- UserHistoryDictIOUtils.constructFusionDictionary(this, originalList);
-
- checkWordsInFusionDict(fusionDict, addedWords);
- }
-
- public void testReadAndWrite() {
- final Context context = getContext();
-
- File file = null;
- try {
- file = File.createTempFile("testReadAndWrite", TEST_DICT_FILE_EXTENSION,
- getContext().getCacheDir());
- } catch (IOException e) {
- Log.d(TAG, "IOException while creating a temporary file", e);
- }
- assertNotNull(file);
-
- // make original dictionary
- final UserHistoryDictionaryBigramList originalList = new UserHistoryDictionaryBigramList();
- final HashMap<String, ArrayList<String>> addedWords = CollectionUtils.newHashMap();
- addBigramToBigramList("this" , "is" , addedWords, originalList);
- addBigramToBigramList("this" , "was" , addedWords, originalList);
- addBigramToBigramList("is" , "not" , addedWords, originalList);
- addBigramToBigramList("hello", "world", addedWords, originalList);
-
- // write to file
- writeDictToFile(file, originalList);
-
- // make result dict.
- final UserHistoryDictionaryBigramList resultList = new UserHistoryDictionaryBigramList();
- final OnAddWordListener listener = new OnAddWordListener() {
- @Override
- public void setUnigram(final String word, final String shortcutTarget,
- final int frequency, final int shortcutFreq) {
- Log.d(TAG, "in: setUnigram: " + word + "," + frequency);
- resultList.addBigram(null, word, (byte)frequency);
- }
- @Override
- public void setBigram(final String word1, final String word2, final int frequency) {
- Log.d(TAG, "in: setBigram: " + word1 + "," + word2 + "," + frequency);
- resultList.addBigram(word1, word2, (byte)frequency);
- }
- };
-
- // load from file
- readDictFromFile(file, listener);
- checkWordsInBigramList(resultList, addedWords);
-
- // add new bigram
- addBigramToBigramList("hello", "java", addedWords, resultList);
-
- // rewrite
- writeDictToFile(file, resultList);
- final UserHistoryDictionaryBigramList resultList2 = new UserHistoryDictionaryBigramList();
- final OnAddWordListener listener2 = new OnAddWordListener() {
- @Override
- public void setUnigram(final String word, final String shortcutTarget,
- final int frequency, final int shortcutFreq) {
- Log.d(TAG, "in: setUnigram: " + word + "," + frequency);
- resultList2.addBigram(null, word, (byte)frequency);
- }
- @Override
- public void setBigram(final String word1, final String word2, final int frequency) {
- Log.d(TAG, "in: setBigram: " + word1 + "," + word2 + "," + frequency);
- resultList2.addBigram(word1, word2, (byte)frequency);
- }
- };
-
- // load from file
- readDictFromFile(file, listener2);
- checkWordsInBigramList(resultList2, addedWords);
- }
-}
diff --git a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
deleted file mode 100644
index 28a9f3d5c..000000000
--- a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.research;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.JsonReader;
-
-import com.android.inputmethod.research.MotionEventReader.ReplayData;
-
-import java.io.IOException;
-import java.io.StringReader;
-
-@SmallTest
-public class MotionEventReaderTests extends AndroidTestCase {
- private MotionEventReader mMotionEventReader = new MotionEventReader();
- private ReplayData mReplayData;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mReplayData = new ReplayData();
- }
-
- private JsonReader jsonReaderForString(final String s) {
- return new JsonReader(new StringReader(s));
- }
-
- public void testTopLevelDataVariant() {
- final JsonReader jsonReader = jsonReaderForString(
- "{"
- + "\"_ct\": 1359590400000,"
- + "\"_ut\": 4381933,"
- + "\"_ty\": \"MotionEvent\","
- + "\"action\": \"UP\","
- + "\"isLoggingRelated\": false,"
- + "\"x\": 100.0,"
- + "\"y\": 200.0"
- + "}"
- );
- try {
- mMotionEventReader.readLogStatement(jsonReader, mReplayData);
- } catch (IOException e) {
- e.printStackTrace();
- fail("IOException thrown");
- }
- assertEquals("x set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
- assertEquals("y set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
- assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1);
- assertEquals("only one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1);
- }
-
- public void testNestedDataVariant() {
- final JsonReader jsonReader = jsonReaderForString(
- "{"
- + " \"_ct\": 135959040000,"
- + " \"_ut\": 4382702,"
- + " \"_ty\": \"MotionEvent\","
- + " \"action\": \"MOVE\","
- + " \"isLoggingRelated\": false,"
- + " \"motionEvent\": {"
- + " \"pointerIds\": ["
- + " 0"
- + " ],"
- + " \"xyt\": ["
- + " {"
- + " \"t\": 4382551,"
- + " \"d\": ["
- + " {"
- + " \"x\": 100.0,"
- + " \"y\": 200.0,"
- + " \"toma\": 999.0,"
- + " \"tomi\": 999.0,"
- + " \"o\": 0.0"
- + " }"
- + " ]"
- + " },"
- + " {"
- + " \"t\": 4382559,"
- + " \"d\": ["
- + " {"
- + " \"x\": 300.0,"
- + " \"y\": 400.0,"
- + " \"toma\": 999.0,"
- + " \"tomi\": 999.0,"
- + " \"o\": 0.0"
- + " }"
- + " ]"
- + " }"
- + " ]"
- + " }"
- + "}"
- );
- try {
- mMotionEventReader.readLogStatement(jsonReader, mReplayData);
- } catch (IOException e) {
- e.printStackTrace();
- fail("IOException thrown");
- }
- assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
- assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
- assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].x, 300);
- assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].y, 400);
- assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1);
- assertEquals("two MotionEvents", mReplayData.mPointerCoordsArrays.size(), 2);
- }
-
- public void testNestedDataVariantMultiPointer() {
- final JsonReader jsonReader = jsonReaderForString(
- "{"
- + " \"_ct\": 135959040000,"
- + " \"_ut\": 4382702,"
- + " \"_ty\": \"MotionEvent\","
- + " \"action\": \"MOVE\","
- + " \"isLoggingRelated\": false,"
- + " \"motionEvent\": {"
- + " \"pointerIds\": ["
- + " 1"
- + " ],"
- + " \"xyt\": ["
- + " {"
- + " \"t\": 4382551,"
- + " \"d\": ["
- + " {"
- + " \"x\": 100.0,"
- + " \"y\": 200.0,"
- + " \"toma\": 999.0,"
- + " \"tomi\": 999.0,"
- + " \"o\": 0.0"
- + " },"
- + " {"
- + " \"x\": 300.0,"
- + " \"y\": 400.0,"
- + " \"toma\": 999.0,"
- + " \"tomi\": 999.0,"
- + " \"o\": 0.0"
- + " }"
- + " ]"
- + " }"
- + " ]"
- + " }"
- + "}"
- );
- try {
- mMotionEventReader.readLogStatement(jsonReader, mReplayData);
- } catch (IOException e) {
- e.printStackTrace();
- fail("IOException thrown");
- }
- assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
- assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
- assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].x, 300);
- assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].y, 400);
- assertEquals("two pointers", mReplayData.mPointerCoordsArrays.get(0).length, 2);
- assertEquals("one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1);
- }
-}