diff options
111 files changed, 3139 insertions, 2537 deletions
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 50ae4dcae..cd31740b6 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -75,7 +75,7 @@ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Onyesha neno lililopendekezwa unapoonyesha ishara"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Imehifadhiwa"</string> <string name="label_go_key" msgid="1635148082137219148">"Nenda"</string> - <string name="label_next_key" msgid="362972844525672568">"Ifuatayo"</string> + <string name="label_next_key" msgid="362972844525672568">"Inayofuata"</string> <string name="label_previous_key" msgid="1211868118071386787">"Iliyotangulia"</string> <string name="label_done_key" msgid="2441578748772529288">"Kwisha"</string> <string name="label_send_key" msgid="2815056534433717444">"Tuma"</string> diff --git a/java/res/xml-sw600dp/keys_arabic3_left.xml b/java/res/xml-sw600dp/keys_arabic3_left.xml new file mode 100644 index 000000000..0f2ccc0ac --- /dev/null +++ b/java/res/xml-sw600dp/keys_arabic3_left.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0630: "ذ" ARABIC LETTER THAL --> + <Key + latin:keyLabel="ذ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE --> + <Key + latin:keyLabel="ئ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml-sw600dp/keys_farsi3_right.xml b/java/res/xml-sw600dp/keys_farsi3_right.xml new file mode 100644 index 000000000..3c91ae92d --- /dev/null +++ b/java/res/xml-sw600dp/keys_farsi3_right.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE --> + <Key + latin:keyLabel="آ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0686: "چ" ARABIC LETTER TCHEH --> + <Key + latin:keyLabel="چ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml-sw600dp/rowkeys_arabic1.xml b/java/res/xml-sw600dp/rowkeys_arabic1.xml deleted file mode 100644 index 3c0acf112..000000000 --- a/java/res/xml-sw600dp/rowkeys_arabic1.xml +++ /dev/null @@ -1,112 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0636: "ض" ARABIC LETTER DAD - U+0661: "١" ARABIC-INDIC DIGIT ONE --> - <Key - latin:keyLabel="ض" - latin:keyHintLabel="1" - latin:additionalMoreKeys="1,١" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0635: "ص" ARABIC LETTER SAD - U+0662: "٢" ARABIC-INDIC DIGIT TWO --> - <Key - latin:keyLabel="ص" - latin:keyHintLabel="2" - latin:additionalMoreKeys="2,٢" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062B: "ث" ARABIC LETTER THEH - U+0663: "٣" ARABIC-INDIC DIGIT THREE --> - <Key - latin:keyLabel="ث" - latin:keyHintLabel="3" - latin:additionalMoreKeys="3,٣" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0642: "ق" ARABIC LETTER QAF - U+06A8: "ڨ" ARABIC LETTER QAF WITH THREE DOTS ABOVE - U+0664: "٤" ARABIC-INDIC DIGIT FOUR --> - <!-- TODO: DroidSansArabic lacks the glyph of U+06A8 ARABIC LETTER QAF WITH THREE DOTS ABOVE --> - <Key - latin:keyLabel="ق" - latin:keyHintLabel="4" - latin:additionalMoreKeys="4,٤" - latin:moreKeys="ڨ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0641: "ف" ARABIC LETTER FEH - U+06A4: "ڤ" ARABIC LETTER VEH - U+06A2: "ڢ" ARABIC LETTER FEH WITH DOT MOVED BELOW - U+06A5: "ڥ" ARABIC LETTER FEH WITH THREE DOTS BELOW - U+0665: "٥" ARABIC-INDIC DIGIT FIVE --> - <!-- TODO: DroidSansArabic lacks the glyph of U+06A2 ARABIC LETTER FEH WITH DOT MOVED BELOW --> - <!-- TODO: DroidSansArabic lacks the glyph of U+06A5 ARABIC LETTER FEH WITH THREE DOTS BELOW --> - <Key - latin:keyLabel="ف" - latin:keyHintLabel="5" - latin:additionalMoreKeys="5,٥" - latin:moreKeys="ڤ,ڢ,ڥ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+063A: "غ" ARABIC LETTER GHAIN - U+0666: "٦" ARABIC-INDIC DIGIT SIX --> - <Key - latin:keyLabel="غ" - latin:keyHintLabel="6" - latin:additionalMoreKeys="6,٦" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0639: "ع" ARABIC LETTER AIN - U+0667: "٧" ARABIC-INDIC DIGIT SEVEN --> - <Key - latin:keyLabel="ع" - latin:keyHintLabel="7" - latin:additionalMoreKeys="7,٧" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0647: "ه" ARABIC LETTER HEH - U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM - U+0647 U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER - U+0668: "٨" ARABIC-INDIC DIGIT EIGHT --> - <Key - latin:keyLabel="ه" - latin:keyHintLabel="8" - latin:additionalMoreKeys="8,٨" - latin:moreKeys="ﻫ|ه‍" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062E: "خ" ARABIC LETTER KHAH - U+0669: "٩" ARABIC-INDIC DIGIT NINE --> - <Key - latin:keyLabel="خ" - latin:keyHintLabel="9" - latin:additionalMoreKeys="9,٩" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062D: "ح" ARABIC LETTER HAH - U+0660: "٠" ARABIC-INDIC DIGIT ZERO --> - <Key - latin:keyLabel="ح" - latin:keyHintLabel="0" - latin:additionalMoreKeys="0,٠" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062C: "ج" ARABIC LETTER JEEM - U+0686: "چ" ARABIC LETTER TCHEH --> - <Key - latin:keyLabel="ج" - latin:moreKeys="چ" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rowkeys_arabic2.xml b/java/res/xml-sw600dp/rowkeys_arabic2.xml deleted file mode 100644 index 00e69ace7..000000000 --- a/java/res/xml-sw600dp/rowkeys_arabic2.xml +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0634: "ش" ARABIC LETTER SHEEN - U+069C: "ڜ" ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE --> - <!-- TODO: DroidSansArabic lacks the glyph of U+069C ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE --> - <Key - latin:keyLabel="ش" - latin:moreKeys="ڜ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0633: "س" ARABIC LETTER SEEN --> - <Key - latin:keyLabel="س" - latin:keyLabelFlags="fontNormal" /> - <!-- U+064A: "ي" ARABIC LETTER YEH - U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE - U+0649: "ى" ARABIC LETTER ALEF MAKSURA --> - <Key - latin:keyLabel="ي" - latin:moreKeys="ئ,ى" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0628: "ب" ARABIC LETTER BEH - U+067E: "پ" ARABIC LETTER PEH --> - <Key - latin:keyLabel="ب" - latin:moreKeys="پ" - latin:keyLabelFlags="fontNormal" /> - <!-- 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 - latin:keyLabel="ل" - latin:moreKeys="ﻻ|لا,ﻷ|لأ,ﻹ|لإ,ﻵ|لآ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0627: "ا" ARABIC LETTER ALEF - U+0621: "ء" ARABIC LETTER HAMZA - U+0671: "ٱ" ARABIC LETTER ALEF WASLA - U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE - U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW - U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE --> - <Key - latin:keyLabel="ا" - latin:moreKeys="ء,ٱ,أ,إ,آ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062A: "ت" ARABIC LETTER TEH --> - <Key - latin:keyLabel="ت" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0646: "ن" ARABIC LETTER NOON --> - <Key - latin:keyLabel="ن" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0645: "م" ARABIC LETTER MEEM --> - <Key - latin:keyLabel="م" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0643: "ك" ARABIC LETTER KAF - U+06AF: "گ" ARABIC LETTER GAF - U+06A9: "ک" ARABIC LETTER KEHEH --> - <Key - latin:keyLabel="ك" - latin:moreKeys="گ,ک" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0637: "ط" ARABIC LETTER TAH --> - <Key - latin:keyLabel="ط" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rowkeys_arabic3.xml b/java/res/xml-sw600dp/rowkeys_arabic3.xml deleted file mode 100644 index b0bcd78d6..000000000 --- a/java/res/xml-sw600dp/rowkeys_arabic3.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE --> - <Key - latin:keyLabel="ئ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0621: "ء" ARABIC LETTER HAMZA --> - <Key - latin:keyLabel="ء" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE --> - <Key - latin:keyLabel="ؤ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0631: "ر" ARABIC LETTER REH --> - <Key - latin:keyLabel="ر" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0630: "ذ" ARABIC LETTER THAL --> - <Key - latin:keyLabel="ذ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0649: "ى" ARABIC LETTER ALEF MAKSURA --> - <Key - latin:keyLabel="ى" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0629: "ة" ARABIC LETTER TEH MARBUTA --> - <Key - latin:keyLabel="ة" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0648: "و" ARABIC LETTER WAW --> - <Key - latin:keyLabel="و" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0632: "ز" ARABIC LETTER ZAIN - U+0698: "ژ" ARABIC LETTER JEH --> - <Key - latin:keyLabel="ز" - latin:moreKeys="ژ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0638: "ظ" ARABIC LETTER ZAH --> - <Key - latin:keyLabel="ظ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062F: "د" ARABIC LETTER DAL --> - <Key - latin:keyLabel="د" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rowkeys_farsi1.xml b/java/res/xml-sw600dp/rowkeys_farsi1.xml deleted file mode 100644 index 6dd9a6575..000000000 --- a/java/res/xml-sw600dp/rowkeys_farsi1.xml +++ /dev/null @@ -1,107 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0636: "ض" ARABIC LETTER DAD - U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE --> - <Key - latin:keyLabel="ض" - latin:keyHintLabel="۱" - latin:additionalMoreKeys="۱,1" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0635: "ص" ARABIC LETTER SAD - U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO --> - <Key - latin:keyLabel="ص" - latin:keyHintLabel="۲" - latin:additionalMoreKeys="۲,2" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062B: "ث" ARABIC LETTER THEH - U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE --> - <Key - latin:keyLabel="ث" - latin:keyHintLabel="۳" - latin:additionalMoreKeys="۳,3" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0642: "ق" ARABIC LETTER QAF - U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR --> - <Key - latin:keyLabel="ق" - latin:keyHintLabel="۴" - latin:additionalMoreKeys="۴,4" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0641: "ف" ARABIC LETTER FEH - U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE --> - <Key - latin:keyLabel="ف" - latin:keyHintLabel="۵" - latin:additionalMoreKeys="۵,5" - latin:keyLabelFlags="fontNormal" /> - <!-- U+063A: "غ" ARABIC LETTER GHAIN - U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX --> - <Key - latin:keyLabel="غ" - latin:keyHintLabel="۶" - latin:additionalMoreKeys="۶,6" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0639: "ع" ARABIC LETTER AIN - U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN --> - <Key - latin:keyLabel="ع" - latin:keyHintLabel="۷" - latin:additionalMoreKeys="۷,7" - latin:keyLabelFlags="fontNormal" /> - <!-- 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 - latin:keyLabel="ه" - latin:moreKeys="ﻫ|ه‍,هٔ,ة,%" - latin:keyHintLabel="۸" - latin:additionalMoreKeys="۸,8" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062E: "خ" ARABIC LETTER KHAH - U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE --> - <Key - latin:keyLabel="خ" - latin:keyHintLabel="۹" - latin:additionalMoreKeys="۹,9" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062D: "ح" ARABIC LETTER HAH - U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO --> - <Key - latin:keyLabel="ح" - latin:keyHintLabel="۰" - latin:additionalMoreKeys="۰,0" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062C: "ج" ARABIC LETTER JEEM --> - <Key - latin:keyLabel="ج" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0686: "چ" ARABIC LETTER TCHEH --> - <Key - latin:keyLabel="چ" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rowkeys_farsi2.xml b/java/res/xml-sw600dp/rowkeys_farsi2.xml deleted file mode 100644 index 3b759b66c..000000000 --- a/java/res/xml-sw600dp/rowkeys_farsi2.xml +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0634: "ش" ARABIC LETTER SHEEN --> - <Key - latin:keyLabel="ش" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0633: "س" ARABIC LETTER SEEN --> - <Key - latin:keyLabel="س" - latin:keyLabelFlags="fontNormal" /> - <!-- 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 - latin:keyLabel="ی" - latin:moreKeys="ئ,ي,ﯨ|ى" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0628: "ب" ARABIC LETTER BEH --> - <Key - latin:keyLabel="ب" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0644: "ل" ARABIC LETTER LAM --> - <Key - latin:keyLabel="ل" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0627: "ا" ARABIC LETTER ALEF - U+0621: "ء" ARABIC LETTER HAMZA - U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE - U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE - U+0671: "ٱ" ARABIC LETTER ALEF WASLA - U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW --> - <Key - latin:keyLabel="ا" - latin:moreKeys="ء,آ,أ,ٱ,إ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062A: "ت" ARABIC LETTER TEH - U+062B: "ﺙ" ARABIC LETTER THEH - U+0629: "ة": ARABIC LETTER TEH MARBUTA --> - <Key - latin:keyLabel="ت" - latin:moreKeys="ث,ة" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0646: "ن" ARABIC LETTER NOON --> - <Key - latin:keyLabel="ن" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0645: "م" ARABIC LETTER MEEM --> - <Key - latin:keyLabel="م" - latin:keyLabelFlags="fontNormal" /> - <!-- U+06A9: "ک" ARABIC LETTER KEHEH - U+0643: "ك" ARABIC LETTER KAF --> - <Key - latin:keyLabel="ک" - latin:moreKeys="ك" - latin:keyLabelFlags="fontNormal" /> - <!-- U+06AF: "گ" ARABIC LETTER GAF --> - <Key - latin:keyLabel="گ" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rowkeys_farsi3.xml b/java/res/xml-sw600dp/rowkeys_farsi3.xml deleted file mode 100644 index 3597618ce..000000000 --- a/java/res/xml-sw600dp/rowkeys_farsi3.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+0638: "ظ" ARABIC LETTER ZAH --> - <Key - latin:keyLabel="ظ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0637: "ط" ARABIC LETTER TAH --> - <Key - latin:keyLabel="ط" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0698: "ژ" ARABIC LETTER JEH --> - <Key - latin:keyLabel="ژ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0632: "ز" ARABIC LETTER ZAIN --> - <Key - latin:keyLabel="ز" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0631: "ر" ARABIC LETTER REH --> - <Key - latin:keyLabel="ر" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0630: "ذ" ARABIC LETTER THAL --> - <Key - latin:keyLabel="ذ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+062F: "د" ARABIC LETTER DAL --> - <Key - latin:keyLabel="د" - latin:keyLabelFlags="fontNormal" /> - <!-- U+067E: "پ" ARABIC LETTER PEH --> - <Key - latin:keyLabel="پ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0648: "و" ARABIC LETTER WAW - U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE --> - <Key - latin:keyLabel="و" - latin:moreKeys="ؤ" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE --> - <Key - latin:keyLabel="آ" - latin:keyLabelFlags="fontNormal" /> -</merge> diff --git a/java/res/xml-sw600dp/rows_arabic.xml b/java/res/xml-sw600dp/rows_arabic.xml index ec7c2ad96..5a28d4572 100644 --- a/java/res/xml-sw600dp/rows_arabic.xml +++ b/java/res/xml-sw600dp/rows_arabic.xml @@ -45,8 +45,7 @@ latin:keyWidth="8.182%p" > <include - latin:keyboardLayout="@xml/rowkeys_arabic3" - latin:keyXPos="4.091%p" /> + latin:keyboardLayout="@xml/rowkeys_arabic3" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw600dp/rows_farsi.xml b/java/res/xml-sw600dp/rows_farsi.xml index 52c2d9329..a353b67c7 100644 --- a/java/res/xml-sw600dp/rows_farsi.xml +++ b/java/res/xml-sw600dp/rows_farsi.xml @@ -28,6 +28,9 @@ > <include latin:keyboardLayout="@xml/rowkeys_farsi1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> </Row> <Row latin:keyWidth="8.182%p" @@ -35,7 +38,7 @@ <include latin:keyboardLayout="@xml/rowkeys_farsi2" /> <Key - latin:keyStyle="deleteKeyStyle" + latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> </Row> <Row @@ -44,9 +47,6 @@ <include latin:keyboardLayout="@xml/rowkeys_farsi3" latin:keyXPos="4.091%p" /> - <Key - latin:keyStyle="enterKeyStyle" - latin:keyWidth="fillRight" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw768dp/rows_arabic.xml b/java/res/xml-sw768dp/rows_arabic.xml index 8b05d9376..204f6d590 100644 --- a/java/res/xml-sw768dp/rows_arabic.xml +++ b/java/res/xml-sw768dp/rows_arabic.xml @@ -54,7 +54,7 @@ > <include latin:keyboardLayout="@xml/rowkeys_arabic3" - latin:keyXPos="13.829%p" /> + latin:keyXPos="6.602%p" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw768dp/rows_farsi.xml b/java/res/xml-sw768dp/rows_farsi.xml index 4b4c970fd..8d3fb0579 100644 --- a/java/res/xml-sw768dp/rows_farsi.xml +++ b/java/res/xml-sw768dp/rows_farsi.xml @@ -32,6 +32,9 @@ latin:keyWidth="7.969%p" /> <include latin:keyboardLayout="@xml/rowkeys_farsi1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> </Row> <Row latin:keyWidth="7.227%p" @@ -39,22 +42,19 @@ <Key latin:keyStyle="toSymbolKeyStyle" latin:keyLabelFlags="alignLeft" - latin:keyWidth="11.172%p"/> + latin:keyWidth="11.172%p" /> <include latin:keyboardLayout="@xml/rowkeys_farsi2" /> <Key - latin:keyStyle="deleteKeyStyle" + latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> </Row> <Row - latin:keyWidth="7.186%p" + latin:keyWidth="7.227%p" > <include latin:keyboardLayout="@xml/rowkeys_farsi3" latin:keyXPos="13.829%p" /> - <Key - latin:keyStyle="enterKeyStyle" - latin:keyWidth="fillRight" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml/keys_arabic3_left.xml b/java/res/xml/keys_arabic3_left.xml new file mode 100644 index 000000000..157af4a52 --- /dev/null +++ b/java/res/xml/keys_arabic3_left.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0630: "ذ" ARABIC LETTER THAL --> + <Key + latin:keyLabel="ذ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml/keys_farsi3_right.xml b/java/res/xml/keys_farsi3_right.xml new file mode 100644 index 000000000..77efb0a21 --- /dev/null +++ b/java/res/xml/keys_farsi3_right.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0686: "چ" ARABIC LETTER TCHEH --> + <Key + latin:keyLabel="چ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index f30ef2362..5e33601e6 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -21,9 +21,10 @@ <!-- for the Input Method Manager. --> <!-- Supported subtypes - keyboard_locale: script_name/keyboard_layout_set[:keyboard_locale] + keyboard_locale: script_name/keyboard_layout_set af: Afrikaans/qwerty ar: Arabic/arabic + az: Azerbaijani/qwerty be: Belarusian/east_slavic bg: Bulgarian/bulgarian bg: Bulgarian/bulgarian_bds @@ -51,6 +52,7 @@ it: Italian/qwerty iw: Hebrew/hebrew # "he" is official language code of Hebrew. ka: Georgian/georgian + kk: Kazakh/east_slavic ky: Kyrgyz/east_slavic lt: Lithuanian/qwerty lv: Latvian/qwerty @@ -117,6 +119,13 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:subtypeId="0x2a362219" + android:imeSubtypeLocale="az" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:subtypeId="0x1dc3a859" android:imeSubtypeLocale="be" android:imeSubtypeMode="keyboard" @@ -296,6 +305,13 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:subtypeId="0x2d73d2f6" + android:imeSubtypeLocale="kk" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:subtypeId="0x2e391c04" android:imeSubtypeLocale="ky" android:imeSubtypeMode="keyboard" diff --git a/java/res/xml/rowkeys_arabic1.xml b/java/res/xml/rowkeys_arabic1.xml index a4bef83c6..3c0acf112 100644 --- a/java/res/xml/rowkeys_arabic1.xml +++ b/java/res/xml/rowkeys_arabic1.xml @@ -35,74 +35,78 @@ latin:keyHintLabel="2" latin:additionalMoreKeys="2,٢" latin:keyLabelFlags="fontNormal" /> + <!-- U+062B: "ث" ARABIC LETTER THEH + U+0663: "٣" ARABIC-INDIC DIGIT THREE --> + <Key + latin:keyLabel="ث" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3,٣" + latin:keyLabelFlags="fontNormal" /> <!-- U+0642: "ق" ARABIC LETTER QAF U+06A8: "ڨ" ARABIC LETTER QAF WITH THREE DOTS ABOVE - U+0663: "٣" ARABIC-INDIC DIGIT THREE --> + U+0664: "٤" ARABIC-INDIC DIGIT FOUR --> <!-- TODO: DroidSansArabic lacks the glyph of U+06A8 ARABIC LETTER QAF WITH THREE DOTS ABOVE --> <Key latin:keyLabel="ق" - latin:keyHintLabel="3" - latin:additionalMoreKeys="3,٣" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4,٤" latin:moreKeys="ڨ" latin:keyLabelFlags="fontNormal" /> <!-- U+0641: "ف" ARABIC LETTER FEH U+06A4: "ڤ" ARABIC LETTER VEH U+06A2: "ڢ" ARABIC LETTER FEH WITH DOT MOVED BELOW U+06A5: "ڥ" ARABIC LETTER FEH WITH THREE DOTS BELOW - U+0664: "٤" ARABIC-INDIC DIGIT FOUR --> + U+0665: "٥" ARABIC-INDIC DIGIT FIVE --> <!-- TODO: DroidSansArabic lacks the glyph of U+06A2 ARABIC LETTER FEH WITH DOT MOVED BELOW --> <!-- TODO: DroidSansArabic lacks the glyph of U+06A5 ARABIC LETTER FEH WITH THREE DOTS BELOW --> <Key latin:keyLabel="ف" - latin:keyHintLabel="4" - latin:additionalMoreKeys="4,٤" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5,٥" latin:moreKeys="ڤ,ڢ,ڥ" latin:keyLabelFlags="fontNormal" /> <!-- U+063A: "غ" ARABIC LETTER GHAIN - U+0665: "٥" ARABIC-INDIC DIGIT FIVE --> + U+0666: "٦" ARABIC-INDIC DIGIT SIX --> <Key latin:keyLabel="غ" - latin:keyHintLabel="5" - latin:additionalMoreKeys="5,٥" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6,٦" latin:keyLabelFlags="fontNormal" /> <!-- U+0639: "ع" ARABIC LETTER AIN - U+0666: "٦" ARABIC-INDIC DIGIT SIX --> + U+0667: "٧" ARABIC-INDIC DIGIT SEVEN --> <Key latin:keyLabel="ع" - latin:keyHintLabel="6" - latin:additionalMoreKeys="6,٦" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7,٧" latin:keyLabelFlags="fontNormal" /> <!-- U+0647: "ه" ARABIC LETTER HEH U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM U+0647 U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER - U+0667: "٧" ARABIC-INDIC DIGIT SEVEN --> + U+0668: "٨" ARABIC-INDIC DIGIT EIGHT --> <Key latin:keyLabel="ه" - latin:keyHintLabel="7" - latin:additionalMoreKeys="7,٧" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8,٨" latin:moreKeys="ﻫ|ه‍" latin:keyLabelFlags="fontNormal" /> <!-- U+062E: "خ" ARABIC LETTER KHAH - U+0668: "٨" ARABIC-INDIC DIGIT EIGHT --> + U+0669: "٩" ARABIC-INDIC DIGIT NINE --> <Key latin:keyLabel="خ" - latin:keyHintLabel="8" - latin:additionalMoreKeys="8,٨" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9,٩" latin:keyLabelFlags="fontNormal" /> <!-- U+062D: "ح" ARABIC LETTER HAH - U+0669: "٩" ARABIC-INDIC DIGIT NINE --> + U+0660: "٠" ARABIC-INDIC DIGIT ZERO --> <Key latin:keyLabel="ح" - latin:keyHintLabel="9" - latin:additionalMoreKeys="9,٩" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0,٠" latin:keyLabelFlags="fontNormal" /> <!-- U+062C: "ج" ARABIC LETTER JEEM - U+0686: "چ" ARABIC LETTER TCHEH - U+0660: "٠" ARABIC-INDIC DIGIT ZERO --> + U+0686: "چ" ARABIC LETTER TCHEH --> <Key latin:keyLabel="ج" - latin:keyHintLabel="0" - latin:additionalMoreKeys="0,٠" latin:moreKeys="چ" latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml/rowkeys_arabic2.xml b/java/res/xml/rowkeys_arabic2.xml index d733f6411..00e69ace7 100644 --- a/java/res/xml/rowkeys_arabic2.xml +++ b/java/res/xml/rowkeys_arabic2.xml @@ -68,11 +68,9 @@ latin:keyLabel="ا" latin:moreKeys="ء,ٱ,أ,إ,آ" latin:keyLabelFlags="fontNormal" /> - <!-- U+062A: "ت" ARABIC LETTER TEH - U+062B: "ﺙ" ARABIC LETTER THEH --> + <!-- U+062A: "ت" ARABIC LETTER TEH --> <Key latin:keyLabel="ت" - latin:moreKeys="ث" latin:keyLabelFlags="fontNormal" /> <!-- U+0646: "ن" ARABIC LETTER NOON --> <Key @@ -89,4 +87,8 @@ latin:keyLabel="ك" latin:moreKeys="گ,ک" latin:keyLabelFlags="fontNormal" /> + <!-- U+0637: "ط" ARABIC LETTER TAH --> + <Key + latin:keyLabel="ط" + latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml/rowkeys_arabic3.xml b/java/res/xml/rowkeys_arabic3.xml index e4e694812..8a17b4b98 100644 --- a/java/res/xml/rowkeys_arabic3.xml +++ b/java/res/xml/rowkeys_arabic3.xml @@ -21,21 +21,33 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- U+0638: "ظ" ARABIC LETTER ZAH --> + <include + latin:keyboardLayout="@xml/keys_arabic3_left" /> + <!-- U+0621: "ء" ARABIC LETTER HAMZA --> <Key - latin:keyLabel="ظ" + latin:keyLabel="ء" latin:keyLabelFlags="fontNormal" /> - <!-- U+0637: "ط" ARABIC LETTER TAH --> + <!-- U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE --> <Key - latin:keyLabel="ط" + latin:keyLabel="ؤ" latin:keyLabelFlags="fontNormal" /> - <!-- U+0630: "ذ" ARABIC LETTER THAL --> + <!-- U+0631: "ر" ARABIC LETTER REH --> <Key - latin:keyLabel="ذ" + latin:keyLabel="ر" latin:keyLabelFlags="fontNormal" /> - <!-- U+062F: "د" ARABIC LETTER DAL --> + <!-- U+0649: "ى" ARABIC LETTER ALEF MAKSURA + U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE --> <Key - latin:keyLabel="د" + latin:keyLabel="ى" + latin:moreKeys="ئ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0629: "ة" ARABIC LETTER TEH MARBUTA --> + <Key + latin:keyLabel="ة" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0648: "و" ARABIC LETTER WAW --> + <Key + latin:keyLabel="و" latin:keyLabelFlags="fontNormal" /> <!-- U+0632: "ز" ARABIC LETTER ZAIN U+0698: "ژ" ARABIC LETTER JEH --> @@ -43,18 +55,12 @@ latin:keyLabel="ز" latin:moreKeys="ژ" latin:keyLabelFlags="fontNormal" /> - <!-- U+0631: "ر" ARABIC LETTER REH --> - <Key - latin:keyLabel="ر" - latin:keyLabelFlags="fontNormal" /> - <!-- U+0629: "ة" ARABIC LETTER TEH MARBUTA --> + <!-- U+0638: "ظ" ARABIC LETTER ZAH --> <Key - latin:keyLabel="ة" + latin:keyLabel="ظ" latin:keyLabelFlags="fontNormal" /> - <!-- U+0648: "و" ARABIC LETTER WAW - U+0624: "ﺅ" ARABIC LETTER WAW WITH HAMZA ABOVE --> + <!-- U+062F: "د" ARABIC LETTER DAL --> <Key - latin:keyLabel="و" - latin:moreKeys="ؤ" + latin:keyLabel="د" latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml/rowkeys_east_slavic1.xml b/java/res/xml/rowkeys_east_slavic1.xml index c1b43bd36..5b3b4b48d 100644 --- a/java/res/xml/rowkeys_east_slavic1.xml +++ b/java/res/xml/rowkeys_east_slavic1.xml @@ -41,7 +41,8 @@ <Key latin:keyLabel="к" latin:keyHintLabel="4" - latin:additionalMoreKeys="4" /> + latin:additionalMoreKeys="4" + latin:moreKeys="!text/more_keys_for_cyrillic_ka" /> <!-- U+0435: "е" CYRILLIC SMALL LETTER IE --> <Key latin:keyLabel="е" diff --git a/java/res/xml/rowkeys_east_slavic2.xml b/java/res/xml/rowkeys_east_slavic2.xml index 9743727c1..2e412f08c 100644 --- a/java/res/xml/rowkeys_east_slavic2.xml +++ b/java/res/xml/rowkeys_east_slavic2.xml @@ -32,7 +32,8 @@ latin:keyLabel="в" /> <!-- U+0430: "а" CYRILLIC SMALL LETTER A --> <Key - latin:keyLabel="а" /> + latin:keyLabel="а" + latin:moreKeys="!text/more_keys_for_cyrillic_a" /> <!-- U+043F: "п" CYRILLIC SMALL LETTER PE --> <Key latin:keyLabel="п" /> @@ -53,5 +54,6 @@ <Key latin:keyLabel="ж" /> <Key - latin:keyLabel="!text/keylabel_for_east_slavic_row2_11" /> + latin:keyLabel="!text/keylabel_for_east_slavic_row2_11" + latin:moreKeys="!text/more_keys_for_east_slavic_row2_11" /> </merge> diff --git a/java/res/xml/rowkeys_farsi1.xml b/java/res/xml/rowkeys_farsi1.xml index 0ccf1ab54..5a22a2462 100644 --- a/java/res/xml/rowkeys_farsi1.xml +++ b/java/res/xml/rowkeys_farsi1.xml @@ -21,81 +21,83 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- U+0635: "ص" ARABIC LETTER SAD - U+0636: "ض" ARABIC LETTER DAD + <!-- U+0636: "ض" ARABIC LETTER DAD U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE --> <Key - latin:keyLabel="ص" - latin:moreKeys="ض,%" + latin:keyLabel="ض" latin:keyHintLabel="۱" latin:additionalMoreKeys="۱,1" latin:keyLabelFlags="fontNormal" /> - <!-- U+0642: "ق" ARABIC LETTER QAF + <!-- U+0635: "ص" ARABIC LETTER SAD U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO --> <Key - latin:keyLabel="ق" + latin:keyLabel="ص" latin:keyHintLabel="۲" latin:additionalMoreKeys="۲,2" latin:keyLabelFlags="fontNormal" /> - <!-- U+0641: "ف" ARABIC LETTER FEH + <!-- U+062B: "ث" ARABIC LETTER THEH U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE --> <Key - latin:keyLabel="ف" + latin:keyLabel="ث" latin:keyHintLabel="۳" latin:additionalMoreKeys="۳,3" latin:keyLabelFlags="fontNormal" /> - <!-- U+063A: "غ" ARABIC LETTER GHAIN + <!-- U+0642: "ق" ARABIC LETTER QAF U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR --> <Key - latin:keyLabel="غ" + latin:keyLabel="ق" latin:keyHintLabel="۴" latin:additionalMoreKeys="۴,4" latin:keyLabelFlags="fontNormal" /> - <!-- U+0639: "ع" ARABIC LETTER AIN + <!-- U+0641: "ف" ARABIC LETTER FEH U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE --> <Key - latin:keyLabel="ع" + latin:keyLabel="ف" latin:keyHintLabel="۵" latin:additionalMoreKeys="۵,5" latin:keyLabelFlags="fontNormal" /> - <!-- 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+063A: "غ" ARABIC LETTER GHAIN U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX --> <Key - latin:keyLabel="ه" - latin:moreKeys="ﻫ|ه‍,هٔ,ة,%" + latin:keyLabel="غ" latin:keyHintLabel="۶" latin:additionalMoreKeys="۶,6" latin:keyLabelFlags="fontNormal" /> - <!-- U+062E: "خ" ARABIC LETTER KHAH + <!-- U+0639: "ع" ARABIC LETTER AIN U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN --> <Key - latin:keyLabel="خ" + latin:keyLabel="ع" latin:keyHintLabel="۷" latin:additionalMoreKeys="۷,7" latin:keyLabelFlags="fontNormal" /> - <!-- U+062D: "ح" ARABIC LETTER HAH + <!-- 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 - latin:keyLabel="ح" + latin:keyLabel="ه" + latin:moreKeys="ﻫ|ه‍,هٔ,ة,%" latin:keyHintLabel="۸" latin:additionalMoreKeys="۸,8" latin:keyLabelFlags="fontNormal" /> - <!-- U+062C: "ج" ARABIC LETTER JEEM + <!-- U+062E: "خ" ARABIC LETTER KHAH U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE --> <Key - latin:keyLabel="ج" + latin:keyLabel="خ" latin:keyHintLabel="۹" latin:additionalMoreKeys="۹,9" latin:keyLabelFlags="fontNormal" /> - <!-- U+0686: "چ" ARABIC LETTER TCHEH + <!-- U+062D: "ح" ARABIC LETTER HAH U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO --> <Key - latin:keyLabel="چ" + latin:keyLabel="ح" latin:keyHintLabel="۰" latin:additionalMoreKeys="۰,0" latin:keyLabelFlags="fontNormal" /> + <!-- U+062C: "ج" ARABIC LETTER JEEM --> + <Key + latin:keyLabel="ج" + latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml/rowkeys_farsi2.xml b/java/res/xml/rowkeys_farsi2.xml index 4b6abe2ab..fc6789eba 100644 --- a/java/res/xml/rowkeys_farsi2.xml +++ b/java/res/xml/rowkeys_farsi2.xml @@ -25,11 +25,9 @@ <Key latin:keyLabel="ش" latin:keyLabelFlags="fontNormal" /> - <!-- U+0633: "س" ARABIC LETTER SEEN - U+0636: "ض" ARABIC LETTER DAD --> + <!-- U+0633: "س" ARABIC LETTER SEEN --> <Key latin:keyLabel="س" - latin:moreKeys="ض" latin:keyLabelFlags="fontNormal" /> <!-- U+06CC: "ی" ARABIC LETTER FARSI YEH U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE @@ -59,11 +57,10 @@ latin:moreKeys="ء,آ,أ,ٱ,إ" latin:keyLabelFlags="fontNormal" /> <!-- U+062A: "ت" ARABIC LETTER TEH - U+062B: "ﺙ" ARABIC LETTER THEH U+0629: "ة": ARABIC LETTER TEH MARBUTA --> <Key latin:keyLabel="ت" - latin:moreKeys="ث,ة" + latin:moreKeys="ة" latin:keyLabelFlags="fontNormal" /> <!-- U+0646: "ن" ARABIC LETTER NOON --> <Key @@ -79,4 +76,8 @@ latin:keyLabel="ک" latin:moreKeys="ك" latin:keyLabelFlags="fontNormal" /> + <!-- U+06AF: "گ" ARABIC LETTER GAF --> + <Key + latin:keyLabel="گ" + latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml/rowkeys_farsi3.xml b/java/res/xml/rowkeys_farsi3.xml index 7d2e81f7d..98949f4c0 100644 --- a/java/res/xml/rowkeys_farsi3.xml +++ b/java/res/xml/rowkeys_farsi3.xml @@ -21,17 +21,21 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- U+0637: "ط" ARABIC LETTER TAH - U+0638: "ظ" ARABIC LETTER ZAH --> + <!-- U+0638: "ظ" ARABIC LETTER ZAH --> + <Key + latin:keyLabel="ظ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0637: "ط" ARABIC LETTER TAH --> <Key latin:keyLabel="ط" - latin:moreKeys="ظ" latin:keyLabelFlags="fontNormal" /> - <!-- U+0632: "ز" ARABIC LETTER ZAIN - U+0698: "ژ" ARABIC LETTER JEH --> + <!-- U+0698: "ژ" ARABIC LETTER JEH --> + <Key + latin:keyLabel="ژ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0632: "ز" ARABIC LETTER ZAIN --> <Key latin:keyLabel="ز" - latin:moreKeys="ژ" latin:keyLabelFlags="fontNormal" /> <!-- U+0631: "ر" ARABIC LETTER REH --> <Key @@ -55,8 +59,6 @@ latin:keyLabel="و" latin:moreKeys="ؤ" latin:keyLabelFlags="fontNormal" /> - <!-- U+06AF: "گ" ARABIC LETTER GAF --> - <Key - latin:keyLabel="گ" - latin:keyLabelFlags="fontNormal" /> + <include + latin:keyboardLayout="@xml/keys_farsi3_right" /> </merge> diff --git a/java/res/xml/rows_arabic.xml b/java/res/xml/rows_arabic.xml index 6449af219..798c23e81 100644 --- a/java/res/xml/rows_arabic.xml +++ b/java/res/xml/rows_arabic.xml @@ -24,27 +24,25 @@ <include latin:keyboardLayout="@xml/key_styles_common" /> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include latin:keyboardLayout="@xml/rowkeys_arabic1" /> </Row> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include latin:keyboardLayout="@xml/rowkeys_arabic2" /> </Row> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include - latin:keyboardLayout="@xml/rowkeys_arabic3" - latin:keyXPos="5.0%p" /> + latin:keyboardLayout="@xml/rowkeys_arabic3" /> <Key latin:keyStyle="deleteKeyStyle" - latin:keyWidth="fillRight" - latin:visualInsetsLeft="1%p" /> + latin:keyWidth="fillRight" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml/rows_farsi.xml b/java/res/xml/rows_farsi.xml index cc0c526b3..c74614fcf 100644 --- a/java/res/xml/rows_farsi.xml +++ b/java/res/xml/rows_farsi.xml @@ -24,27 +24,25 @@ <include latin:keyboardLayout="@xml/key_styles_common" /> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include latin:keyboardLayout="@xml/rowkeys_farsi1" /> </Row> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include latin:keyboardLayout="@xml/rowkeys_farsi2" /> </Row> <Row - latin:keyWidth="10%p" + latin:keyWidth="9.091%p" > <include - latin:keyboardLayout="@xml/rowkeys_farsi3" - latin:keyXPos="5.0%p" /> + latin:keyboardLayout="@xml/rowkeys_farsi3" /> <Key latin:keyStyle="deleteKeyStyle" - latin:keyWidth="fillRight" - latin:visualInsetsLeft="1%p" /> + latin:keyWidth="fillRight" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java index 3bed2c7a0..d5e638e7e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java @@ -28,8 +28,8 @@ import android.util.Log; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; -import com.android.inputmethod.latin.utils.Utils; import java.util.LinkedList; import java.util.Queue; @@ -144,7 +144,7 @@ public final class ActionBatch { // DownloadManager also stupidly cuts the extension to replace with its own that it // gets from the content-type. We need to circumvent this. final String disambiguator = "#" + System.currentTimeMillis() - + Utils.getVersionName(context) + ".dict"; + + ApplicationUtils.getVersionName(context) + ".dict"; final Uri uri = Uri.parse(mWordList.mRemoteFilename + disambiguator); final Request request = new Request(uri); diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index 6e3dd7109..767f895dc 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -22,7 +22,6 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; -import android.text.format.DateUtils; import android.util.Log; import android.widget.Toast; @@ -30,6 +29,7 @@ import com.android.inputmethod.latin.R; import java.util.Locale; import java.util.Random; +import java.util.concurrent.TimeUnit; /** * Service that handles background tasks for the dictionary provider. @@ -77,19 +77,19 @@ public final class DictionaryService extends Service { * How often, in milliseconds, we want to update the metadata. This is a * floor value; actually, it may happen several hours later, or even more. */ - private static final long UPDATE_FREQUENCY = 4 * DateUtils.DAY_IN_MILLIS; + private static final long UPDATE_FREQUENCY = TimeUnit.DAYS.toMillis(4); /** * We are waked around midnight, local time. We want to wake between midnight and 6 am, * roughly. So use a random time between 0 and this delay. */ - private static final int MAX_ALARM_DELAY = 6 * ((int)AlarmManager.INTERVAL_HOUR); + private static final int MAX_ALARM_DELAY = (int)TimeUnit.HOURS.toMillis(6); /** * How long we consider a "very long time". If no update took place in this time, * the content provider will trigger an update in the background. */ - private static final long VERY_LONG_TIME = 14 * DateUtils.DAY_IN_MILLIS; + private static final long VERY_LONG_TIME = TimeUnit.DAYS.toMillis(14); /** * The last seen start Id. This must be stored because we must only call stopSelfResult() with @@ -170,7 +170,7 @@ public final class DictionaryService extends Service { checkTimeAndMaybeSetupUpdateAlarm(context); } else if (DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION.equals(intent.getAction())) { // Intent to trigger an update now. - UpdateHandler.update(context, false); + UpdateHandler.tryUpdate(context, false); } else { UpdateHandler.downloadFinished(context, intent); } @@ -221,7 +221,7 @@ public final class DictionaryService extends Service { */ public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) { if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return; - UpdateHandler.update(context, false); + UpdateHandler.tryUpdate(context, false); } /** diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index 4b89d20bb..7bbd041e7 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import android.view.animation.AnimationUtils; @@ -104,9 +105,16 @@ public final class DictionarySettingsFragment extends PreferenceFragment @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - mUpdateNowMenu = menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now); - mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - refreshNetworkState(); + final String metadataUri = + MetadataDbHelper.getMetadataUriAsString(getActivity(), mClientId); + // We only add the "Refresh" button if we have a non-empty URL to refresh from. If the + // URL is empty, of course we can't refresh so it makes no sense to display this. + if (!TextUtils.isEmpty(metadataUri)) { + mUpdateNowMenu = + menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now); + mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + refreshNetworkState(); + } } @Override @@ -353,7 +361,12 @@ public final class DictionarySettingsFragment extends PreferenceFragment new Thread("updateByHand") { @Override public void run() { - UpdateHandler.update(activity, true); + // We call tryUpdate(), which returns whether we could successfully start an update. + // If we couldn't, we'll never receive the end callback, so we stop the loading + // animation and return to the previous screen. + if (!UpdateHandler.tryUpdate(activity, true)) { + stopLoadingAnimation(); + } } }.start(); } @@ -368,7 +381,9 @@ public final class DictionarySettingsFragment extends PreferenceFragment private void startLoadingAnimation() { mLoadingView.setVisibility(View.VISIBLE); getView().setVisibility(View.GONE); - mUpdateNowMenu.setTitle(R.string.cancel); + // We come here when the menu element is pressed so presumably it can't be null. But + // better safe than sorry. + if (null != mUpdateNowMenu) mUpdateNowMenu.setTitle(R.string.cancel); } private void stopLoadingAnimation() { diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index 719f24e59..f66ef8733 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -38,8 +38,8 @@ import android.util.Log; import com.android.inputmethod.compat.ConnectivityManagerCompatUtils; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; -import com.android.inputmethod.latin.utils.Utils; import java.io.File; import java.io.FileInputStream; @@ -173,14 +173,15 @@ public final class UpdateHandler { * Download latest metadata from the server through DownloadManager for all known clients * @param context The context for retrieving resources * @param updateNow Whether we should update NOW, or respect bandwidth policies + * @return true if an update successfully started, false otherwise. */ - public static void update(final Context context, final boolean updateNow) { + public static boolean tryUpdate(final Context context, final boolean updateNow) { // TODO: loop through all clients instead of only doing the default one. final TreeSet<String> uris = new TreeSet<String>(); final Cursor cursor = MetadataDbHelper.queryClientIds(context); - if (null == cursor) return; + if (null == cursor) return false; try { - if (!cursor.moveToFirst()) return; + if (!cursor.moveToFirst()) return false; do { final String clientId = cursor.getString(0); final String metadataUri = @@ -192,6 +193,7 @@ public final class UpdateHandler { } finally { cursor.close(); } + boolean started = false; for (final String metadataUri : uris) { if (!TextUtils.isEmpty(metadataUri)) { // If the metadata URI is empty, that means we should never update it at all. @@ -200,8 +202,10 @@ public final class UpdateHandler { // is a bug and it happens anyway, doing nothing is the right thing to do. // For more information, {@see DictionaryProvider#insert(Uri, ContentValues)}. updateClientsWithMetadataUri(context, updateNow, metadataUri); + started = true; } } + return started; } /** @@ -218,7 +222,7 @@ public final class UpdateHandler { // DownloadManager also stupidly cuts the extension to replace with its own that it // gets from the content-type. We need to circumvent this. final String disambiguator = "#" + System.currentTimeMillis() - + Utils.getVersionName(context) + ".json"; + + ApplicationUtils.getVersionName(context) + ".json"; final Request metadataRequest = new Request(Uri.parse(metadataUri + disambiguator)); DebugLogUtils.l("Request =", metadataRequest); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 9eeee5baf..00ea20d2c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -20,7 +20,6 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; public interface KeyboardActionListener { - /** * Called when the user presses a key. This is sent before the {@link #onCodeInput} is called. * For keys that repeat, this is only called once. @@ -99,9 +98,9 @@ public interface KeyboardActionListener { */ public boolean onCustomRequest(int requestCode); - public static class Adapter implements KeyboardActionListener { - public static final Adapter EMPTY_LISTENER = new Adapter(); + public static final KeyboardActionListener EMPTY_LISTENER = new Adapter(); + public static class Adapter implements KeyboardActionListener { @Override public void onPressKey(int primaryCode, boolean isSinglePointer) {} @Override diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 4323f7171..98e2baee2 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -31,7 +31,6 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyboardState; -import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; @@ -210,7 +209,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public void onPressKey(final int code, final boolean isSinglePointer) { - hapticAndAudioFeedback(code); mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState()); } @@ -299,13 +297,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { ? keyboardView.getTimerProxy().isInDoubleTapShiftKeyTimeout() : false; } - private void hapticAndAudioFeedback(final int code) { - if (mKeyboardView == null || mKeyboardView.isInSlidingKeyInput()) { - return; - } - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, mKeyboardView); - } - /** * Updates state machine to figure out when to automatically switch back to the previous mode. */ diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 4cee4cfa6..254b20b87 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -37,6 +37,7 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; import com.android.inputmethod.research.ResearchLogger; import java.util.HashSet; diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 8a926d655..f85e60449 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -57,10 +57,8 @@ import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview; import com.android.inputmethod.keyboard.internal.TouchScreenRegulator; -import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.DebugSettings; -import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; @@ -72,7 +70,9 @@ import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.StringUtils; -import com.android.inputmethod.latin.utils.Utils.UsabilityStudyLogUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; +import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; +import com.android.inputmethod.latin.utils.ViewLayoutUtils; import com.android.inputmethod.research.ResearchLogger; import java.util.Locale; @@ -240,11 +240,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack break; case MSG_REPEAT_KEY: final Key currentKey = tracker.getKey(); - if (currentKey != null && currentKey.mCode == msg.arg1) { - tracker.onRepeatKey(currentKey); - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback( - currentKey.mCode, keyboardView); + final int code = msg.arg1; + if (currentKey != null && currentKey.mCode == code) { startKeyRepeatTimer(tracker, mKeyRepeatInterval); + startTypingStateTimer(currentKey); + final KeyboardActionListener listener = + keyboardView.getKeyboardActionListener(); + listener.onPressKey(code, true /* isSinglePointer */); + listener.onCodeInput(code, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } break; case MSG_LONGPRESS_KEY: @@ -564,6 +568,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack altCodeKeyWhileTypingFadeoutAnimatorResId, this); mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator( altCodeKeyWhileTypingFadeinAnimatorResId, this); + + mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; } private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { @@ -977,39 +983,28 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.mainKeyboardView_onLongPress(); } - final int code = key.mCode; + final KeyboardActionListener listener = mKeyboardActionListener; if (key.hasNoPanelAutoMoreKey()) { - final int embeddedCode = key.mMoreKeys[0].mCode; + final int moreKeyCode = key.mMoreKeys[0].mCode; tracker.onLongPressed(); - invokeCodeInput(embeddedCode); - invokeReleaseKey(code); - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, this); + listener.onPressKey(moreKeyCode, true /* isSinglePointer */); + listener.onCodeInput(moreKeyCode, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); + listener.onReleaseKey(moreKeyCode, false /* withSliding */); return; } + final int code = key.mCode; if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) { // Long pressing the space key invokes IME switcher dialog. - if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { + if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) { tracker.onLongPressed(); - invokeReleaseKey(code); + listener.onReleaseKey(code, false /* withSliding */); return; } } openMoreKeysPanel(key, tracker); } - private boolean invokeCustomRequest(final int requestCode) { - return mKeyboardActionListener.onCustomRequest(requestCode); - } - - private void invokeCodeInput(final int code) { - mKeyboardActionListener.onCodeInput( - code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } - - private void invokeReleaseKey(final int code) { - mKeyboardActionListener.onReleaseKey(code, false); - } - private void openMoreKeysPanel(final Key key, final PointerTracker tracker) { final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext()); if (moreKeysPanel == null) { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index 51f5446db..d29e6382c 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -29,6 +29,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreKeysKeyboard extends Keyboard { private final int mDefaultKeyCoordX; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 7b14259a0..53207597a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -175,7 +175,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private DrawingProxy mDrawingProxy; private TimerProxy mTimerProxy; private KeyDetector mKeyDetector; - private KeyboardActionListener mListener = KeyboardActionListener.Adapter.EMPTY_LISTENER; + private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER; private Keyboard mKeyboard; private int mPhantonSuddenMoveThreshold; @@ -1263,13 +1263,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (!key.isRepeatable()) return; // Don't start key repeat when we are in sliding input mode. if (mIsInSlidingKeyInput) return; - onRepeatKey(key); - mTimerProxy.startKeyRepeatTimer(this); - } - - public void onRepeatKey(final Key key) { detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis()); - mTimerProxy.startTypingStateTimer(key); + mTimerProxy.startKeyRepeatTimer(this); } private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 370d0cb93..8ead44c31 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -81,9 +81,6 @@ public final class KeyboardState { private boolean mPrevSymbolsKeyboardWasShifted; private int mRecapitalizeMode; - // For handling long press. - private boolean mLongPressShiftLockFired; - // For handling double tap. private boolean mIsInAlphabetUnshiftedFromShifted; private boolean mIsInDoubleTapShiftKey; @@ -325,10 +322,11 @@ public final class KeyboardState { } if (code == Constants.CODE_SHIFT) { onPressShift(); + } else if (code == Constants.CODE_CAPSLOCK) { + // Nothing to do here. See {@link #onReleaseKey(int,boolean)}. } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { onPressSymbol(); } else { - mLongPressShiftLockFired = false; mShiftKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed(); // It is required to reset the auto caps state when all of the following conditions @@ -356,6 +354,8 @@ public final class KeyboardState { } if (code == Constants.CODE_SHIFT) { onReleaseShift(withSliding); + } else if (code == Constants.CODE_CAPSLOCK) { + setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { onReleaseSymbol(withSliding); } @@ -437,7 +437,6 @@ public final class KeyboardState { } private void onPressShift() { - mLongPressShiftLockFired = false; // If we are recapitalizing, we don't do any of the normal processing, including // importantly the double tap timer. if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { @@ -499,8 +498,6 @@ public final class KeyboardState { // Double tap shift key has been handled in {@link #onPressShift}, so that just // ignore this release shift key here. mIsInDoubleTapShiftKey = false; - } else if (mLongPressShiftLockFired) { - setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (mShiftKeyState.isChording()) { if (mAlphabetShiftState.isShiftLockShifted()) { // After chording input while shift locked state. @@ -610,12 +607,6 @@ public final class KeyboardState { break; } - if (code == Constants.CODE_CAPSLOCK) { - // Changing shift lock state will be handled at {@link #onPressShift()} when the shift - // key is released. - mLongPressShiftLockFired = true; - } - // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 467c15ffd..1594df75b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -133,122 +133,125 @@ public final class KeyboardTextsSet { /* 28 */ "keylabel_for_east_slavic_row2_11", /* 29 */ "keylabel_for_east_slavic_row3_5", /* 30 */ "more_keys_for_cyrillic_u", - /* 31 */ "more_keys_for_cyrillic_en", - /* 32 */ "more_keys_for_cyrillic_ghe", - /* 33 */ "more_keys_for_east_slavic_row2_1", - /* 34 */ "more_keys_for_cyrillic_o", - /* 35 */ "more_keys_for_cyrillic_soft_sign", - /* 36 */ "keylabel_for_south_slavic_row1_6", - /* 37 */ "keylabel_for_south_slavic_row2_11", - /* 38 */ "keylabel_for_south_slavic_row3_1", - /* 39 */ "keylabel_for_south_slavic_row3_8", - /* 40 */ "more_keys_for_cyrillic_ie", - /* 41 */ "more_keys_for_cyrillic_i", - /* 42 */ "label_to_alpha_key", - /* 43 */ "single_quotes", - /* 44 */ "double_quotes", - /* 45 */ "single_angle_quotes", - /* 46 */ "double_angle_quotes", - /* 47 */ "more_keys_for_currency_dollar", - /* 48 */ "keylabel_for_currency_generic", - /* 49 */ "more_keys_for_currency_generic", - /* 50 */ "more_keys_for_punctuation", - /* 51 */ "more_keys_for_star", - /* 52 */ "more_keys_for_bullet", - /* 53 */ "more_keys_for_plus", - /* 54 */ "more_keys_for_left_parenthesis", - /* 55 */ "more_keys_for_right_parenthesis", - /* 56 */ "more_keys_for_less_than", - /* 57 */ "more_keys_for_greater_than", - /* 58 */ "more_keys_for_arabic_diacritics", - /* 59 */ "keyhintlabel_for_arabic_diacritics", - /* 60 */ "keylabel_for_symbols_1", - /* 61 */ "keylabel_for_symbols_2", - /* 62 */ "keylabel_for_symbols_3", - /* 63 */ "keylabel_for_symbols_4", - /* 64 */ "keylabel_for_symbols_5", - /* 65 */ "keylabel_for_symbols_6", - /* 66 */ "keylabel_for_symbols_7", - /* 67 */ "keylabel_for_symbols_8", - /* 68 */ "keylabel_for_symbols_9", - /* 69 */ "keylabel_for_symbols_0", - /* 70 */ "label_to_symbol_key", - /* 71 */ "label_to_symbol_with_microphone_key", - /* 72 */ "additional_more_keys_for_symbols_1", - /* 73 */ "additional_more_keys_for_symbols_2", - /* 74 */ "additional_more_keys_for_symbols_3", - /* 75 */ "additional_more_keys_for_symbols_4", - /* 76 */ "additional_more_keys_for_symbols_5", - /* 77 */ "additional_more_keys_for_symbols_6", - /* 78 */ "additional_more_keys_for_symbols_7", - /* 79 */ "additional_more_keys_for_symbols_8", - /* 80 */ "additional_more_keys_for_symbols_9", - /* 81 */ "additional_more_keys_for_symbols_0", - /* 82 */ "more_keys_for_symbols_1", - /* 83 */ "more_keys_for_symbols_2", - /* 84 */ "more_keys_for_symbols_3", - /* 85 */ "more_keys_for_symbols_4", - /* 86 */ "more_keys_for_symbols_5", - /* 87 */ "more_keys_for_symbols_6", - /* 88 */ "more_keys_for_symbols_7", - /* 89 */ "more_keys_for_symbols_8", - /* 90 */ "more_keys_for_symbols_9", - /* 91 */ "more_keys_for_symbols_0", - /* 92 */ "keylabel_for_comma", - /* 93 */ "more_keys_for_comma", - /* 94 */ "keylabel_for_symbols_question", - /* 95 */ "keylabel_for_symbols_semicolon", - /* 96 */ "keylabel_for_symbols_percent", - /* 97 */ "more_keys_for_symbols_exclamation", - /* 98 */ "more_keys_for_symbols_question", - /* 99 */ "more_keys_for_symbols_semicolon", - /* 100 */ "more_keys_for_symbols_percent", - /* 101 */ "keylabel_for_tablet_comma", - /* 102 */ "keyhintlabel_for_tablet_comma", - /* 103 */ "more_keys_for_tablet_comma", - /* 104 */ "keyhintlabel_for_tablet_period", - /* 105 */ "more_keys_for_tablet_period", - /* 106 */ "keylabel_for_apostrophe", - /* 107 */ "keyhintlabel_for_apostrophe", - /* 108 */ "more_keys_for_apostrophe", - /* 109 */ "more_keys_for_q", - /* 110 */ "more_keys_for_x", - /* 111 */ "keylabel_for_q", - /* 112 */ "keylabel_for_w", - /* 113 */ "keylabel_for_y", - /* 114 */ "keylabel_for_x", - /* 115 */ "keylabel_for_spanish_row2_10", - /* 116 */ "more_keys_for_am_pm", - /* 117 */ "settings_as_more_key", - /* 118 */ "shortcut_as_more_key", - /* 119 */ "action_next_as_more_key", - /* 120 */ "action_previous_as_more_key", - /* 121 */ "label_to_more_symbol_key", - /* 122 */ "label_to_more_symbol_for_tablet_key", - /* 123 */ "label_tab_key", - /* 124 */ "label_to_phone_numeric_key", - /* 125 */ "label_to_phone_symbols_key", - /* 126 */ "label_time_am", - /* 127 */ "label_time_pm", - /* 128 */ "label_to_symbol_key_pcqwerty", - /* 129 */ "keylabel_for_popular_domain", - /* 130 */ "more_keys_for_popular_domain", - /* 131 */ "more_keys_for_smiley", - /* 132 */ "single_laqm_raqm", - /* 133 */ "single_laqm_raqm_rtl", - /* 134 */ "single_raqm_laqm", - /* 135 */ "double_laqm_raqm", - /* 136 */ "double_laqm_raqm_rtl", - /* 137 */ "double_raqm_laqm", - /* 138 */ "single_lqm_rqm", - /* 139 */ "single_9qm_lqm", - /* 140 */ "single_9qm_rqm", - /* 141 */ "double_lqm_rqm", - /* 142 */ "double_9qm_lqm", - /* 143 */ "double_9qm_rqm", - /* 144 */ "more_keys_for_single_quote", - /* 145 */ "more_keys_for_double_quote", - /* 146 */ "more_keys_for_tablet_double_quote", + /* 31 */ "more_keys_for_cyrillic_ka", + /* 32 */ "more_keys_for_cyrillic_en", + /* 33 */ "more_keys_for_cyrillic_ghe", + /* 34 */ "more_keys_for_east_slavic_row2_1", + /* 35 */ "more_keys_for_cyrillic_a", + /* 36 */ "more_keys_for_cyrillic_o", + /* 37 */ "more_keys_for_cyrillic_soft_sign", + /* 38 */ "more_keys_for_east_slavic_row2_11", + /* 39 */ "keylabel_for_south_slavic_row1_6", + /* 40 */ "keylabel_for_south_slavic_row2_11", + /* 41 */ "keylabel_for_south_slavic_row3_1", + /* 42 */ "keylabel_for_south_slavic_row3_8", + /* 43 */ "more_keys_for_cyrillic_ie", + /* 44 */ "more_keys_for_cyrillic_i", + /* 45 */ "label_to_alpha_key", + /* 46 */ "single_quotes", + /* 47 */ "double_quotes", + /* 48 */ "single_angle_quotes", + /* 49 */ "double_angle_quotes", + /* 50 */ "more_keys_for_currency_dollar", + /* 51 */ "keylabel_for_currency_generic", + /* 52 */ "more_keys_for_currency_generic", + /* 53 */ "more_keys_for_punctuation", + /* 54 */ "more_keys_for_star", + /* 55 */ "more_keys_for_bullet", + /* 56 */ "more_keys_for_plus", + /* 57 */ "more_keys_for_left_parenthesis", + /* 58 */ "more_keys_for_right_parenthesis", + /* 59 */ "more_keys_for_less_than", + /* 60 */ "more_keys_for_greater_than", + /* 61 */ "more_keys_for_arabic_diacritics", + /* 62 */ "keyhintlabel_for_arabic_diacritics", + /* 63 */ "keylabel_for_symbols_1", + /* 64 */ "keylabel_for_symbols_2", + /* 65 */ "keylabel_for_symbols_3", + /* 66 */ "keylabel_for_symbols_4", + /* 67 */ "keylabel_for_symbols_5", + /* 68 */ "keylabel_for_symbols_6", + /* 69 */ "keylabel_for_symbols_7", + /* 70 */ "keylabel_for_symbols_8", + /* 71 */ "keylabel_for_symbols_9", + /* 72 */ "keylabel_for_symbols_0", + /* 73 */ "label_to_symbol_key", + /* 74 */ "label_to_symbol_with_microphone_key", + /* 75 */ "additional_more_keys_for_symbols_1", + /* 76 */ "additional_more_keys_for_symbols_2", + /* 77 */ "additional_more_keys_for_symbols_3", + /* 78 */ "additional_more_keys_for_symbols_4", + /* 79 */ "additional_more_keys_for_symbols_5", + /* 80 */ "additional_more_keys_for_symbols_6", + /* 81 */ "additional_more_keys_for_symbols_7", + /* 82 */ "additional_more_keys_for_symbols_8", + /* 83 */ "additional_more_keys_for_symbols_9", + /* 84 */ "additional_more_keys_for_symbols_0", + /* 85 */ "more_keys_for_symbols_1", + /* 86 */ "more_keys_for_symbols_2", + /* 87 */ "more_keys_for_symbols_3", + /* 88 */ "more_keys_for_symbols_4", + /* 89 */ "more_keys_for_symbols_5", + /* 90 */ "more_keys_for_symbols_6", + /* 91 */ "more_keys_for_symbols_7", + /* 92 */ "more_keys_for_symbols_8", + /* 93 */ "more_keys_for_symbols_9", + /* 94 */ "more_keys_for_symbols_0", + /* 95 */ "keylabel_for_comma", + /* 96 */ "more_keys_for_comma", + /* 97 */ "keylabel_for_symbols_question", + /* 98 */ "keylabel_for_symbols_semicolon", + /* 99 */ "keylabel_for_symbols_percent", + /* 100 */ "more_keys_for_symbols_exclamation", + /* 101 */ "more_keys_for_symbols_question", + /* 102 */ "more_keys_for_symbols_semicolon", + /* 103 */ "more_keys_for_symbols_percent", + /* 104 */ "keylabel_for_tablet_comma", + /* 105 */ "keyhintlabel_for_tablet_comma", + /* 106 */ "more_keys_for_tablet_comma", + /* 107 */ "keyhintlabel_for_tablet_period", + /* 108 */ "more_keys_for_tablet_period", + /* 109 */ "keylabel_for_apostrophe", + /* 110 */ "keyhintlabel_for_apostrophe", + /* 111 */ "more_keys_for_apostrophe", + /* 112 */ "more_keys_for_q", + /* 113 */ "more_keys_for_x", + /* 114 */ "keylabel_for_q", + /* 115 */ "keylabel_for_w", + /* 116 */ "keylabel_for_y", + /* 117 */ "keylabel_for_x", + /* 118 */ "keylabel_for_spanish_row2_10", + /* 119 */ "more_keys_for_am_pm", + /* 120 */ "settings_as_more_key", + /* 121 */ "shortcut_as_more_key", + /* 122 */ "action_next_as_more_key", + /* 123 */ "action_previous_as_more_key", + /* 124 */ "label_to_more_symbol_key", + /* 125 */ "label_to_more_symbol_for_tablet_key", + /* 126 */ "label_tab_key", + /* 127 */ "label_to_phone_numeric_key", + /* 128 */ "label_to_phone_symbols_key", + /* 129 */ "label_time_am", + /* 130 */ "label_time_pm", + /* 131 */ "label_to_symbol_key_pcqwerty", + /* 132 */ "keylabel_for_popular_domain", + /* 133 */ "more_keys_for_popular_domain", + /* 134 */ "more_keys_for_smiley", + /* 135 */ "single_laqm_raqm", + /* 136 */ "single_laqm_raqm_rtl", + /* 137 */ "single_raqm_laqm", + /* 138 */ "double_laqm_raqm", + /* 139 */ "double_laqm_raqm_rtl", + /* 140 */ "double_raqm_laqm", + /* 141 */ "single_lqm_rqm", + /* 142 */ "single_9qm_lqm", + /* 143 */ "single_9qm_rqm", + /* 144 */ "double_lqm_rqm", + /* 145 */ "double_9qm_lqm", + /* 146 */ "double_9qm_rqm", + /* 147 */ "more_keys_for_single_quote", + /* 148 */ "more_keys_for_double_quote", + /* 149 */ "more_keys_for_tablet_double_quote", }; private static final String EMPTY = ""; @@ -259,147 +262,147 @@ public final class KeyboardTextsSet { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - EMPTY, EMPTY, EMPTY, - /* ~41 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~44 */ // Label for "switch to alphabetic" key. - /* 42 */ "ABC", - /* 43 */ "!text/single_lqm_rqm", - /* 44 */ "!text/double_lqm_rqm", - /* 45 */ "!text/single_laqm_raqm", - /* 46 */ "!text/double_laqm_raqm", + /* 45 */ "ABC", + /* 46 */ "!text/single_lqm_rqm", + /* 47 */ "!text/double_lqm_rqm", + /* 48 */ "!text/single_laqm_raqm", + /* 49 */ "!text/double_laqm_raqm", // U+00A2: "¢" CENT SIGN // U+00A3: "£" POUND SIGN // U+20AC: "€" EURO SIGN // U+00A5: "¥" YEN SIGN // U+20B1: "₱" PESO SIGN - /* 47 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", - /* 48 */ "$", - /* 49 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + /* 51 */ "$", + /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR - /* 51 */ "\u2020,\u2021,\u2605", + /* 54 */ "\u2020,\u2021,\u2605", // U+266A: "♪" EIGHTH NOTE // U+2665: "♥" BLACK HEART SUIT // U+2660: "♠" BLACK SPADE SUIT // U+2666: "♦" BLACK DIAMOND SUIT // U+2663: "♣" BLACK CLUB SUIT - /* 52 */ "\u266A,\u2665,\u2660,\u2666,\u2663", + /* 55 */ "\u266A,\u2665,\u2660,\u2666,\u2663", // U+00B1: "±" PLUS-MINUS SIGN - /* 53 */ "\u00B1", + /* 56 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 54 */ "!fixedColumnOrder!3,<,{,[", - /* 55 */ "!fixedColumnOrder!3,>,},]", + /* 57 */ "!fixedColumnOrder!3,<,{,[", + /* 58 */ "!fixedColumnOrder!3,>,},]", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /* 56 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", - /* 57 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", - /* 58 */ EMPTY, - /* 59 */ EMPTY, - /* 60 */ "1", - /* 61 */ "2", - /* 62 */ "3", - /* 63 */ "4", - /* 64 */ "5", - /* 65 */ "6", - /* 66 */ "7", - /* 67 */ "8", - /* 68 */ "9", - /* 69 */ "0", + /* 59 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", + /* 60 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", + /* 61 */ EMPTY, + /* 62 */ EMPTY, + /* 63 */ "1", + /* 64 */ "2", + /* 65 */ "3", + /* 66 */ "4", + /* 67 */ "5", + /* 68 */ "6", + /* 69 */ "7", + /* 70 */ "8", + /* 71 */ "9", + /* 72 */ "0", // Label for "switch to symbols" key. - /* 70 */ "?123", + /* 73 */ "?123", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "123", - /* 72~ */ + /* 74 */ "123", + /* 75~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~81 */ + /* ~84 */ // 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 - /* 82 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", + /* 85 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", // U+00B2: "²" SUPERSCRIPT TWO // U+2154: "⅔" VULGAR FRACTION TWO THIRDS - /* 83 */ "\u00B2,\u2154", + /* 86 */ "\u00B2,\u2154", // U+00B3: "³" SUPERSCRIPT THREE // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS - /* 84 */ "\u00B3,\u00BE,\u215C", + /* 87 */ "\u00B3,\u00BE,\u215C", // U+2074: "⁴" SUPERSCRIPT FOUR - /* 85 */ "\u2074", + /* 88 */ "\u2074", // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS - /* 86 */ "\u215D", - /* 87 */ EMPTY, - // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS - /* 88 */ "\u215E", - /* 89 */ EMPTY, + /* 89 */ "\u215D", /* 90 */ EMPTY, + // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS + /* 91 */ "\u215E", + /* 92 */ EMPTY, + /* 93 */ EMPTY, // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N // U+2205: "∅" EMPTY SET - /* 91 */ "\u207F,\u2205", - /* 92 */ ",", - /* 93 */ EMPTY, - /* 94 */ "?", - /* 95 */ ";", - /* 96 */ "%", + /* 94 */ "\u207F,\u2205", + /* 95 */ ",", + /* 96 */ EMPTY, + /* 97 */ "?", + /* 98 */ ";", + /* 99 */ "%", // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 97 */ "\u00A1", + /* 100 */ "\u00A1", // U+00BF: "¿" INVERTED QUESTION MARK - /* 98 */ "\u00BF", - /* 99 */ EMPTY, + /* 101 */ "\u00BF", + /* 102 */ EMPTY, // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\u2030", - /* 101 */ ",", - /* 102 */ "!", - /* 103 */ "!", - /* 104 */ "?", - /* 105 */ "?", - /* 106 */ "\'", - /* 107 */ "\"", - /* 108 */ "\"", - /* 109 */ EMPTY, - /* 110 */ EMPTY, - /* 111 */ "q", - /* 112 */ "w", - /* 113 */ "y", - /* 114 */ "x", + /* 103 */ "\u2030", + /* 104 */ ",", + /* 105 */ "!", + /* 106 */ "!", + /* 107 */ "?", + /* 108 */ "?", + /* 109 */ "\'", + /* 110 */ "\"", + /* 111 */ "\"", + /* 112 */ EMPTY, + /* 113 */ EMPTY, + /* 114 */ "q", + /* 115 */ "w", + /* 116 */ "y", + /* 117 */ "x", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE - /* 115 */ "\u00F1", - /* 116 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", - /* 117 */ "!icon/settings_key|!code/key_settings", - /* 118 */ "!icon/shortcut_key|!code/key_shortcut", - /* 119 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", - /* 120 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + /* 118 */ "\u00F1", + /* 119 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", + /* 120 */ "!icon/settings_key|!code/key_settings", + /* 121 */ "!icon/shortcut_key|!code/key_shortcut", + /* 122 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", + /* 123 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", // Label for "switch to more symbol" modifier key. Must be short to fit on key! - /* 121 */ "= \\ <", + /* 124 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 122 */ "~ \\ {", + /* 125 */ "~ \\ {", // Label for "Tab" key. Must be short to fit on key! - /* 123 */ "Tab", + /* 126 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 124 */ "123", + /* 127 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 125 */ "\uFF0A\uFF03", + /* 128 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 126 */ "AM", + /* 129 */ "AM", // Key label for "post meridiem" - /* 127 */ "PM", + /* 130 */ "PM", // Label for "switch to symbols" key on PC QWERTY layout - /* 128 */ "Sym", - /* 129 */ ".com", + /* 131 */ "Sym", + /* 132 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 130 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 131 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 133 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 134 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -421,24 +424,24 @@ public final class KeyboardTextsSet { // The following each quotation mark pair consist of // <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 132 */ "\u2039,\u203A", - /* 133 */ "\u2039|\u203A,\u203A|\u2039", - /* 134 */ "\u203A,\u2039", - /* 135 */ "\u00AB,\u00BB", - /* 136 */ "\u00AB|\u00BB,\u00BB|\u00AB", - /* 137 */ "\u00BB,\u00AB", + /* 135 */ "\u2039,\u203A", + /* 136 */ "\u2039|\u203A,\u203A|\u2039", + /* 137 */ "\u203A,\u2039", + /* 138 */ "\u00AB,\u00BB", + /* 139 */ "\u00AB|\u00BB,\u00BB|\u00AB", + /* 140 */ "\u00BB,\u00AB", // The following each quotation mark triplet consists of // <another quotation mark>, <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 138 */ "\u201A,\u2018,\u2019", - /* 139 */ "\u2019,\u201A,\u2018", - /* 140 */ "\u2018,\u201A,\u2019", - /* 141 */ "\u201E,\u201C,\u201D", - /* 142 */ "\u201D,\u201E,\u201C", - /* 143 */ "\u201C,\u201E,\u201D", - /* 144 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", - /* 145 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", - /* 146 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", + /* 141 */ "\u201A,\u2018,\u2019", + /* 142 */ "\u2019,\u201A,\u2018", + /* 143 */ "\u2018,\u201A,\u2019", + /* 144 */ "\u201E,\u201C,\u201D", + /* 145 */ "\u201D,\u201E,\u201C", + /* 146 */ "\u201C,\u201E,\u201D", + /* 147 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", + /* 148 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", + /* 149 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", }; /* Language af: Afrikaans */ @@ -499,45 +502,45 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0623: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "پ" ARABIC LETTER PEH - /* 42 */ "\u0623\u200C\u0628\u200C\u062C", - /* 43 */ null, - /* 44 */ null, - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 45 */ "\u0623\u200C\u0628\u200C\u062C", + /* 46 */ null, + /* 47 */ null, + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, - /* ~49 */ + /* ~52 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 51 */ "\u2605,\u066D", + /* 54 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 52 */ "\u266A", - /* 53 */ null, + /* 55 */ "\u266A", + /* 56 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 54 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0654: "ٔ" ARABIC HAMZA ABOVE // U+0652: "ْ" ARABIC SUKUN @@ -554,70 +557,115 @@ public final class KeyboardTextsSet { // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. - /* 58 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640", - /* 59 */ "\u0651", + /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640", + /* 62 */ "\u0651", // U+0661: "١" ARABIC-INDIC DIGIT ONE - /* 60 */ "\u0661", + /* 63 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO - /* 61 */ "\u0662", + /* 64 */ "\u0662", // U+0663: "٣" ARABIC-INDIC DIGIT THREE - /* 62 */ "\u0663", + /* 65 */ "\u0663", // U+0664: "٤" ARABIC-INDIC DIGIT FOUR - /* 63 */ "\u0664", + /* 66 */ "\u0664", // U+0665: "٥" ARABIC-INDIC DIGIT FIVE - /* 64 */ "\u0665", + /* 67 */ "\u0665", // U+0666: "٦" ARABIC-INDIC DIGIT SIX - /* 65 */ "\u0666", + /* 68 */ "\u0666", // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN - /* 66 */ "\u0667", + /* 69 */ "\u0667", // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT - /* 67 */ "\u0668", + /* 70 */ "\u0668", // U+0669: "٩" ARABIC-INDIC DIGIT NINE - /* 68 */ "\u0669", + /* 71 */ "\u0669", // U+0660: "٠" ARABIC-INDIC DIGIT ZERO - /* 69 */ "\u0660", + /* 72 */ "\u0660", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 70 */ "\u0663\u0662\u0661\u061F", + /* 73 */ "\u0663\u0662\u0661\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u0663\u0662\u0661", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", + /* 74 */ "\u0663\u0662\u0661", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 81 */ "0,\u066B,\u066C", - /* 82~ */ + /* 84 */ "0,\u066B,\u066C", + /* 85~ */ null, null, null, null, null, null, null, null, null, null, - /* ~91 */ + /* ~94 */ // U+060C: "،" ARABIC COMMA - /* 92 */ "\u060C", - /* 93 */ "\\,", - /* 94 */ "\u061F", - /* 95 */ "\u061B", + /* 95 */ "\u060C", + /* 96 */ "\\,", + /* 97 */ "\u061F", + /* 98 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 96 */ "\u066A", - /* 97 */ null, - /* 98 */ "?", - /* 99 */ ";", + /* 99 */ "\u066A", + /* 100 */ null, + /* 101 */ "?", + /* 102 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\\%,\u2030", - /* 101~ */ + /* 103 */ "\\%,\u2030", + /* 104~ */ null, null, null, null, null, - /* ~105 */ + /* ~108 */ // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK - /* 106 */ "\u060C", - /* 107 */ "\u061F", - /* 108 */ "\u061F,\u061B,!,:,-,/,\',\"", + /* 109 */ "\u060C", + /* 110 */ "\u061F", + /* 111 */ "\u061F,\u061B,!,:,-,/,\',\"", + }; + + /* Language az: Azerbaijani */ + private static final String[] LANGUAGE_az = { + /* 0 */ null, + // U+0259: "ə" LATIN SMALL LETTER SCHWA + /* 1 */ "\u0259", + // 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 + /* 2 */ "\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 + /* 3 */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D", + // 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 + /* 4 */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + // 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 + /* 5 */ "\u015F,\u00DF,\u015B,\u0161", + /* 6 */ null, + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE + // U+010D: "č" LATIN SMALL LETTER C WITH CARON + /* 7 */ "\u00E7,\u0107,\u010D", + /* 8~ */ + null, null, null, null, null, null, null, + /* ~14 */ + // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE + /* 15 */ "\u011F", }; /* Language be: Belarusian */ @@ -637,23 +685,23 @@ public final class KeyboardTextsSet { // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I /* 29 */ "\u0456", /* 30~ */ - null, null, null, null, null, - /* ~34 */ + null, null, null, null, null, null, null, + /* ~36 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language bg: Bulgarian */ @@ -661,16 +709,16 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ null, + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ null, // single_quotes of Bulgarian is default single_quotes_right_left. - /* 44 */ "!text/double_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language ca: Catalan */ @@ -804,11 +852,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language da: Danish */ @@ -872,12 +921,12 @@ public final class KeyboardTextsSet { /* 24 */ "\u00F6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language de: German */ @@ -923,12 +972,12 @@ public final class KeyboardTextsSet { /* 7~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language el: Greek */ @@ -936,13 +985,13 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA // U+0393: "Γ" GREEK CAPITAL LETTER GAMMA - /* 42 */ "\u0391\u0392\u0393", + /* 45 */ "\u0391\u0392\u0393", }; /* Language en: English */ @@ -1113,20 +1162,21 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~108 */ - /* 109 */ "q", - /* 110 */ "x", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, + /* ~111 */ + /* 112 */ "q", + /* 113 */ "x", // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX - /* 111 */ "\u015D", + /* 114 */ "\u015D", // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX - /* 112 */ "\u011D", + /* 115 */ "\u011D", // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE - /* 113 */ "\u016D", + /* 116 */ "\u016D", // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX - /* 114 */ "\u0109", + /* 117 */ "\u0109", // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX - /* 115 */ "\u0135", + /* 118 */ "\u0135", }; /* Language es: Spanish */ @@ -1184,25 +1234,25 @@ public final class KeyboardTextsSet { /* 8~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~49 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~52 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 50 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", - /* 51~ */ + /* 53 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", + /* 54~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~102 */ + /* ~105 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 103 */ "!,\u00A1", - /* 104 */ null, + /* 106 */ "!,\u00A1", + /* 107 */ null, // U+00BF: "¿" INVERTED QUESTION MARK - /* 105 */ "?,\u00BF", - /* 106 */ "\"", - /* 107 */ "\'", - /* 108 */ "\'", + /* 108 */ "?,\u00BF", + /* 109 */ "\"", + /* 110 */ "\'", + /* 111 */ "\'", }; /* Language et: Estonian */ @@ -1305,10 +1355,10 @@ public final class KeyboardTextsSet { /* 23 */ "\u00F5", /* 24~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language fa: Persian */ @@ -1316,45 +1366,45 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH - /* 42 */ "\u0627\u200C\u0628\u200C\u067E", - /* 43 */ null, - /* 44 */ null, - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 45 */ "\u0627\u200C\u0628\u200C\u067E", + /* 46 */ null, + /* 47 */ null, + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, - /* ~49 */ + /* ~52 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 51 */ "\u2605,\u066D", + /* 54 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 52 */ "\u266A", - /* 53 */ null, + /* 55 */ "\u266A", + /* 56 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 54 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0652: "ْ" ARABIC SUKUN // U+0651: "ّ" ARABIC SHADDA @@ -1371,74 +1421,74 @@ public final class KeyboardTextsSet { // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. - /* 58 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640", - /* 59 */ "\u064B", + /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640", + /* 62 */ "\u064B", // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE - /* 60 */ "\u06F1", + /* 63 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO - /* 61 */ "\u06F2", + /* 64 */ "\u06F2", // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE - /* 62 */ "\u06F3", + /* 65 */ "\u06F3", // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR - /* 63 */ "\u06F4", + /* 66 */ "\u06F4", // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE - /* 64 */ "\u06F5", + /* 67 */ "\u06F5", // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX - /* 65 */ "\u06F6", + /* 68 */ "\u06F6", // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN - /* 66 */ "\u06F7", + /* 69 */ "\u06F7", // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT - /* 67 */ "\u06F8", + /* 70 */ "\u06F8", // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE - /* 68 */ "\u06F9", + /* 71 */ "\u06F9", // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO - /* 69 */ "\u06F0", + /* 72 */ "\u06F0", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 70 */ "\u06F3\u06F2\u06F1\u061F", + /* 73 */ "\u06F3\u06F2\u06F1\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u06F3\u06F2\u06F1", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", + /* 74 */ "\u06F3\u06F2\u06F1", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 81 */ "0,\u066B,\u066C", - /* 82~ */ + /* 84 */ "0,\u066B,\u066C", + /* 85~ */ null, null, null, null, null, null, null, null, null, null, - /* ~91 */ + /* ~94 */ // U+060C: "،" ARABIC COMMA - /* 92 */ "\u060C", - /* 93 */ "\\,", - /* 94 */ "\u061F", - /* 95 */ "\u061B", + /* 95 */ "\u060C", + /* 96 */ "\\,", + /* 97 */ "\u061F", + /* 98 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 96 */ "\u066A", - /* 97 */ null, - /* 98 */ "?", - /* 99 */ ";", + /* 99 */ "\u066A", + /* 100 */ null, + /* 101 */ "?", + /* 102 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\\%,\u2030", + /* 103 */ "\\%,\u2030", // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /* 101 */ "\u060C", - /* 102 */ "!", - /* 103 */ "!,\\,", - /* 104 */ "\u061F", - /* 105 */ "\u061F,?", - /* 106 */ "\u060C", + /* 104 */ "\u060C", + /* 105 */ "!", + /* 106 */ "!,\\,", /* 107 */ "\u061F", - /* 108 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 108 */ "\u061F,?", + /* 109 */ "\u060C", + /* 110 */ "\u061F", + /* 111 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", }; /* Language fi: Finnish */ @@ -1546,56 +1596,56 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA - /* 42 */ "\u0915\u0916\u0917", - /* 43~ */ + /* 45 */ "\u0915\u0916\u0917", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+20B9: "₹" INDIAN RUPEE SIGN - /* 48 */ "\u20B9", - /* 49~ */ + /* 51 */ "\u20B9", + /* 52~ */ null, null, null, null, null, null, null, null, null, null, null, - /* ~59 */ + /* ~62 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 60 */ "\u0967", + /* 63 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 61 */ "\u0968", + /* 64 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 62 */ "\u0969", + /* 65 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 63 */ "\u096A", + /* 66 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 64 */ "\u096B", + /* 67 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 65 */ "\u096C", + /* 68 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 66 */ "\u096D", + /* 69 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 67 */ "\u096E", + /* 70 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 68 */ "\u096F", + /* 71 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 69 */ "\u0966", + /* 72 */ "\u0966", // Label for "switch to symbols" key. - /* 70 */ "?\u0967\u0968\u0969", + /* 73 */ "?\u0967\u0968\u0969", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u0967\u0968\u0969", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", - /* 81 */ "0", + /* 74 */ "\u0967\u0968\u0969", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", + /* 84 */ "0", }; /* Language hr: Croatian */ @@ -1626,11 +1676,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language hu: Hungarian */ @@ -1679,12 +1730,12 @@ public final class KeyboardTextsSet { /* 5~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language is: Icelandic */ @@ -1750,10 +1801,10 @@ public final class KeyboardTextsSet { /* 22 */ "\u00FE", /* 23~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language it: Italian */ @@ -1806,13 +1857,13 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL - /* 42 */ "\u05D0\u05D1\u05D2", + /* 45 */ "\u05D0\u05D1\u05D2", // The following characters don't need BIDI mirroring. // U+2018: "‘" LEFT SINGLE QUOTATION MARK // U+2019: "’" RIGHT SINGLE QUOTATION MARK @@ -1820,31 +1871,31 @@ public final class KeyboardTextsSet { // U+201C: "“" LEFT DOUBLE QUOTATION MARK // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK - /* 43 */ "\u2018,\u2019,\u201A", - /* 44 */ "\u201C,\u201D,\u201E", - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 46 */ "\u2018,\u2019,\u201A", + /* 47 */ "\u201C,\u201D,\u201E", + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, null, - /* ~50 */ + /* ~53 */ // U+2605: "★" BLACK STAR - /* 51 */ "\u2605", - /* 52 */ null, + /* 54 */ "\u2605", + /* 55 */ null, // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 53 */ "\u00B1,\uFB29", + /* 56 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 54 */ "!fixedColumnOrder!3,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!3,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!3,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!3,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", }; /* Language ka: Georgian */ @@ -1852,15 +1903,63 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN - /* 42 */ "\u10D0\u10D1\u10D2", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u10D0\u10D1\u10D2", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + }; + + /* Language kk: Kazakh */ + private static final String[] LANGUAGE_kk = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + /* ~24 */ + // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA + /* 25 */ "\u0449", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 26 */ "\u044A", + // U+044B: "ы" CYRILLIC SMALL LETTER YERU + /* 27 */ "\u044B", + // U+044D: "э" CYRILLIC SMALL LETTER E + /* 28 */ "\u044D", + // U+0438: "и" CYRILLIC SMALL LETTER I + /* 29 */ "\u0438", + // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U + // U+04B1: "ұ" CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE + /* 30 */ "\u04AF,\u04B1", + // U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER + /* 31 */ "\u049B", + // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER + /* 32 */ "\u04A3", + // U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE + /* 33 */ "\u0493", + // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + /* 34 */ "\u0456", + // U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA + /* 35 */ "\u04D9", + // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O + /* 36 */ "\u04E9", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 37 */ "\u044A", + // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA + /* 38 */ "\u04BB", + /* 39~ */ + null, null, null, null, + /* ~42 */ + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 43 */ "\u0451", + /* 44 */ null, + // Label for "switch to alphabetic" key. + // U+0410: "А" CYRILLIC CAPITAL LETTER A + // U+0411: "Б" CYRILLIC CAPITAL LETTER BE + // U+0412: "В" CYRILLIC CAPITAL LETTER VE + /* 45 */ "\u0410\u0411\u0412", }; /* Language ky: Kirghiz */ @@ -1881,25 +1980,27 @@ public final class KeyboardTextsSet { /* 29 */ "\u0438", // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U /* 30 */ "\u04AF", + /* 31 */ null, // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER - /* 31 */ "\u04A3", - /* 32 */ null, - /* 33 */ null, + /* 32 */ "\u04A3", + /* 33~ */ + null, null, null, + /* ~35 */ // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O - /* 34 */ "\u04E9", + /* 36 */ "\u04E9", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", + /* 45 */ "\u0410\u0411\u0412", }; /* Language lt: Lithuanian */ @@ -1992,10 +2093,10 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language lv: Latvian */ @@ -2087,10 +2188,10 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language mk: Macedonian */ @@ -2098,27 +2199,27 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~35 */ + null, null, null, null, null, null, null, null, null, + /* ~38 */ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 36 */ "\u0455", + /* 39 */ "\u0455", // U+045C: "ќ" CYRILLIC SMALL LETTER KJE - /* 37 */ "\u045C", + /* 40 */ "\u045C", // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 38 */ "\u0437", + /* 41 */ "\u0437", // U+0453: "ѓ" CYRILLIC SMALL LETTER GJE - /* 39 */ "\u0453", + /* 42 */ "\u0453", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 40 */ "\u0450", + /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 41 */ "\u045D", + /* 44 */ "\u045D", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language mn: Mongolian */ @@ -2126,18 +2227,18 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43~ */ + /* 45 */ "\u0410\u0411\u0412", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+20AE: "₮" TUGRIK SIGN - /* 48 */ "\u20AE", + /* 51 */ "\u20AE", }; /* Language nb: Norwegian Bokmål */ @@ -2187,10 +2288,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E4", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language nl: Dutch */ @@ -2245,10 +2346,10 @@ public final class KeyboardTextsSet { /* 9~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language pl: Polish */ @@ -2305,10 +2406,11 @@ public final class KeyboardTextsSet { /* 14 */ "\u0142", /* 15~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language pt: Portuguese */ @@ -2411,10 +2513,10 @@ public final class KeyboardTextsSet { /* 12~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language ru: Russian */ @@ -2434,23 +2536,23 @@ public final class KeyboardTextsSet { // U+0438: "и" CYRILLIC SMALL LETTER I /* 29 */ "\u0438", /* 30~ */ - null, null, null, null, null, - /* ~34 */ + null, null, null, null, null, null, null, + /* ~36 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language sk: Slovak */ @@ -2543,12 +2645,12 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sl: Slovenian */ @@ -2572,11 +2674,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sr: Serbian */ @@ -2584,8 +2687,8 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~35 */ + null, null, null, null, null, null, null, null, null, + /* ~38 */ // TODO: Move these to sr-Latn once we can handle IETF language tag with script name specified. // BEGIN: More keys definitions for Serbian (Latin) // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -2605,27 +2708,27 @@ public final class KeyboardTextsSet { // END: More keys definitions for Serbian (Latin) // BEGIN: More keys definitions for Serbian (Cyrillic) // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 36 */ "\u0437", + /* 39 */ "\u0437", // U+045B: "ћ" CYRILLIC SMALL LETTER TSHE - /* 37 */ "\u045B", + /* 40 */ "\u045B", // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 38 */ "\u0455", + /* 41 */ "\u0455", // U+0452: "ђ" CYRILLIC SMALL LETTER DJE - /* 39 */ "\u0452", + /* 42 */ "\u0452", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 40 */ "\u0450", + /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 41 */ "\u045D", + /* 44 */ "\u045D", // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sv: Swedish */ @@ -2670,10 +2773,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, - /* ~44 */ - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, + /* ~47 */ + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sw: Swahili */ @@ -2732,18 +2835,18 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI - /* 42 */ "\u0E01\u0E02\u0E04", - /* 43~ */ + /* 45 */ "\u0E01\u0E02\u0E04", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT - /* 48 */ "\u0E3F", + /* 51 */ "\u0E3F", }; /* Language tl: Tagalog */ @@ -2861,30 +2964,32 @@ public final class KeyboardTextsSet { /* 28 */ "\u0454", // U+0438: "и" CYRILLIC SMALL LETTER I /* 29 */ "\u0438", - /* 30 */ null, - /* 31 */ null, + /* 30~ */ + null, null, null, + /* ~32 */ // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN - /* 32 */ "\u0491", + /* 33 */ "\u0491", // U+0457: "ї" CYRILLIC SMALL LETTER YI - /* 33 */ "\u0457", - /* 34 */ null, + /* 34 */ "\u0457", + /* 35 */ null, + /* 36 */ null, // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, null, null, - /* ~41 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45~ */ + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48~ */ null, null, null, - /* ~47 */ + /* ~50 */ // U+20B4: "₴" HRYVNIA SIGN - /* 48 */ "\u20B4", + /* 51 */ "\u20B4", }; /* Language vi: Vietnamese */ @@ -2969,10 +3074,10 @@ public final class KeyboardTextsSet { /* 10~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~47 */ + null, null, null, null, null, null, null, null, null, null, null, + /* ~50 */ // U+20AB: "₫" DONG SIGN - /* 48 */ "\u20AB", + /* 51 */ "\u20AB", }; /* Language zu: Zulu */ @@ -3149,6 +3254,7 @@ public final class KeyboardTextsSet { "DEFAULT", LANGUAGE_DEFAULT, /* default */ "af", LANGUAGE_af, /* Afrikaans */ "ar", LANGUAGE_ar, /* Arabic */ + "az", LANGUAGE_az, /* Azerbaijani */ "be", LANGUAGE_be, /* Belarusian */ "bg", LANGUAGE_bg, /* Bulgarian */ "ca", LANGUAGE_ca, /* Catalan */ @@ -3170,6 +3276,7 @@ public final class KeyboardTextsSet { "it", LANGUAGE_it, /* Italian */ "iw", LANGUAGE_iw, /* Hebrew */ "ka", LANGUAGE_ka, /* Georgian */ + "kk", LANGUAGE_kk, /* Kazakh */ "ky", LANGUAGE_ky, /* Kirghiz */ "lt", LANGUAGE_lt, /* Lithuanian */ "lv", LANGUAGE_lv, /* Latvian */ diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java new file mode 100644 index 000000000..ebbcedc96 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.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; + +import android.content.Context; +import android.util.Log; + +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +// TODO: Quit extending Dictionary after implementing dynamic binary dictionary. +abstract public class AbstractDictionaryWriter extends Dictionary { + /** Used for Log actions from this class */ + private static final String TAG = AbstractDictionaryWriter.class.getSimpleName(); + + private final Context mContext; + + public AbstractDictionaryWriter(final Context context, final String dictType) { + super(dictType); + mContext = context; + } + + abstract public void clear(); + + abstract public void addUnigramWord(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord); + + abstract public void addBigramWords(final String word0, final String word1, + final int frequency, final boolean isValid); + + abstract public void removeBigramWords(final String word0, final String word1); + + abstract protected void writeBinaryDictionary(final FileOutputStream out) + throws IOException, UnsupportedFormatException; + + public void write(final String fileName) { + final String tempFileName = fileName + ".temp"; + final File file = new File(mContext.getFilesDir(), fileName); + final File tempFile = new File(mContext.getFilesDir(), tempFileName); + FileOutputStream out = null; + try { + out = new FileOutputStream(tempFile); + writeBinaryDictionary(out); + out.flush(); + out.close(); + tempFile.renameTo(file); + } catch (IOException e) { + Log.e(TAG, "IO exception while writing file", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Unsupported format", e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 4b5d02716..6e26a587f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -308,6 +308,7 @@ public final class BinaryDictionaryFileDumper { Log.e(TAG, "Could not have the dictionary pack delete a word list"); } BinaryDictionaryGetter.removeFilesWithIdExcept(context, wordlistId, finalFile); + Log.e(TAG, "Successfully copied file for wordlist ID " + wordlistId); // Success! Close files (through the finally{} clause) and return. return; } catch (Exception e) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 51dc85295..31a892e19 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -224,14 +224,10 @@ final public class BinaryDictionaryGetter { } } - // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason - // for this is, since those do not include whitelist entries, the new code with an old version - // of the dictionary would lose whitelist functionality. + // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since + // those do not include whitelist entries, the new code with an old version of the dictionary + // would lose whitelist functionality. private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) { - // Only for English - other languages didn't have a whitelist, hence this - // ad-hoc ## HACK ## - if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true; - FileInputStream inStream = null; try { // Read the version of the file diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index bb4a42ede..ad09b6a56 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -149,6 +149,13 @@ public final class Constants { } /** + * Custom request code used in + * {@link com.android.inputmethod.keyboard.KeyboardActionListener#onCustomRequest(int)}. + */ + // The code to show input method picker. + public static final int CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER = 1; + + /** * Some common keys code. Must be positive. */ public static final int CODE_ENTER = '\n'; diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 110be9db3..c99d0e2ea 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -109,7 +109,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @Override public void loadDictionaryAsync() { - clearFusionDictionary(); loadDeviceAccountsEmailAddresses(); loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI); // TODO: Switch this URL to the newer ContactsContract too @@ -236,6 +235,11 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } @Override + protected boolean needsToReloadBeforeWriting() { + return true; + } + + @Override protected boolean hasContentChanged() { final long startTime = SystemClock.uptimeMillis(); final int contactCount = getContactCount(); diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index 01ec7f9a7..5dbc9b157 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -25,7 +25,7 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ApplicationUtils; public final class DebugSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -118,7 +118,7 @@ public final class DebugSettings extends PreferenceFragment } boolean isDebugMode = mDebugMode.isChecked(); final String version = getResources().getString( - R.string.version_text, Utils.getVersionName(getActivity())); + R.string.version_text, ApplicationUtils.getVersionName(getActivity())); if (!isDebugMode) { mDebugMode.setTitle(version); mDebugMode.setSummary(""); diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java new file mode 100644 index 000000000..8be04c1c0 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -0,0 +1,107 @@ +/* + * 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.content.Context; + +import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.Node; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * An in memory dictionary for memorizing entries and writing a binary dictionary. + */ +public class DictionaryWriter extends AbstractDictionaryWriter { + // TODO: Regenerate version 3 binary dictionary. + private static final int BINARY_DICT_VERSION = 2; + private static final FormatSpec.FormatOptions FORMAT_OPTIONS = + new FormatSpec.FormatOptions(BINARY_DICT_VERSION); + + private FusionDictionary mFusionDictionary; + + public DictionaryWriter(final Context context, final String dictType) { + super(context, dictType); + clear(); + } + + @Override + public void clear() { + final HashMap<String, String> attributes = CollectionUtils.newHashMap(); + mFusionDictionary = new FusionDictionary(new Node(), + new FusionDictionary.DictionaryOptions(attributes, false, false)); + } + + /** + * Adds a word unigram to the fusion dictionary. + */ + // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries, + // considering performance regression. + @Override + public void addUnigramWord(final String word, final String shortcutTarget, final int frequency, + final boolean isNotAWord) { + if (shortcutTarget == null) { + mFusionDictionary.add(word, frequency, null, isNotAWord); + } else { + // TODO: Do this in the subclass, with this class taking an arraylist. + final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); + shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); + mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord); + } + } + + @Override + public void addBigramWords(final String word0, final String word1, final int frequency, + final boolean isValid) { + mFusionDictionary.setBigram(word0, word1, frequency); + } + + @Override + public void removeBigramWords(final String word0, final String word1) { + // This class don't support removing bigram words. + } + + @Override + protected void writeBinaryDictionary(final FileOutputStream out) + throws IOException, UnsupportedFormatException { + BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS); + } + + @Override + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + boolean blockOffensiveWords) { + // This class doesn't support suggestion. + return null; + } + + @Override + public boolean isValidWord(String word) { + // This class doesn't support dictionary retrieval. + return false; + } +} diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 9cdb86c2d..657fc64b4 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -22,20 +22,12 @@ import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; -import com.android.inputmethod.latin.makedict.FormatSpec; -import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.Node; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Abstract base class for an expandable dictionary that can be created and updated dynamically @@ -76,8 +68,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private BinaryDictionary mBinaryDictionary; - /** The expandable fusion dictionary used to generate the binary dictionary. */ - private FusionDictionary mFusionDictionary; + /** The in-memory dictionary used to generate the binary dictionary. */ + private AbstractDictionaryWriter mDictionaryWriter; /** * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple @@ -92,10 +84,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Controls access to the local binary dictionary for this instance. */ private final DictionaryController mLocalDictionaryController = new DictionaryController(); - private static final int BINARY_DICT_VERSION = 1; - private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(BINARY_DICT_VERSION); - /** * Abstract method for loading the unigrams and bigrams of a given dictionary in a background * thread. @@ -137,7 +125,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mContext = context; mBinaryDictionary = null; mSharedDictionaryController = getSharedDictionaryController(filename); - clearFusionDictionary(); + mDictionaryWriter = new DictionaryWriter(context, dictType); } protected static String getFilenameWithLocale(final String name, final String localeStr) { @@ -150,53 +138,57 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public void close() { // Ensure that no other threads are accessing the local binary dictionary. - mLocalDictionaryController.lock(); + mLocalDictionaryController.writeLock().lock(); try { if (mBinaryDictionary != null) { mBinaryDictionary.close(); mBinaryDictionary = null; } + mDictionaryWriter.close(); } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().unlock(); } } /** - * Clears the fusion dictionary on the Java side. Note: Does not modify the binary dictionary on - * the native side. + * Adds a word unigram to the dictionary. Used for loading a dictionary. */ - public void clearFusionDictionary() { - final HashMap<String, String> attributes = CollectionUtils.newHashMap(); - mFusionDictionary = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions(attributes, false, false)); + protected void addWord(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord) { + mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); } /** - * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes - * are done to update the binary dictionary. + * Sets a word bigram in the dictionary. Used for loading a dictionary. */ - // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries, - // considering performance regression. - protected void addWord(final String word, final String shortcutTarget, final int frequency, - final boolean isNotAWord) { - if (shortcutTarget == null) { - mFusionDictionary.add(word, frequency, null, isNotAWord); - } else { - // TODO: Do this in the subclass, with this class taking an arraylist. - final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); - shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); - mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord); + protected void setBigram(final String prevWord, final String word, final int frequency) { + mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); + } + + /** + * Dynamically adds a word unigram to the dictionary. + */ + protected void addWordDynamically(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord) { + mLocalDictionaryController.writeLock().lock(); + try { + mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); + } finally { + mLocalDictionaryController.writeLock().unlock(); } } /** - * Sets a word bigram in the fusion dictionary. Call updateBinaryDictionary when all changes are - * done to update the binary dictionary. + * Dynamically sets a word bigram in the dictionary. */ - // TODO: Create "cache dictionary" to cache fresh bigrams for frequently updated dictionaries, - // considering performance regression. - protected void setBigram(final String prevWord, final String word, final int frequency) { - mFusionDictionary.setBigram(prevWord, word, frequency); + protected void setBigramDynamically(final String prevWord, final String word, + final int frequency) { + mLocalDictionaryController.writeLock().lock(); + try { + mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); + } finally { + mLocalDictionaryController.writeLock().unlock(); + } } @Override @@ -204,14 +196,29 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords) { asyncReloadDictionaryIfRequired(); - if (mLocalDictionaryController.tryLock()) { + // Write lock because getSuggestions in native updates session status. + if (mLocalDictionaryController.writeLock().tryLock()) { try { + final ArrayList<SuggestedWordInfo> inMemDictSuggestion = + mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, + blockOffensiveWords); if (mBinaryDictionary != null) { - return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords); + final ArrayList<SuggestedWordInfo> binarySuggestion = + mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, + blockOffensiveWords); + if (inMemDictSuggestion == null) { + return binarySuggestion; + } else if (binarySuggestion == null) { + return inMemDictSuggestion; + } else { + binarySuggestion.addAll(binarySuggestion); + return binarySuggestion; + } + } else { + return inMemDictSuggestion; } } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().unlock(); } } return null; @@ -224,11 +231,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } protected boolean isValidWordInner(final String word) { - if (mLocalDictionaryController.tryLock()) { + if (mLocalDictionaryController.readLock().tryLock()) { try { return isValidWordLocked(word); } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.readLock().unlock(); } } return false; @@ -239,22 +246,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary.isValidWord(word); } - protected boolean isValidBigram(final String word1, final String word2) { - if (mBinaryDictionary == null) return false; - return mBinaryDictionary.isValidBigram(word1, word2); - } - - protected boolean isValidBigramInner(final String word1, final String word2) { - if (mLocalDictionaryController.tryLock()) { - try { - return isValidBigramLocked(word1, word2); - } finally { - mLocalDictionaryController.unlock(); - } - } - return false; - } - protected boolean isValidBigramLocked(final String word1, final String word2) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidBigram(word1, word2); @@ -273,7 +264,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Loads the current binary dictionary from internal storage. Assumes the dictionary file * exists. */ - protected void loadBinaryDictionary() { + private void loadBinaryDictionary() { if (DEBUG) { Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" + mSharedDictionaryController.mLastUpdateRequestTime + " update=" @@ -292,9 +283,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // Ensure all threads accessing the current dictionary have finished before swapping in // the new one. final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; - mLocalDictionaryController.lock(); - mBinaryDictionary = newBinaryDictionary; - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().lock(); + try { + mBinaryDictionary = newBinaryDictionary; + } finally { + mLocalDictionaryController.writeLock().unlock(); + } oldBinaryDictionary.close(); } else { mBinaryDictionary = newBinaryDictionary; @@ -302,6 +296,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** + * Abstract method for checking if it is required to reload the dictionary before writing + * a binary dictionary. + */ + abstract protected boolean needsToReloadBeforeWriting(); + + /** * Generates and writes a new binary dictionary based on the contents of the fusion dictionary. */ private void generateBinaryDictionary() { @@ -310,33 +310,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mSharedDictionaryController.mLastUpdateRequestTime + " update=" + mSharedDictionaryController.mLastUpdateTime); } - - loadDictionaryAsync(); - - final String tempFileName = mFilename + ".temp"; - final File file = new File(mContext.getFilesDir(), mFilename); - final File tempFile = new File(mContext.getFilesDir(), tempFileName); - FileOutputStream out = null; - try { - out = new FileOutputStream(tempFile); - BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS); - out.flush(); - out.close(); - tempFile.renameTo(file); - clearFusionDictionary(); - } catch (IOException e) { - Log.e(TAG, "IO exception while writing file", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "Unsupported format", e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // ignore - } - } + if (needsToReloadBeforeWriting()) { + mDictionaryWriter.clear(); + loadDictionaryAsync(); } + mDictionaryWriter.write(mFilename); } /** @@ -389,7 +367,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private final void syncReloadDictionaryInternal() { // Ensure that only one thread attempts to read or write to the shared binary dictionary // file at the same time. - mSharedDictionaryController.lock(); + mSharedDictionaryController.writeLock().lock(); try { final long time = SystemClock.uptimeMillis(); final boolean dictionaryFileExists = dictionaryFileExists(); @@ -415,9 +393,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // shared dictionary. loadBinaryDictionary(); } + if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) { + // Binary dictionary is not valid. Regenerate the dictionary file. + mSharedDictionaryController.mLastUpdateTime = time; + generateBinaryDictionary(); + loadBinaryDictionary(); + } mLocalDictionaryController.mLastUpdateTime = time; } finally { - mSharedDictionaryController.unlock(); + mSharedDictionaryController.writeLock().unlock(); } } @@ -442,7 +426,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * dictionary is out of date. Can be shared across multiple dictionary instances that access the * same filename. */ - private static class DictionaryController extends ReentrantLock { + private static class DictionaryController extends ReentrantReadWriteLock { private volatile long mLastUpdateTime = 0; private volatile long mLastUpdateRequestTime = 0; diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index a67c919b3..2666573bb 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -728,172 +728,206 @@ public class ExpandableDictionary extends Dictionary { * to their base characters. If c is in range, BASE_CHARS[c] == c * if c is not a combined character, or the base character if it * is combined. + * + * cf. native/jni/src/utils/char_utils.cpp */ private static final char BASE_CHARS[] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, - 0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0020, - 0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7, - 0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf, - 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00c6, 0x0043, - 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, - 0x00d0, 0x004e, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x00d7, - 0x004f, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00de, 0x0073, // Manually changed d8 to 4f - // Manually changed df to 73 - 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063, - 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, - 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7, - 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, // Manually changed f8 to 6f - 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, - 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, - 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, - 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, - 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, - 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, - 0x0049, 0x0131, 0x0049, 0x0069, 0x004a, 0x006a, 0x004b, 0x006b, - 0x0138, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, - 0x006c, 0x0141, 0x0142, 0x004e, 0x006e, 0x004e, 0x006e, 0x004e, - 0x006e, 0x02bc, 0x014a, 0x014b, 0x004f, 0x006f, 0x004f, 0x006f, - 0x004f, 0x006f, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, - 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, - 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, - 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, - 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, - 0x0059, 0x005a, 0x007a, 0x005a, 0x007a, 0x005a, 0x007a, 0x0073, - 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, - 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f, - 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, - 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f, - 0x004f, 0x006f, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, - 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x0055, - 0x0075, 0x01b1, 0x01b2, 0x01b3, 0x01b4, 0x01b5, 0x01b6, 0x01b7, - 0x01b8, 0x01b9, 0x01ba, 0x01bb, 0x01bc, 0x01bd, 0x01be, 0x01bf, - 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0044, 0x0044, 0x0064, 0x004c, - 0x004c, 0x006c, 0x004e, 0x004e, 0x006e, 0x0041, 0x0061, 0x0049, - 0x0069, 0x004f, 0x006f, 0x0055, 0x0075, 0x00dc, 0x00fc, 0x00dc, - 0x00fc, 0x00dc, 0x00fc, 0x00dc, 0x00fc, 0x01dd, 0x00c4, 0x00e4, - 0x0226, 0x0227, 0x00c6, 0x00e6, 0x01e4, 0x01e5, 0x0047, 0x0067, - 0x004b, 0x006b, 0x004f, 0x006f, 0x01ea, 0x01eb, 0x01b7, 0x0292, - 0x006a, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01f6, 0x01f7, - 0x004e, 0x006e, 0x00c5, 0x00e5, 0x00c6, 0x00e6, 0x00d8, 0x00f8, - 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, - 0x0049, 0x0069, 0x0049, 0x0069, 0x004f, 0x006f, 0x004f, 0x006f, - 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, - 0x0053, 0x0073, 0x0054, 0x0074, 0x021c, 0x021d, 0x0048, 0x0068, - 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, - 0x0045, 0x0065, 0x00d6, 0x00f6, 0x00d5, 0x00f5, 0x004f, 0x006f, - 0x022e, 0x022f, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, - 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f, - 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, - 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f, - 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, - 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, - 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, - 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, - 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, - 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, - 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, - 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, - 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, - 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, - 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, - 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, - 0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077, - 0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, - 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, - 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, - 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df, - 0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7, - 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, - 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, - 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, - 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, - 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, - 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, - 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, - 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, - 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, - 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, - 0x0370, 0x0371, 0x0372, 0x0373, 0x02b9, 0x0375, 0x0376, 0x0377, - 0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00a8, 0x0391, 0x00b7, - 0x0395, 0x0397, 0x0399, 0x038b, 0x039f, 0x038d, 0x03a5, 0x03a9, - 0x03ca, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x0399, 0x03a5, 0x03b1, 0x03b5, 0x03b7, 0x03b9, - 0x03cb, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, - 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, - 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, - 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03cf, - 0x03b2, 0x03b8, 0x03a5, 0x03d2, 0x03d2, 0x03c6, 0x03c0, 0x03d7, - 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03dc, 0x03dd, 0x03de, 0x03df, - 0x03e0, 0x03e1, 0x03e2, 0x03e3, 0x03e4, 0x03e5, 0x03e6, 0x03e7, - 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03ed, 0x03ee, 0x03ef, - 0x03ba, 0x03c1, 0x03c2, 0x03f3, 0x0398, 0x03b5, 0x03f6, 0x03f7, - 0x03f8, 0x03a3, 0x03fa, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff, - 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, - 0x0408, 0x0409, 0x040a, 0x040b, 0x041a, 0x0418, 0x0423, 0x040f, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0418, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, - 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, - 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, - 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f, - 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, - 0x0468, 0x0469, 0x046a, 0x046b, 0x046c, 0x046d, 0x046e, 0x046f, - 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, - 0x0478, 0x0479, 0x047a, 0x047b, 0x047c, 0x047d, 0x047e, 0x047f, - 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f, - 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, - 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x049f, - 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, - 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af, - 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, - 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x04be, 0x04bf, - 0x04c0, 0x0416, 0x0436, 0x04c3, 0x04c4, 0x04c5, 0x04c6, 0x04c7, - 0x04c8, 0x04c9, 0x04ca, 0x04cb, 0x04cc, 0x04cd, 0x04ce, 0x04cf, - 0x0410, 0x0430, 0x0410, 0x0430, 0x04d4, 0x04d5, 0x0415, 0x0435, - 0x04d8, 0x04d9, 0x04d8, 0x04d9, 0x0416, 0x0436, 0x0417, 0x0437, - 0x04e0, 0x04e1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041e, 0x043e, - 0x04e8, 0x04e9, 0x04e8, 0x04e9, 0x042d, 0x044d, 0x0423, 0x0443, - 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04f6, 0x04f7, - 0x042b, 0x044b, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff, + /* U+0000 */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + /* U+0008 */ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* U+0010 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + /* U+0018 */ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* U+0020 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + /* U+0028 */ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* U+0030 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + /* U+0038 */ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* U+0040 */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + /* U+0048 */ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + /* U+0050 */ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + /* U+0058 */ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* U+0060 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + /* U+0068 */ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* U+0070 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + /* U+0078 */ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* U+0080 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + /* U+0088 */ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* U+0090 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + /* U+0098 */ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* U+00A0 */ 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + /* U+00A8 */ 0x0020, 0x00A9, 0x0061, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0020, + /* U+00B0 */ 0x00B0, 0x00B1, 0x0032, 0x0033, 0x0020, 0x03BC, 0x00B6, 0x00B7, + /* U+00B8 */ 0x0020, 0x0031, 0x006F, 0x00BB, 0x0031, 0x0031, 0x0033, 0x00BF, + /* U+00C0 */ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00C6, 0x0043, + /* U+00C8 */ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, + /* U+00D0 */ 0x00D0, 0x004E, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x00D7, + /* U+00D8 */ 0x004F, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00DE, 0x0073, + // U+00D8: Manually changed from 00D8 to 004F + // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O + // U+00DF: Manually changed from 00DF to 0073 + /* U+00E0 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00E6, 0x0063, + /* U+00E8 */ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, + /* U+00F0 */ 0x00F0, 0x006E, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x00F7, + /* U+00F8 */ 0x006F, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00FE, 0x0079, + // U+00F8: Manually changed from 00F8 to 006F + // TODO: Check if it's really acceptable to consider ø a diacritical variant of o + /* U+0100 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, + /* U+0108 */ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, + /* U+0110 */ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, + /* U+0118 */ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, + /* U+0120 */ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, + /* U+0128 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, + /* U+0130 */ 0x0049, 0x0131, 0x0049, 0x0069, 0x004A, 0x006A, 0x004B, 0x006B, + /* U+0138 */ 0x0138, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, + /* U+0140 */ 0x006C, 0x004C, 0x006C, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, + // U+0141: Manually changed from 0141 to 004C + // U+0142: Manually changed from 0142 to 006C + /* U+0148 */ 0x006E, 0x02BC, 0x014A, 0x014B, 0x004F, 0x006F, 0x004F, 0x006F, + /* U+0150 */ 0x004F, 0x006F, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, + /* U+0158 */ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, + /* U+0160 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, + /* U+0168 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, + /* U+0170 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, + /* U+0178 */ 0x0059, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0073, + /* U+0180 */ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, + /* U+0188 */ 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, + /* U+0190 */ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, + /* U+0198 */ 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, + /* U+01A0 */ 0x004F, 0x006F, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, + /* U+01A8 */ 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x0055, + /* U+01B0 */ 0x0075, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, + /* U+01B8 */ 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, + /* U+01C0 */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x0044, 0x0044, 0x0064, 0x004C, + /* U+01C8 */ 0x004C, 0x006C, 0x004E, 0x004E, 0x006E, 0x0041, 0x0061, 0x0049, + /* U+01D0 */ 0x0069, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, + // U+01D5: Manually changed from 00DC to 0055 + // U+01D6: Manually changed from 00FC to 0075 + // U+01D7: Manually changed from 00DC to 0055 + /* U+01D8 */ 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x01DD, 0x0041, 0x0061, + // U+01D8: Manually changed from 00FC to 0075 + // U+01D9: Manually changed from 00DC to 0055 + // U+01DA: Manually changed from 00FC to 0075 + // U+01DB: Manually changed from 00DC to 0055 + // U+01DC: Manually changed from 00FC to 0075 + // U+01DE: Manually changed from 00C4 to 0041 + // U+01DF: Manually changed from 00E4 to 0061 + /* U+01E0 */ 0x0041, 0x0061, 0x00C6, 0x00E6, 0x01E4, 0x01E5, 0x0047, 0x0067, + // U+01E0: Manually changed from 0226 to 0041 + // U+01E1: Manually changed from 0227 to 0061 + /* U+01E8 */ 0x004B, 0x006B, 0x004F, 0x006F, 0x004F, 0x006F, 0x01B7, 0x0292, + // U+01EC: Manually changed from 01EA to 004F + // U+01ED: Manually changed from 01EB to 006F + /* U+01F0 */ 0x006A, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01F6, 0x01F7, + /* U+01F8 */ 0x004E, 0x006E, 0x0041, 0x0061, 0x00C6, 0x00E6, 0x004F, 0x006F, + // U+01FA: Manually changed from 00C5 to 0041 + // U+01FB: Manually changed from 00E5 to 0061 + // U+01FE: Manually changed from 00D8 to 004F + // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O + // U+01FF: Manually changed from 00F8 to 006F + // TODO: Check if it's really acceptable to consider ø a diacritical variant of o + /* U+0200 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, + /* U+0208 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F, + /* U+0210 */ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, + /* U+0218 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x021C, 0x021D, 0x0048, 0x0068, + /* U+0220 */ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, + /* U+0228 */ 0x0045, 0x0065, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, + // U+022A: Manually changed from 00D6 to 004F + // U+022B: Manually changed from 00F6 to 006F + // U+022C: Manually changed from 00D5 to 004F + // U+022D: Manually changed from 00F5 to 006F + /* U+0230 */ 0x004F, 0x006F, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, + // U+0230: Manually changed from 022E to 004F + // U+0231: Manually changed from 022F to 006F + /* U+0238 */ 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, + /* U+0240 */ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, + /* U+0248 */ 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, + /* U+0250 */ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, + /* U+0258 */ 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, + /* U+0260 */ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, + /* U+0268 */ 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, + /* U+0270 */ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, + /* U+0278 */ 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, + /* U+0280 */ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, + /* U+0288 */ 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, + /* U+0290 */ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, + /* U+0298 */ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, + /* U+02A0 */ 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, + /* U+02A8 */ 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, + /* U+02B0 */ 0x0068, 0x0266, 0x006A, 0x0072, 0x0279, 0x027B, 0x0281, 0x0077, + /* U+02B8 */ 0x0079, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, + /* U+02C0 */ 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, + /* U+02C8 */ 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF, + /* U+02D0 */ 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, + /* U+02D8 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02DE, 0x02DF, + /* U+02E0 */ 0x0263, 0x006C, 0x0073, 0x0078, 0x0295, 0x02E5, 0x02E6, 0x02E7, + /* U+02E8 */ 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, + /* U+02F0 */ 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, + /* U+02F8 */ 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF, + /* U+0300 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + /* U+0308 */ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* U+0310 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + /* U+0318 */ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* U+0320 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + /* U+0328 */ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* U+0330 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + /* U+0338 */ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* U+0340 */ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, + /* U+0348 */ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* U+0350 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + /* U+0358 */ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* U+0360 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + /* U+0368 */ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* U+0370 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x02B9, 0x0375, 0x0376, 0x0377, + /* U+0378 */ 0x0378, 0x0379, 0x0020, 0x037B, 0x037C, 0x037D, 0x003B, 0x037F, + /* U+0380 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00A8, 0x0391, 0x00B7, + /* U+0388 */ 0x0395, 0x0397, 0x0399, 0x038B, 0x039F, 0x038D, 0x03A5, 0x03A9, + /* U+0390 */ 0x03CA, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + /* U+0398 */ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + /* U+03A0 */ 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + /* U+03A8 */ 0x03A8, 0x03A9, 0x0399, 0x03A5, 0x03B1, 0x03B5, 0x03B7, 0x03B9, + /* U+03B0 */ 0x03CB, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + /* U+03B8 */ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* U+03C0 */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + /* U+03C8 */ 0x03C8, 0x03C9, 0x03B9, 0x03C5, 0x03BF, 0x03C5, 0x03C9, 0x03CF, + /* U+03D0 */ 0x03B2, 0x03B8, 0x03A5, 0x03D2, 0x03D2, 0x03C6, 0x03C0, 0x03D7, + /* U+03D8 */ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* U+03E0 */ 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7, + /* U+03E8 */ 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF, + /* U+03F0 */ 0x03BA, 0x03C1, 0x03C2, 0x03F3, 0x0398, 0x03B5, 0x03F6, 0x03F7, + /* U+03F8 */ 0x03F8, 0x03A3, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + /* U+0400 */ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, + /* U+0408 */ 0x0408, 0x0409, 0x040A, 0x040B, 0x041A, 0x0418, 0x0423, 0x040F, + /* U+0410 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + /* U+0418 */ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + // U+0419: Manually changed from 0418 to 0419 + /* U+0420 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + /* U+0428 */ 0x0428, 0x0429, 0x042C, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + // U+042A: Manually changed from 042A to 042C + /* U+0430 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + /* U+0438 */ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + // U+0439: Manually changed from 0438 to 0439 + /* U+0440 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + /* U+0448 */ 0x0448, 0x0449, 0x044C, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + // U+044A: Manually changed from 044A to 044C + /* U+0450 */ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, + /* U+0458 */ 0x0458, 0x0459, 0x045A, 0x045B, 0x043A, 0x0438, 0x0443, 0x045F, + /* U+0460 */ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, + /* U+0468 */ 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F, + /* U+0470 */ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, + /* U+0478 */ 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F, + /* U+0480 */ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + /* U+0488 */ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* U+0490 */ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, + /* U+0498 */ 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F, + /* U+04A0 */ 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7, + /* U+04A8 */ 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF, + /* U+04B0 */ 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, + /* U+04B8 */ 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, + /* U+04C0 */ 0x04C0, 0x0416, 0x0436, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7, + /* U+04C8 */ 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* U+04D0 */ 0x0410, 0x0430, 0x0410, 0x0430, 0x04D4, 0x04D5, 0x0415, 0x0435, + /* U+04D8 */ 0x04D8, 0x04D9, 0x04D8, 0x04D9, 0x0416, 0x0436, 0x0417, 0x0437, + /* U+04E0 */ 0x04E0, 0x04E1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041E, 0x043E, + /* U+04E8 */ 0x04E8, 0x04E9, 0x04E8, 0x04E9, 0x042D, 0x044D, 0x0423, 0x0443, + /* U+04F0 */ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04F6, 0x04F7, + /* U+04F8 */ 0x042B, 0x044B, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, }; - - // generated with: - // cat UnicodeData.txt | perl -e 'while (<>) { @foo = split(/;/); $foo[5] =~ s/<.*> //; $base[hex($foo[0])] = hex($foo[5]);} for ($i = 0; $i < 0x500; $i += 8) { for ($j = $i; $j < $i + 8; $j++) { printf("0x%04x, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }' - } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0560cf528..8f5e57182 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -73,22 +73,22 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.latin.RichInputConnection.Range; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.SuggestionStripView; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CompletionInfoUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; +import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; import com.android.inputmethod.latin.utils.PositionalInfoForUserDictPendingAddition; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; -import com.android.inputmethod.latin.utils.Utils; -import com.android.inputmethod.latin.utils.Utils.Stats; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; import java.io.FileDescriptor; @@ -961,7 +961,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: is it still necessary to test for composingSpan related stuff? final boolean selectionChangedOrSafeToReset = selectionChanged || (!mWordComposer.isComposingWord()) || noComposingSpan; - if (selectionChangedOrSafeToReset) { + final boolean hasOrHadSelection = (oldSelStart != oldSelEnd + || newSelStart != newSelEnd); + final int moveAmount = newSelStart - oldSelStart; + if (selectionChangedOrSafeToReset && (hasOrHadSelection + || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -1356,14 +1360,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen showSubtypeSelectorAndSettings(); } - // Virtual codes representing custom requests. These are used in onCustomRequest() below. - public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1; - @Override public boolean onCustomRequest(final int requestCode) { if (isShowingOptionDialog()) return false; switch (requestCode) { - case CODE_SHOW_INPUT_METHOD_PICKER: + case Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER: if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { mRichImm.getInputMethodManager().showInputMethodPicker(); return true; @@ -1550,7 +1551,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (SPACE_STATE_PHANTOM == spaceState) { if (mSettings.isInternal()) { if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) { - Stats.onAutoCorrection( + LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); } } @@ -1611,7 +1612,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isComposingWord()) { if (mSettings.isInternal()) { if (mWordComposer.isBatchMode()) { - Stats.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer); + LatinImeLoggerUtils.onAutoCorrection( + "", mWordComposer.getTypedWord(), " ", mWordComposer); } } final int wordComposerSize = mWordComposer.size(); @@ -1859,7 +1861,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { if (mLastComposedWord.canRevertCommit()) { if (mSettings.isInternal()) { - Stats.onAutoCorrectionCancellation(); + LatinImeLoggerUtils.onAutoCorrectionCancellation(); } revertCommit(); return; @@ -2030,7 +2032,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mHandler.postUpdateSuggestionStrip(); if (mSettings.isInternal()) { - Utils.Stats.onNonSeparator((char)primaryCode, x, y); + LatinImeLoggerUtils.onNonSeparator((char)primaryCode, x, y); } } @@ -2135,7 +2137,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setPunctuationSuggestions(); } if (mSettings.isInternal()) { - Utils.Stats.onSeparator((char)primaryCode, x, y); + LatinImeLoggerUtils.onSeparator((char)primaryCode, x, y); } mKeyboardSwitcher.updateShiftState(); @@ -2324,7 +2326,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } if (mSettings.isInternal()) { - Stats.onAutoCorrection(typedWord, autoCorrection, separatorString, mWordComposer); + LatinImeLoggerUtils.onAutoCorrection( + typedWord, autoCorrection, separatorString, mWordComposer); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { final SuggestedWords suggestedWords = mSuggestedWords; @@ -2428,7 +2431,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen && !AutoCorrection.isValidWord(mSuggest, suggestion, true); if (mSettings.isInternal()) { - Stats.onSeparator((char)Constants.CODE_SPACE, + LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { @@ -2514,7 +2517,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If we don't know the cursor location, return. if (mLastSelectionStart < 0) return; if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; - final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), + final TextRange range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), 0 /* additionalPrecedingWordsCount */); if (null == range) return; // Happens if we don't have an input connection at all // If for some strange reason (editor bug or so) we measure the text before the cursor as @@ -2535,6 +2538,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard()); + // TODO: this is in chars but the callee expects code points! mWordComposer.setCursorPositionWithinWord(numberOfCharsInWordBeforeCursor); mConnection.setComposingRegion( mLastSelectionStart - numberOfCharsInWordBeforeCursor, @@ -2631,7 +2635,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1); if (mSettings.isInternal()) { - Stats.onSeparator(mLastComposedWord.mSeparatorString, + LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -2673,15 +2677,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // Callback called by PointerTracker through the KeyboardActionListener. This is called when a - // key is depressed; release matching call is onReleaseKey below. + // Callback of the {@link KeyboardActionListener}. This is called when a key is depressed; + // release matching call is {@link #onReleaseKey(int,boolean)} below. @Override public void onPressKey(final int primaryCode, final boolean isSinglePointer) { mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer); + final MainKeyboardView mKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + final boolean noFeedback = (mKeyboardView != null && mKeyboardView.isInSlidingKeyInput()) + || (primaryCode == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()); + if (!noFeedback) { + AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback( + primaryCode, mKeyboardView); + } } - // Callback by PointerTracker through the KeyboardActionListener. This is called when a key - // is released; press matching call is onPressKey above. + // Callback of the {@link KeyboardActionListener}. This is called when a key is released; + // press matching call is {@link #onPressKey(int,boolean)} above. @Override public void onReleaseKey(final int primaryCode, final boolean withSliding) { mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding); @@ -2778,7 +2789,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence[] items = new CharSequence[] { // TODO: Should use new string "Select active input modes". getString(R.string.language_selection_title), - getString(Utils.getAcitivityTitleResId(this, SettingsActivity.class)), + getString(ApplicationUtils.getAcitivityTitleResId(this, SettingsActivity.class)), }; final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 6b22cb12e..461de53ad 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -17,9 +17,7 @@ package com.android.inputmethod.latin; import android.inputmethodservice.InputMethodService; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -32,9 +30,9 @@ import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; -import java.util.Arrays; import java.util.Locale; import java.util.regex.Pattern; @@ -193,6 +191,10 @@ public final class RichInputConnection { return mIC.getSelectedText(flags); } + public boolean canDeleteCharacters() { + return mCurrentCursorPosition > 0; + } + /** * Gets the caps modes we should be in after this specific string. * @@ -441,100 +443,6 @@ public final class RichInputConnection { return getNthPreviousWord(prev, sentenceSeperators, n); } - /** - * Represents a range of text, relative to the current cursor position. - */ - public static final class Range { - private final CharSequence mTextAtCursor; - private final int mWordAtCursorStartIndex; - private final int mWordAtCursorEndIndex; - private final int mCursorIndex; - - public final CharSequence mWord; - - public int getNumberOfCharsInWordBeforeCursor() { - return mCursorIndex - mWordAtCursorStartIndex; - } - - public int getNumberOfCharsInWordAfterCursor() { - return mWordAtCursorEndIndex - mCursorIndex; - } - - /** - * Gets the suggestion spans that are put squarely on the word, with the exact start - * and end of the span matching the boundaries of the word. - * @return the list of spans. - */ - public SuggestionSpan[] getSuggestionSpansAtWord() { - if (!(mTextAtCursor instanceof Spanned && mWord instanceof Spanned)) { - return new SuggestionSpan[0]; - } - final Spanned text = (Spanned)mTextAtCursor; - // Note: it's fine to pass indices negative or greater than the length of the string - // to the #getSpans() method. The reason we need to get from -1 to +1 is that, the - // spans were cut at the cursor position, and #getSpans(start, end) does not return - // spans that end at `start' or begin at `end'. Consider the following case: - // this| is (The | symbolizes the cursor position - // ---- --- - // In this case, the cursor is in position 4, so the 0~7 span has been split into - // a 0~4 part and a 4~7 part. - // If we called #getSpans(0, 4) in this case, we would only get the part from 0 to 4 - // of the span, and not the part from 4 to 7, so we would not realize the span actually - // extends from 0 to 7. But if we call #getSpans(-1, 5) we'll get both the 0~4 and - // the 4~7 spans and we can merge them accordingly. - // Any span starting more than 1 char away from the word boundaries in any direction - // does not touch the word, so we don't need to consider it. That's why requesting - // -1 ~ +1 is enough. - // Of course this is only relevant if the cursor is at one end of the word. If it's - // in the middle, the -1 and +1 are not necessary, but they are harmless. - final SuggestionSpan[] spans = text.getSpans(mWordAtCursorStartIndex - 1, - mWordAtCursorEndIndex + 1, SuggestionSpan.class); - int readIndex = 0; - int writeIndex = 0; - for (; readIndex < spans.length; ++readIndex) { - final SuggestionSpan span = spans[readIndex]; - // The span may be null, as we null them when we find duplicates. Cf a few lines - // down. - if (null == span) continue; - // Tentative span start and end. This may be modified later if we realize the - // same span is also applied to other parts of the string. - int spanStart = text.getSpanStart(span); - int spanEnd = text.getSpanEnd(span); - for (int i = readIndex + 1; i < spans.length; ++i) { - if (span.equals(spans[i])) { - // We found the same span somewhere else. Read the new extent of this - // span, and adjust our values accordingly. - spanStart = Math.min(spanStart, text.getSpanStart(spans[i])); - spanEnd = Math.max(spanEnd, text.getSpanEnd(spans[i])); - // ...and mark the span as processed. - spans[i] = null; - } - } - if (spanStart == mWordAtCursorStartIndex && spanEnd == mWordAtCursorEndIndex) { - // If the span does not start and stop here, we ignore it. It probably extends - // past the start or end of the word, as happens in missing space correction - // or EasyEditSpans put by voice input. - spans[writeIndex++] = spans[readIndex]; - } - } - return writeIndex == readIndex ? spans : Arrays.copyOfRange(spans, 0, writeIndex); - } - - public Range(final CharSequence textAtCursor, final int wordAtCursorStartIndex, - final int wordAtCursorEndIndex, final int cursorIndex) { - if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex - || cursorIndex > wordAtCursorEndIndex - || wordAtCursorEndIndex > textAtCursor.length()) { - throw new IndexOutOfBoundsException(); - } - mTextAtCursor = textAtCursor; - mWordAtCursorStartIndex = wordAtCursorStartIndex; - mWordAtCursorEndIndex = wordAtCursorEndIndex; - mCursorIndex = cursorIndex; - mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex); - } - } - private static boolean isSeparator(int code, String sep) { return sep.indexOf(code) != -1; } @@ -581,7 +489,7 @@ public final class RichInputConnection { */ public CharSequence getWordAtCursor(String separators) { // getWordRangeAtCursor returns null if the connection is null - Range r = getWordRangeAtCursor(separators, 0); + TextRange r = getWordRangeAtCursor(separators, 0); return (r == null) ? null : r.mWord; } @@ -593,7 +501,8 @@ public final class RichInputConnection { * be included in the returned range * @return a range containing the text surrounding the cursor */ - public Range getWordRangeAtCursor(final String sep, final int additionalPrecedingWordsCount) { + public TextRange getWordRangeAtCursor(final String sep, + final int additionalPrecedingWordsCount) { mIC = mParent.getCurrentInputConnection(); if (mIC == null || sep == null) { return null; @@ -643,7 +552,7 @@ public final class RichInputConnection { } } - return new Range(TextUtils.concat(before, after), startIndexInBefore, + return new TextRange(TextUtils.concat(before, after), startIndexInBefore, before.length() + endIndexInAfter, before.length()); } diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java index f52e56441..8c41cf8b9 100644 --- a/java/src/com/android/inputmethod/latin/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java @@ -42,8 +42,8 @@ import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; import com.android.inputmethod.latin.userdictionary.UserDictionaryList; import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; import com.android.inputmethod.latin.utils.AdditionalFeaturesSettingUtils; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.FeedbackUtils; -import com.android.inputmethod.latin.utils.Utils; import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; @@ -90,7 +90,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { preferenceScreen.setTitle( - Utils.getAcitivityTitleResId(getActivity(), SettingsActivity.class)); + ApplicationUtils.getAcitivityTitleResId(getActivity(), SettingsActivity.class)); } final Resources res = getResources(); diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 5b47dda0d..22beaefee 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -75,6 +75,21 @@ public final class SuggestedWords { return mSuggestedWordInfoList.get(index); } + public String getDebugString(final int pos) { + if (!LatinImeLogger.sDBG) { + return null; + } + final SuggestedWordInfo wordInfo = getInfo(pos); + if (wordInfo == null) { + return null; + } + final String debugString = wordInfo.getDebugString(); + if (TextUtils.isEmpty(debugString)) { + return null; + } + return debugString; + } + public boolean willAutoCorrect() { return mWillAutoCorrect; } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index ba84c1ad3..ab8f34893 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -240,7 +240,6 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { private void addWords(final Cursor cursor) { final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - clearFusionDictionary(); if (cursor == null) return; if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); @@ -267,4 +266,9 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { protected boolean hasContentChanged() { return true; } + + @Override + protected boolean needsToReloadBeforeWriting() { + return true; + } } diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index ca2d8840b..8c668b810 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -320,7 +320,11 @@ public final class UserHistoryDictionary extends ExpandableDictionary { mUserHistoryDictionary.mBigramListLock.unlock(); } } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) { - doWriteTaskLocked(); + try { + doWriteTaskLocked(); + } finally { + mUserHistoryDictionary.mBigramListLock.unlock(); + } } return null; } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index e078f03f4..2babe8b0c 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -192,6 +192,40 @@ public final class WordComposer { return mCursorPositionWithinWord != mCodePointSize; } + /** + * When the cursor is moved by the user, we need to update its position. + * If it falls inside the currently composing word, we don't reset the composition, and + * only update the cursor position. + * + * @param expectedMoveAmount How many java chars to move the cursor. Negative values move + * the cursor backward, positive values move the cursor forward. + * @return true if the cursor is still inside the composing word, false otherwise. + */ + public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) { + int actualMoveAmountWithinWord = 0; + int cursorPos = mCursorPositionWithinWord; + if (expectedMoveAmount >= 0) { + // Moving the cursor forward for the expected amount or until the end of the word has + // been reached, whichever comes first. + while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) { + actualMoveAmountWithinWord += Character.charCount(mPrimaryKeyCodes[cursorPos]); + ++cursorPos; + } + } else { + // Moving the cursor backward for the expected amount or until the start of the word + // has been reached, whichever comes first. + while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) { + --cursorPos; + actualMoveAmountWithinWord -= Character.charCount(mPrimaryKeyCodes[cursorPos]); + } + } + // If the actual and expected amounts differ, we crossed the start or the end of the word + // so the result would not be inside the composing word. + if (actualMoveAmountWithinWord != expectedMoveAmount) return false; + mCursorPositionWithinWord = cursorPos; + return true; + } + public void setBatchInputPointers(final InputPointers batchPointers) { mInputPointers.set(batchPointers); mIsBatchMode = true; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java index da5812603..999ca775b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java @@ -21,7 +21,7 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ApplicationUtils; /** * Preference screen. @@ -39,7 +39,7 @@ public final class SpellCheckerSettingsFragment extends PreferenceFragment { addPreferencesFromResource(R.xml.spell_checker_settings); final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { - preferenceScreen.setTitle(Utils.getAcitivityTitleResId( + preferenceScreen.setTitle(ApplicationUtils.getAcitivityTitleResId( getActivity(), SpellCheckerSettingsActivity.class)); } } diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 2218b3bc6..e97069dff 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -24,14 +24,13 @@ import android.graphics.drawable.Drawable; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.keyboard.TypefaceUtils; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreSuggestions extends Keyboard { public static final int SUGGESTION_CODE_BASE = 1024; @@ -207,7 +206,7 @@ public final class MoreSuggestions extends Keyboard { final int y = params.getY(index); final int width = params.getWidth(index); final String word = mSuggestedWords.getWord(index); - final String info = Utils.getDebugInfo(mSuggestedWords, index); + final String info = mSuggestedWords.getDebugString(index); final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE; final Key key = new Key( params, word, info, KeyboardIconsSet.ICON_UNDEFINED, indexInMoreSuggestions, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 9565f63f7..ce340b666 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -45,13 +45,12 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.ResourceUtils; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayList; @@ -446,7 +445,7 @@ final class SuggestionStripLayoutHelper { wordView.setTextColor(getSuggestionTextColor(positionInStrip, suggestedWords)); if (SuggestionStripView.DBG) { mDebugInfoViews.get(positionInStrip).setText( - Utils.getDebugInfo(suggestedWords, indexInSuggestedWords)); + suggestedWords.getDebugString(indexInSuggestedWords)); } } } diff --git a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java new file mode 100644 index 000000000..08a2a8c5a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java @@ -0,0 +1,65 @@ +/* + * 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.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.util.Log; + +public final class ApplicationUtils { + private static final String TAG = ApplicationUtils.class.getSimpleName(); + + private ApplicationUtils() { + // This utility class is not publicly instantiable. + } + + public static int getAcitivityTitleResId(final Context context, + final Class<? extends Activity> cls) { + final ComponentName cn = new ComponentName(context, cls); + try { + final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0); + if (ai != null) { + return ai.labelRes; + } + } catch (final NameNotFoundException e) { + Log.e(TAG, "Failed to get settings activity title res id.", e); + } + return 0; + } + + /** + * A utility method to get the application's PackageInfo.versionName + * @return the application's PackageInfo.versionName + */ + public static String getVersionName(final Context context) { + try { + if (context == null) { + return ""; + } + final String packageName = context.getPackageName(); + final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + return info.versionName; + } catch (final NameNotFoundException e) { + Log.e(TAG, "Could not find version info.", e); + } + return ""; + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index b3d37d78c..34eccd65b 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -20,7 +20,6 @@ import android.content.ContentValues; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; -import android.text.format.DateUtils; import android.util.Log; import com.android.inputmethod.latin.AssetFileAddress; @@ -35,6 +34,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; +import java.util.concurrent.TimeUnit; /** * This class encapsulates the logic for the Latin-IME side of dictionary information management. @@ -74,8 +74,8 @@ public class DictionaryInfoUtils { values.put(LOCALE_COLUMN, mLocale.toString()); values.put(DESCRIPTION_COLUMN, mDescription); values.put(LOCAL_FILENAME_COLUMN, mFileAddress.mFilename); - values.put(DATE_COLUMN, - new File(mFileAddress.mFilename).lastModified() / DateUtils.SECOND_IN_MILLIS); + values.put(DATE_COLUMN, TimeUnit.MILLISECONDS.toSeconds( + new File(mFileAddress.mFilename).lastModified())); values.put(FILESIZE_COLUMN, mFileAddress.mLength); values.put(VERSION_COLUMN, mVersion); return values; diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java new file mode 100644 index 000000000..e958a7e71 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java @@ -0,0 +1,77 @@ +/* + * 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.text.TextUtils; + +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.WordComposer; + +public final class LatinImeLoggerUtils { + private LatinImeLoggerUtils() { + // This utility class is not publicly instantiable. + } + + public static void onNonSeparator(final char code, final int x, final int y) { + UserLogRingCharBuffer.getInstance().push(code, x, y); + LatinImeLogger.logOnInputChar(); + } + + public static void onSeparator(final int code, final int x, final int y) { + // Helper method to log a single code point separator + // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils + onSeparator(new String(new int[]{code}, 0, 1), x, y); + } + + public static void onSeparator(final String separator, final int x, final int y) { + final int length = separator.length(); + for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { + int codePoint = Character.codePointAt(separator, i); + // TODO: accept code points + UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); + } + LatinImeLogger.logOnInputSeparator(); + } + + public static void onAutoCorrection(final String typedWord, final String correctedWord, + final String separatorString, final WordComposer wordComposer) { + final boolean isBatchMode = wordComposer.isBatchMode(); + if (!isBatchMode && TextUtils.isEmpty(typedWord)) { + return; + } + // TODO: this fails when the separator is more than 1 code point long, but + // the backend can't handle it yet. The only case when this happens is with + // smileys and other multi-character keys. + final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE + : separatorString.codePointAt(0); + if (!isBatchMode) { + LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); + } else { + if (!TextUtils.isEmpty(correctedWord)) { + // We must make sure that InputPointer contains only the relative timestamps, + // not actual timestamps. + LatinImeLogger.logOnAutoCorrectionForGeometric( + "", correctedWord, codePoint, wordComposer.getInputPointers()); + } + } + } + + public static void onAutoCorrectionCancellation() { + LatinImeLogger.logOnAutoCorrectionCancelled(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java new file mode 100644 index 000000000..5793e4170 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java @@ -0,0 +1,116 @@ +/* + * 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.text.Spanned; +import android.text.style.SuggestionSpan; + +import java.util.Arrays; + +/** + * Represents a range of text, relative to the current cursor position. + */ +public final class TextRange { + private final CharSequence mTextAtCursor; + private final int mWordAtCursorStartIndex; + private final int mWordAtCursorEndIndex; + private final int mCursorIndex; + + public final CharSequence mWord; + + public int getNumberOfCharsInWordBeforeCursor() { + return mCursorIndex - mWordAtCursorStartIndex; + } + + public int getNumberOfCharsInWordAfterCursor() { + return mWordAtCursorEndIndex - mCursorIndex; + } + + /** + * Gets the suggestion spans that are put squarely on the word, with the exact start + * and end of the span matching the boundaries of the word. + * @return the list of spans. + */ + public SuggestionSpan[] getSuggestionSpansAtWord() { + if (!(mTextAtCursor instanceof Spanned && mWord instanceof Spanned)) { + return new SuggestionSpan[0]; + } + final Spanned text = (Spanned)mTextAtCursor; + // Note: it's fine to pass indices negative or greater than the length of the string + // to the #getSpans() method. The reason we need to get from -1 to +1 is that, the + // spans were cut at the cursor position, and #getSpans(start, end) does not return + // spans that end at `start' or begin at `end'. Consider the following case: + // this| is (The | symbolizes the cursor position + // ---- --- + // In this case, the cursor is in position 4, so the 0~7 span has been split into + // a 0~4 part and a 4~7 part. + // If we called #getSpans(0, 4) in this case, we would only get the part from 0 to 4 + // of the span, and not the part from 4 to 7, so we would not realize the span actually + // extends from 0 to 7. But if we call #getSpans(-1, 5) we'll get both the 0~4 and + // the 4~7 spans and we can merge them accordingly. + // Any span starting more than 1 char away from the word boundaries in any direction + // does not touch the word, so we don't need to consider it. That's why requesting + // -1 ~ +1 is enough. + // Of course this is only relevant if the cursor is at one end of the word. If it's + // in the middle, the -1 and +1 are not necessary, but they are harmless. + final SuggestionSpan[] spans = text.getSpans(mWordAtCursorStartIndex - 1, + mWordAtCursorEndIndex + 1, SuggestionSpan.class); + int readIndex = 0; + int writeIndex = 0; + for (; readIndex < spans.length; ++readIndex) { + final SuggestionSpan span = spans[readIndex]; + // The span may be null, as we null them when we find duplicates. Cf a few lines + // down. + if (null == span) continue; + // Tentative span start and end. This may be modified later if we realize the + // same span is also applied to other parts of the string. + int spanStart = text.getSpanStart(span); + int spanEnd = text.getSpanEnd(span); + for (int i = readIndex + 1; i < spans.length; ++i) { + if (span.equals(spans[i])) { + // We found the same span somewhere else. Read the new extent of this + // span, and adjust our values accordingly. + spanStart = Math.min(spanStart, text.getSpanStart(spans[i])); + spanEnd = Math.max(spanEnd, text.getSpanEnd(spans[i])); + // ...and mark the span as processed. + spans[i] = null; + } + } + if (spanStart == mWordAtCursorStartIndex && spanEnd == mWordAtCursorEndIndex) { + // If the span does not start and stop here, we ignore it. It probably extends + // past the start or end of the word, as happens in missing space correction + // or EasyEditSpans put by voice input. + spans[writeIndex++] = spans[readIndex]; + } + } + return writeIndex == readIndex ? spans : Arrays.copyOfRange(spans, 0, writeIndex); + } + + public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex, + final int wordAtCursorEndIndex, final int cursorIndex) { + if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex + || cursorIndex > wordAtCursorEndIndex + || wordAtCursorEndIndex > textAtCursor.length()) { + throw new IndexOutOfBoundsException(); + } + mTextAtCursor = textAtCursor; + mWordAtCursorStartIndex = wordAtCursorStartIndex; + mWordAtCursorEndIndex = wordAtCursorEndIndex; + mCursorIndex = cursorIndex; + mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex); + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/keyboard/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index c3b952037..544e4d201 100644 --- a/java/src/com/android/inputmethod/keyboard/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.latin.utils; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.util.SparseArray; -import com.android.inputmethod.latin.utils.CollectionUtils; - public final class TypefaceUtils { private TypefaceUtils() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java new file mode 100644 index 000000000..ef9cacf61 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2016 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.Intent; +import android.content.pm.PackageManager; +import android.inputmethodservice.InputMethodService; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.util.Log; + +import com.android.inputmethod.latin.LatinImeLogger; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.channels.FileChannel; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public final class UsabilityStudyLogUtils { + // TODO: remove code duplication with ResearchLog class + private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); + private static final String FILENAME = "log.txt"; + private final Handler mLoggingHandler; + private File mFile; + private File mDirectory; + private InputMethodService mIms; + private PrintWriter mWriter; + private final Date mDate; + private final SimpleDateFormat mDateFormat; + + private UsabilityStudyLogUtils() { + mDate = new Date(); + mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); + + HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", + Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + mLoggingHandler = new Handler(handlerThread.getLooper()); + } + + // Initialization-on-demand holder + private static final class OnDemandInitializationHolder { + public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); + } + + public static UsabilityStudyLogUtils getInstance() { + return OnDemandInitializationHolder.sInstance; + } + + public void init(final InputMethodService ims) { + mIms = ims; + mDirectory = ims.getFilesDir(); + } + + private void createLogFileIfNotExist() { + if ((mFile == null || !mFile.exists()) + && (mDirectory != null && mDirectory.exists())) { + try { + mWriter = getPrintWriter(mDirectory, FILENAME, false); + } catch (final IOException e) { + Log.e(USABILITY_TAG, "Can't create log file."); + } + } + } + + public static void writeBackSpace(final int x, final int y) { + UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); + } + + public static void writeChar(final char c, final int x, final int y) { + String inputChar = String.valueOf(c); + switch (c) { + case '\n': + inputChar = "<enter>"; + break; + case '\t': + inputChar = "<tab>"; + break; + case ' ': + inputChar = "<space>"; + break; + } + UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); + LatinImeLogger.onPrintAllUsabilityStudyLogs(); + } + + public void write(final String log) { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + createLogFileIfNotExist(); + final long currentTime = System.currentTimeMillis(); + mDate.setTime(currentTime); + + final String printString = String.format(Locale.US, "%s\t%d\t%s\n", + mDateFormat.format(mDate), currentTime, log); + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Write: " + log); + } + mWriter.print(printString); + } + }); + } + + private synchronized String getBufferedLogs() { + mWriter.flush(); + final StringBuilder sb = new StringBuilder(); + final BufferedReader br = getBufferedReader(); + String line; + try { + while ((line = br.readLine()) != null) { + sb.append('\n'); + sb.append(line); + } + } catch (final IOException e) { + Log.e(USABILITY_TAG, "Can't read log file."); + } finally { + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); + } + try { + br.close(); + } catch (final IOException e) { + // ignore. + } + } + return sb.toString(); + } + + public void emailResearcherLogsAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + final Date date = new Date(); + date.setTime(System.currentTimeMillis()); + final String currentDateTimeString = + new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); + if (mFile == null) { + Log.w(USABILITY_TAG, "No internal log file found."); + return; + } + if (mIms.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); + return; + } + mWriter.flush(); + final String destPath = Environment.getExternalStorageDirectory() + + "/research-" + currentDateTimeString + ".log"; + final File destFile = new File(destPath); + try { + final FileInputStream srcStream = new FileInputStream(mFile); + final FileOutputStream destStream = new FileOutputStream(destFile); + final FileChannel src = srcStream.getChannel(); + final FileChannel dest = destStream.getChannel(); + src.transferTo(0, src.size(), dest); + src.close(); + srcStream.close(); + dest.close(); + destStream.close(); + } catch (final FileNotFoundException e1) { + Log.w(USABILITY_TAG, e1); + return; + } catch (final IOException e2) { + Log.w(USABILITY_TAG, e2); + return; + } + if (destFile == null || !destFile.exists()) { + Log.w(USABILITY_TAG, "Dest file doesn't exist."); + return; + } + final Intent intent = new Intent(Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); + } + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); + intent.putExtra(Intent.EXTRA_SUBJECT, + "[Research Logs] " + currentDateTimeString); + mIms.startActivity(intent); + } + }); + } + + public void printAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); + } + }); + } + + public void clearAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + if (mFile != null && mFile.exists()) { + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Delete log file."); + } + mFile.delete(); + mWriter.close(); + } + } + }); + } + + private BufferedReader getBufferedReader() { + createLogFileIfNotExist(); + try { + return new BufferedReader(new FileReader(mFile)); + } catch (final FileNotFoundException e) { + return null; + } + } + + private PrintWriter getPrintWriter(final File dir, final String filename, + final boolean renew) throws IOException { + mFile = new File(dir, filename); + if (mFile.exists()) { + if (renew) { + mFile.delete(); + } + } + return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java index 9f842f976..713a45bda 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java @@ -16,9 +16,10 @@ package com.android.inputmethod.latin.utils; -import android.text.format.DateUtils; import android.util.Log; +import java.util.concurrent.TimeUnit; + public final class UserHistoryForgettingCurveUtils { private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName(); private static final boolean DEBUG = false; @@ -27,8 +28,8 @@ public final class UserHistoryForgettingCurveUtils { private static final int FC_LEVEL_MAX = 3; /* package */ static final int ELAPSED_TIME_MAX = 15; private static final int ELAPSED_TIME_INTERVAL_HOURS = 6; - private static final long ELAPSED_TIME_INTERVAL_MILLIS = ELAPSED_TIME_INTERVAL_HOURS - * DateUtils.HOUR_IN_MILLIS; + private static final long ELAPSED_TIME_INTERVAL_MILLIS = + TimeUnit.HOURS.toMillis(ELAPSED_TIME_INTERVAL_HOURS); private static final int HALF_LIFE_HOURS = 48; private static final int MAX_PUSH_ELAPSED = (FC_LEVEL_MAX + 1) * (ELAPSED_TIME_MAX + 1); diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java index 3e67e8216..a2c6c4524 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java +++ b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java @@ -20,7 +20,6 @@ import android.inputmethodservice.InputMethodService; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Settings; -import com.android.inputmethod.latin.utils.Utils.UsabilityStudyLogUtils; public final class UserLogRingCharBuffer { public /* for test */ static final int BUFSIZE = 20; diff --git a/java/src/com/android/inputmethod/latin/utils/Utils.java b/java/src/com/android/inputmethod/latin/utils/Utils.java deleted file mode 100644 index c4e18ed7e..000000000 --- a/java/src/com/android/inputmethod/latin/utils/Utils.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * 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.latin.utils; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.inputmethodservice.InputMethodService; -import android.net.Uri; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.text.TextUtils; -import android.util.Log; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.WordComposer; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.channels.FileChannel; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -// TODO: Come up with a more descriptive class name -public final class Utils { - private static final String TAG = Utils.class.getSimpleName(); - - private Utils() { - // This utility class is not publicly instantiable. - } - - // TODO: Make this an external class - public static final class UsabilityStudyLogUtils { - // TODO: remove code duplication with ResearchLog class - private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); - private static final String FILENAME = "log.txt"; - private final Handler mLoggingHandler; - private File mFile; - private File mDirectory; - private InputMethodService mIms; - private PrintWriter mWriter; - private final Date mDate; - private final SimpleDateFormat mDateFormat; - - private UsabilityStudyLogUtils() { - mDate = new Date(); - mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); - - HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mLoggingHandler = new Handler(handlerThread.getLooper()); - } - - // Initialization-on-demand holder - private static final class OnDemandInitializationHolder { - public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); - } - - public static UsabilityStudyLogUtils getInstance() { - return OnDemandInitializationHolder.sInstance; - } - - public void init(final InputMethodService ims) { - mIms = ims; - mDirectory = ims.getFilesDir(); - } - - private void createLogFileIfNotExist() { - if ((mFile == null || !mFile.exists()) - && (mDirectory != null && mDirectory.exists())) { - try { - mWriter = getPrintWriter(mDirectory, FILENAME, false); - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't create log file."); - } - } - } - - public static void writeBackSpace(final int x, final int y) { - UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); - } - - public static void writeChar(final char c, final int x, final int y) { - String inputChar = String.valueOf(c); - switch (c) { - case '\n': - inputChar = "<enter>"; - break; - case '\t': - inputChar = "<tab>"; - break; - case ' ': - inputChar = "<space>"; - break; - } - UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); - LatinImeLogger.onPrintAllUsabilityStudyLogs(); - } - - public void write(final String log) { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - createLogFileIfNotExist(); - final long currentTime = System.currentTimeMillis(); - mDate.setTime(currentTime); - - final String printString = String.format(Locale.US, "%s\t%d\t%s\n", - mDateFormat.format(mDate), currentTime, log); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Write: " + log); - } - mWriter.print(printString); - } - }); - } - - private synchronized String getBufferedLogs() { - mWriter.flush(); - final StringBuilder sb = new StringBuilder(); - final BufferedReader br = getBufferedReader(); - String line; - try { - while ((line = br.readLine()) != null) { - sb.append('\n'); - sb.append(line); - } - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't read log file."); - } finally { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); - } - try { - br.close(); - } catch (final IOException e) { - // ignore. - } - } - return sb.toString(); - } - - public void emailResearcherLogsAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - final Date date = new Date(); - date.setTime(System.currentTimeMillis()); - final String currentDateTimeString = - new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); - if (mFile == null) { - Log.w(USABILITY_TAG, "No internal log file found."); - return; - } - if (mIms.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); - return; - } - mWriter.flush(); - final String destPath = Environment.getExternalStorageDirectory() - + "/research-" + currentDateTimeString + ".log"; - final File destFile = new File(destPath); - try { - final FileInputStream srcStream = new FileInputStream(mFile); - final FileOutputStream destStream = new FileOutputStream(destFile); - final FileChannel src = srcStream.getChannel(); - final FileChannel dest = destStream.getChannel(); - src.transferTo(0, src.size(), dest); - src.close(); - srcStream.close(); - dest.close(); - destStream.close(); - } catch (final FileNotFoundException e1) { - Log.w(USABILITY_TAG, e1); - return; - } catch (final IOException e2) { - Log.w(USABILITY_TAG, e2); - return; - } - if (destFile == null || !destFile.exists()) { - Log.w(USABILITY_TAG, "Dest file doesn't exist."); - return; - } - final Intent intent = new Intent(Intent.ACTION_SEND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); - } - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); - intent.putExtra(Intent.EXTRA_SUBJECT, - "[Research Logs] " + currentDateTimeString); - mIms.startActivity(intent); - } - }); - } - - public void printAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); - } - }); - } - - public void clearAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - if (mFile != null && mFile.exists()) { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Delete log file."); - } - mFile.delete(); - mWriter.close(); - } - } - }); - } - - private BufferedReader getBufferedReader() { - createLogFileIfNotExist(); - try { - return new BufferedReader(new FileReader(mFile)); - } catch (final FileNotFoundException e) { - return null; - } - } - - private PrintWriter getPrintWriter(final File dir, final String filename, - final boolean renew) throws IOException { - mFile = new File(dir, filename); - if (mFile.exists()) { - if (renew) { - mFile.delete(); - } - } - return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); - } - } - - // TODO: Make this an external class - public static final class Stats { - public static void onNonSeparator(final char code, final int x, final int y) { - UserLogRingCharBuffer.getInstance().push(code, x, y); - LatinImeLogger.logOnInputChar(); - } - - public static void onSeparator(final int code, final int x, final int y) { - // Helper method to log a single code point separator - // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils - onSeparator(new String(new int[]{code}, 0, 1), x, y); - } - - public static void onSeparator(final String separator, final int x, final int y) { - final int length = separator.length(); - for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { - int codePoint = Character.codePointAt(separator, i); - // TODO: accept code points - UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); - } - LatinImeLogger.logOnInputSeparator(); - } - - public static void onAutoCorrection(final String typedWord, final String correctedWord, - final String separatorString, final WordComposer wordComposer) { - final boolean isBatchMode = wordComposer.isBatchMode(); - if (!isBatchMode && TextUtils.isEmpty(typedWord)) { - return; - } - // TODO: this fails when the separator is more than 1 code point long, but - // the backend can't handle it yet. The only case when this happens is with - // smileys and other multi-character keys. - final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE - : separatorString.codePointAt(0); - if (!isBatchMode) { - LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); - } else { - if (!TextUtils.isEmpty(correctedWord)) { - // We must make sure that InputPointer contains only the relative timestamps, - // not actual timestamps. - LatinImeLogger.logOnAutoCorrectionForGeometric( - "", correctedWord, codePoint, wordComposer.getInputPointers()); - } - } - } - - public static void onAutoCorrectionCancellation() { - LatinImeLogger.logOnAutoCorrectionCancelled(); - } - } - - public static String getDebugInfo(final SuggestedWords suggestions, final int pos) { - if (!LatinImeLogger.sDBG) { - return null; - } - final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); - if (wordInfo == null) { - return null; - } - final String info = wordInfo.getDebugString(); - if (TextUtils.isEmpty(info)) { - return null; - } - return info; - } - - public static int getAcitivityTitleResId(final Context context, - final Class<? extends Activity> cls) { - final ComponentName cn = new ComponentName(context, cls); - try { - final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0); - if (ai != null) { - return ai.labelRes; - } - } catch (final NameNotFoundException e) { - Log.e(TAG, "Failed to get settings activity title res id.", e); - } - return 0; - } - - /** - * A utility method to get the application's PackageInfo.versionName - * @return the application's PackageInfo.versionName - */ - public static String getVersionName(final Context context) { - try { - if (context == null) { - return ""; - } - final String packageName = context.getPackageName(); - final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); - return info.versionName; - } catch (final NameNotFoundException e) { - Log.e(TAG, "Could not find version info.", e); - } - return ""; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java index dc12fa468..f9d853493 100644 --- a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.latin.utils; import android.view.View; import android.view.ViewGroup; @@ -27,7 +27,8 @@ public final class ViewLayoutUtils { // This utility class is not publicly instantiable. } - public static MarginLayoutParams newLayoutParam(ViewGroup placer, int width, int height) { + public static MarginLayoutParams newLayoutParam(final ViewGroup placer, final int width, + final int height) { if (placer instanceof FrameLayout) { return new FrameLayout.LayoutParams(width, height); } else if (placer instanceof RelativeLayout) { @@ -40,7 +41,8 @@ public final class ViewLayoutUtils { } } - public static void placeViewAt(View view, int x, int y, int w, int h) { + public static void placeViewAt(final View view, final int x, final int y, final int w, + final int h) { final ViewGroup.LayoutParams lp = view.getLayoutParams(); if (lp instanceof MarginLayoutParams) { final MarginLayoutParams marginLayoutParams = (MarginLayoutParams)lp; diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 88207c024..46e620ae5 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -55,7 +55,7 @@ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static final long FLUSH_DELAY_IN_MS = TimeUnit.SECONDS.toMillis(5); /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index ecf809242..ed047e13a 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,7 +37,6 @@ import android.os.IBinder; import android.os.SystemClock; import android.preference.PreferenceManager; import android.text.TextUtils; -import android.text.format.DateUtils; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -57,11 +56,11 @@ import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputConnection; -import com.android.inputmethod.latin.RichInputConnection.Range; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.InputTypeUtils; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.MotionEventReader.ReplayData; import com.android.inputmethod.research.ui.SplashScreen; @@ -75,6 +74,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; // TODO: Add a unit test for every "logging" method (i.e. that is called from the IME and calls @@ -137,10 +137,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_SAVED_CHANNEL = "pref_research_saved_channel"; - private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = 5 * 1000; - private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = 5 * 1000; - private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS; - private static final long MAX_LOGFILE_AGE_IN_MS = 4 * DateUtils.DAY_IN_MILLIS; + private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); + private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); + private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = TimeUnit.DAYS.toMillis(1); + private static final long MAX_LOGFILE_AGE_IN_MS = TimeUnit.DAYS.toMillis(4); private static final ResearchLogger sInstance = new ResearchLogger(); private static String sAccountType = null; @@ -195,7 +195,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // not performed on text that the user types into the feedback dialog. private boolean mInFeedbackDialog = false; private Handler mUserRecordingTimeoutHandler; - private static final long USER_RECORDING_TIMEOUT_MS = 30L * DateUtils.SECOND_IN_MILLIS; + private static final long USER_RECORDING_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(30); // Stores a temporary LogUnit while generating a phantom space. Needed because phantom spaces // are issued out-of-order, immediately before the characters generated by other operations that @@ -542,8 +542,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang toast.show(); boolean isLogDeleted = abort(); final long currentTime = System.currentTimeMillis(); - final long resumeTime = currentTime + 1000 * 60 * - SUSPEND_DURATION_IN_MINUTES; + final long resumeTime = currentTime + + TimeUnit.MINUTES.toMillis(SUSPEND_DURATION_IN_MINUTES); suspendLoggingUntil(resumeTime); toast.cancel(); Toast.makeText(latinIME, R.string.research_notify_logging_suspended, @@ -635,7 +635,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMotionEventReader.readMotionEventData(mUserRecordingFile); mReplayer.replay(replayData, null); } - }, 1000); + }, TimeUnit.SECONDS.toMillis(1)); } if (FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD) { @@ -1220,7 +1220,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final RichInputConnection connection) { String word = ""; if (connection != null) { - Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); + TextRange range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); if (range != null) { word = range.mWord.toString(); } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index e573ca012..fd323a104 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -21,6 +21,8 @@ import android.util.Log; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.define.ProductionFlag; +import java.util.concurrent.TimeUnit; + public class Statistics { private static final String TAG = Statistics.class.getSimpleName(); private static final boolean DEBUG = false @@ -102,8 +104,8 @@ public class Statistics { // To account for the interruptions when the user's attention is directed elsewhere, times // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds + public static final long MIN_TYPING_INTERMISSION = TimeUnit.SECONDS.toMillis(2); + public static final long MIN_DELETION_INTERMISSION = TimeUnit.SECONDS.toMillis(10); // The last time that a tap was performed private long mLastTapTime; diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 8bd46c19e..fd3f2f60e 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -34,7 +34,6 @@ public final class UploaderService extends IntentService { public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; public static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() + ".extra.UPLOAD_UNCONDITIONALLY"; - protected static final int TIMEOUT_IN_MS = 1000 * 4; public UploaderService() { super("Research Uploader Service"); diff --git a/native/jni/Android.mk b/native/jni/Android.mk index f89eea735..d78da969b 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -79,8 +79,9 @@ LATIN_IME_CORE_SRC_FILES := \ typing_traversal.cpp \ typing_weighting.cpp) \ $(addprefix utils/, \ + autocorrection_threshold_utils.cpp \ char_utils.cpp \ - autocorrection_threshold_utils.cpp) + log_utils.cpp) LOCAL_SRC_FILES := \ $(LATIN_IME_JNI_SRC_FILES) \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index a93bbeb8c..6e1b80ee0 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -93,8 +93,8 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s AKLOGE("DICT: dictionary format is unknown, bad magic number"); releaseDictBuf(static_cast<const char *>(dictBuf) - offset, adjDictSize, fd); } else { - dictionary = new Dictionary( - dictBuf, static_cast<int>(dictSize), fd, offset, updatableMmap); + dictionary = new Dictionary(env, dictBuf, static_cast<int>(dictSize), fd, offset, + updatableMmap); } PROF_END(66); PROF_CLOSE; @@ -201,7 +201,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict, jintArray word) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); - if (!dictionary) return 0; + if (!dictionary) return NOT_A_PROBABILITY; const jsize wordLength = env->GetArrayLength(word); int codePoints[wordLength]; env->GetIntArrayRegion(word, 0, wordLength, codePoints); diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index cb6681456..607a74400 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -35,46 +35,74 @@ // Must be equal to ProximityInfo.MAX_PROXIMITY_CHARS_SIZE in Java #define MAX_PROXIMITY_CHARS_SIZE 16 #define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2 +#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) -#if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG) -#include <android/log.h> -#ifndef LOG_TAG -#define LOG_TAG "LatinIME: " -#endif // LOG_TAG -#define AKLOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__) -#define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__) - -#define DUMP_RESULT(words, frequencies) do { dumpResult(words, frequencies); } while (0) -#define DUMP_WORD(word, length) do { dumpWord(word, length); } while (0) -#define INTS_TO_CHARS(input, length, output) do { \ - intArrayToCharArray(input, length, output); } while (0) - -// TODO: Support full UTF-8 conversion -AK_FORCE_INLINE static int intArrayToCharArray(const int *source, const int sourceSize, - char *dest) { +AK_FORCE_INLINE static int intArrayToCharArray(const int *const source, const int sourceSize, + char *dest, const int destSize) { + // We want to always terminate with a 0 char, so stop one short of the length to make + // sure there is room. + const int destLimit = destSize - 1; int si = 0; int di = 0; - while (si < sourceSize && di < MAX_WORD_LENGTH - 1 && 0 != source[si]) { + while (si < sourceSize && di < destLimit && 0 != source[si]) { const int codePoint = source[si++]; - if (codePoint < 0x7F) { + if (codePoint < 0x7F) { // One byte dest[di++] = codePoint; - } else if (codePoint < 0x7FF) { + } else if (codePoint < 0x7FF) { // Two bytes + if (di + 1 >= destLimit) break; dest[di++] = 0xC0 + (codePoint >> 6); dest[di++] = 0x80 + (codePoint & 0x3F); - } else if (codePoint < 0xFFFF) { + } else if (codePoint < 0xFFFF) { // Three bytes + if (di + 2 >= destLimit) break; dest[di++] = 0xE0 + (codePoint >> 12); - dest[di++] = 0x80 + ((codePoint & 0xFC0) >> 6); + dest[di++] = 0x80 + ((codePoint >> 6) & 0x3F); + dest[di++] = 0x80 + (codePoint & 0x3F); + } else if (codePoint <= 0x1FFFFF) { // Four bytes + if (di + 3 >= destLimit) break; + dest[di++] = 0xF0 + (codePoint >> 18); + dest[di++] = 0x80 + ((codePoint >> 12) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 6) & 0x3F); dest[di++] = 0x80 + (codePoint & 0x3F); + } else if (codePoint <= 0x3FFFFFF) { // Five bytes + if (di + 4 >= destLimit) break; + dest[di++] = 0xF8 + (codePoint >> 24); + dest[di++] = 0x80 + ((codePoint >> 18) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 12) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 6) & 0x3F); + dest[di++] = codePoint & 0x3F; + } else if (codePoint <= 0x7FFFFFFF) { // Six bytes + if (di + 5 >= destLimit) break; + dest[di++] = 0xFC + (codePoint >> 30); + dest[di++] = 0x80 + ((codePoint >> 24) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 18) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 12) & 0x3F); + dest[di++] = 0x80 + ((codePoint >> 6) & 0x3F); + dest[di++] = codePoint & 0x3F; + } else { + // Not a code point... skip. } } dest[di] = 0; return di; } +#if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG) +#include <android/log.h> +#ifndef LOG_TAG +#define LOG_TAG "LatinIME: " +#endif // LOG_TAG +#define AKLOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__) +#define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__) + +#define DUMP_RESULT(words, frequencies) do { dumpResult(words, frequencies); } while (0) +#define DUMP_WORD(word, length) do { dumpWord(word, length); } while (0) +#define INTS_TO_CHARS(input, length, output, outlength) do { \ + intArrayToCharArray(input, length, output, outlength); } while (0) + static inline void dumpWordInfo(const int *word, const int length, const int rank, const int probability) { static char charBuf[50]; - const int N = intArrayToCharArray(word, length, charBuf); + const int N = intArrayToCharArray(word, length, charBuf, NELEMS(charBuf)); if (N > 1) { AKLOGI("%2d [ %s ] (%d)", rank, charBuf, probability); } @@ -90,7 +118,7 @@ static inline void dumpResult(const int *outWords, const int *frequencies) { static AK_FORCE_INLINE void dumpWord(const int *word, const int length) { static char charBuf[50]; - const int N = intArrayToCharArray(word, length, charBuf); + const int N = intArrayToCharArray(word, length, charBuf, NELEMS(charBuf)); if (N > 1) { AKLOGI("[ %s ]", charBuf); } @@ -304,8 +332,6 @@ static inline void prof_out(void) { template<typename T> AK_FORCE_INLINE const T &min(const T &a, const T &b) { return a < b ? a : b; } template<typename T> AK_FORCE_INLINE const T &max(const T &a, const T &b) { return a > b ? a : b; } -#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) - // DEBUG #define INPUTLENGTH_FOR_DEBUG (-1) #define MIN_OUTPUT_INDEX_FOR_DEBUG (-1) diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 52db8e9c7..973da67e4 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -28,15 +28,16 @@ #if DEBUG_DICT #define LOGI_SHOW_ADD_COST_PROP \ do { char charBuf[50]; \ - INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf); \ + INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf, NELEMS(charBuf)); \ AKLOGI("%20s, \"%c\", size = %03d, total = %03d, index(0) = %02d, dist = %.4f, %s,,", \ __FUNCTION__, getNodeCodePoint(), inputSize, getTotalInputIndex(), \ getInputIndex(0), getNormalizedCompoundDistance(), charBuf); } while (0) #define DUMP_WORD_AND_SCORE(header) \ do { char charBuf[50]; char prevWordCharBuf[50]; \ - INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf); \ + INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf, NELEMS(charBuf)); \ INTS_TO_CHARS(mDicNodeState.mDicNodeStatePrevWord.mPrevWord, \ - mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(), prevWordCharBuf); \ + mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(), prevWordCharBuf, \ + NELEMS(prevWordCharBuf)); \ AKLOGI("#%8s, %5f, %5f, %5f, %5f, %s, %s, %d,,", header, \ getSpatialDistanceForScoring(), getLanguageDistanceForScoring(), \ getNormalizedCompoundDistance(), getRawLength(), prevWordCharBuf, charBuf, \ @@ -111,32 +112,23 @@ class DicNode { mIsUsed = true; mIsCachedForNextSuggestion = false; mDicNodeProperties.init( - NOT_A_DICT_POS, 0 /* flags */, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, + NOT_A_DICT_POS, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, - false /* isTerminal */, true /* hasChildren */, 0 /* depth */, - 0 /* terminalDepth */); + false /* isTerminal */, true /* hasChildren */, + false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); mDicNodeState.init(prevWordNodePos); PROF_NODE_RESET(mProfiler); } - void initAsPassingChild(DicNode *parentNode) { - mIsUsed = true; - mIsCachedForNextSuggestion = parentNode->mIsCachedForNextSuggestion; - const int c = parentNode->getNodeTypedCodePoint(); - mDicNodeProperties.init(&parentNode->mDicNodeProperties, c); - mDicNodeState.init(&parentNode->mDicNodeState); - PROF_NODE_COPY(&parentNode->mProfiler, mProfiler); - } - // Init for root with previous word void initAsRootWithPreviousWord(DicNode *dicNode, const int rootGroupPos) { mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.init( - NOT_A_DICT_POS, 0 /* flags */, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, + NOT_A_DICT_POS, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, - false /* isTerminal */, true /* hasChildren */, 0 /* depth */, - 0 /* terminalDepth */); + false /* isTerminal */, true /* hasChildren */, + false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); // TODO: Move to dicNodeState? mDicNodeState.mDicNodeStateOutput.init(); // reset for next word mDicNodeState.mDicNodeStateInput.init( @@ -156,18 +148,27 @@ class DicNode { PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - // TODO: minimize arguments by looking binary_format - void initAsChild(DicNode *dicNode, const int pos, const uint8_t flags, const int childrenPos, + void initAsPassingChild(DicNode *parentNode) { + mIsUsed = true; + mIsCachedForNextSuggestion = parentNode->mIsCachedForNextSuggestion; + const int c = parentNode->getNodeTypedCodePoint(); + mDicNodeProperties.init(&parentNode->mDicNodeProperties, c); + mDicNodeState.init(&parentNode->mDicNodeState); + PROF_NODE_COPY(&parentNode->mProfiler, mProfiler); + } + + void initAsChild(DicNode *dicNode, const int pos, const int childrenPos, const int attributesPos, const int probability, const bool isTerminal, - const bool hasChildren, const uint16_t mergedNodeCodePointCount, - const int *const mergedNodeCodePoints) { + const bool hasChildren, const bool isBlacklistedOrNotAWord, + const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { mIsUsed = true; uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1); mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; const uint16_t newLeavingDepth = static_cast<uint16_t>( dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount); - mDicNodeProperties.init(pos, flags, childrenPos, attributesPos, mergedNodeCodePoints[0], - probability, isTerminal, hasChildren, newDepth, newLeavingDepth); + mDicNodeProperties.init(pos, childrenPos, attributesPos, mergedNodeCodePoints[0], + probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth, + newLeavingDepth); mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount, mergedNodeCodePoints); PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); @@ -215,7 +216,7 @@ class DicNode { } bool isImpossibleBigramWord() const { - if (mDicNodeProperties.hasBlacklistedOrNotAWordFlag()) { + if (isBlacklistedOrNotAWord()) { return true; } const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength() @@ -462,8 +463,8 @@ class DicNode { return mDicNodeState.mDicNodeStateScoring.isExactMatch(); } - uint8_t getFlags() const { - return mDicNodeProperties.getFlags(); + bool isBlacklistedOrNotAWord() const { + return mDicNodeProperties.isBlacklistedOrNotAWord(); } int getAttributesPos() const { @@ -503,6 +504,12 @@ class DicNode { if (!right->isUsed()) { return false; } + // Promote exact matches to prevent them from being pruned. + const bool leftExactMatch = isExactMatch(); + const bool rightExactMatch = right->isExactMatch(); + if (leftExactMatch != rightExactMatch) { + return leftExactMatch; + } const float diff = right->getNormalizedCompoundDistance() - getNormalizedCompoundDistance(); static const float MIN_DIFF = 0.000001f; diff --git a/native/jni/src/suggest/core/dicnode/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/dic_node_properties.h index 7e8aa4979..d98000d83 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_properties.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_properties.h @@ -20,7 +20,6 @@ #include <stdint.h> #include "defines.h" -#include "suggest/core/dictionary/binary_format.h" namespace latinime { @@ -32,24 +31,25 @@ namespace latinime { class DicNodeProperties { public: AK_FORCE_INLINE DicNodeProperties() - : mPos(0), mFlags(0), mChildrenPos(0), mAttributesPos(0), mProbability(0), - mNodeCodePoint(0), mDepth(0), mLeavingDepth(0), mIsTerminal(false), - mHasChildren(false) {} + : mPos(0), mChildrenPos(0), mAttributesPos(0), mProbability(0), + mNodeCodePoint(0), mIsTerminal(false), mHasChildren(false), + mIsBlacklistedOrNotAWord(false), mDepth(0), mLeavingDepth(0) {} virtual ~DicNodeProperties() {} // Should be called only once per DicNode is initialized. - void init(const int pos, const uint8_t flags, const int childrenPos, const int attributesPos, + void init(const int pos, const int childrenPos, const int attributesPos, const int nodeCodePoint, const int probability, const bool isTerminal, - const bool hasChildren, const uint16_t depth, const uint16_t leavingDepth) { + const bool hasChildren, const bool isBlacklistedOrNotAWord, + const uint16_t depth, const uint16_t leavingDepth) { mPos = pos; - mFlags = flags; mChildrenPos = childrenPos; mAttributesPos = attributesPos; mNodeCodePoint = nodeCodePoint; mProbability = probability; mIsTerminal = isTerminal; mHasChildren = hasChildren; + mIsBlacklistedOrNotAWord = isBlacklistedOrNotAWord; mDepth = depth; mLeavingDepth = leavingDepth; } @@ -57,13 +57,13 @@ class DicNodeProperties { // Init for copy void init(const DicNodeProperties *const nodeProp) { mPos = nodeProp->mPos; - mFlags = nodeProp->mFlags; mChildrenPos = nodeProp->mChildrenPos; mAttributesPos = nodeProp->mAttributesPos; mNodeCodePoint = nodeProp->mNodeCodePoint; mProbability = nodeProp->mProbability; mIsTerminal = nodeProp->mIsTerminal; mHasChildren = nodeProp->mHasChildren; + mIsBlacklistedOrNotAWord = nodeProp->mIsBlacklistedOrNotAWord; mDepth = nodeProp->mDepth; mLeavingDepth = nodeProp->mLeavingDepth; } @@ -71,13 +71,13 @@ class DicNodeProperties { // Init as passing child void init(const DicNodeProperties *const nodeProp, const int codePoint) { mPos = nodeProp->mPos; - mFlags = nodeProp->mFlags; mChildrenPos = nodeProp->mChildrenPos; mAttributesPos = nodeProp->mAttributesPos; mNodeCodePoint = codePoint; // Overwrite the node char of a passing child mProbability = nodeProp->mProbability; mIsTerminal = nodeProp->mIsTerminal; mHasChildren = nodeProp->mHasChildren; + mIsBlacklistedOrNotAWord = nodeProp->mIsBlacklistedOrNotAWord; mDepth = nodeProp->mDepth + 1; // Increment the depth of a passing child mLeavingDepth = nodeProp->mLeavingDepth; } @@ -86,10 +86,6 @@ class DicNodeProperties { return mPos; } - uint8_t getFlags() const { - return mFlags; - } - int getChildrenPos() const { return mChildrenPos; } @@ -123,8 +119,8 @@ class DicNodeProperties { return mHasChildren || mDepth != mLeavingDepth; } - bool hasBlacklistedOrNotAWordFlag() const { - return BinaryFormat::hasBlacklistedOrNotAWordFlag(mFlags); + bool isBlacklistedOrNotAWord() const { + return mIsBlacklistedOrNotAWord; } private: @@ -132,15 +128,15 @@ class DicNodeProperties { // Use a default copy constructor and an assign operator because shallow copies are ok // for this class int mPos; - uint8_t mFlags; int mChildrenPos; int mAttributesPos; int mProbability; int mNodeCodePoint; - uint16_t mDepth; - uint16_t mLeavingDepth; bool mIsTerminal; bool mHasChildren; + bool mIsBlacklistedOrNotAWord; + uint16_t mDepth; + uint16_t mLeavingDepth; }; } // namespace latinime #endif // LATINIME_DIC_NODE_PROPERTIES_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h b/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h new file mode 100644 index 000000000..1a39f2ef3 --- /dev/null +++ b/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef LATINIME_DIC_NODE_PROXIMITY_FILTER_H +#define LATINIME_DIC_NODE_PROXIMITY_FILTER_H + +#include "defines.h" +#include "suggest/core/layout/proximity_info_state.h" +#include "suggest/core/layout/proximity_info_utils.h" +#include "suggest/core/policy/dictionary_structure_policy.h" + +namespace latinime { + +class DicNodeProximityFilter : public DictionaryStructurePolicy::NodeFilter { + public: + DicNodeProximityFilter(const ProximityInfoState *const pInfoState, + const int pointIndex, const bool exactOnly) + : mProximityInfoState(pInfoState), mPointIndex(pointIndex), mExactOnly(exactOnly) {} + + bool isFilteredOut(const int codePoint) const { + return !isProximityCodePoint(codePoint); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeProximityFilter); + + const ProximityInfoState *const mProximityInfoState; + const int mPointIndex; + const bool mExactOnly; + + // TODO: Move to proximity info state + bool isProximityCodePoint(const int codePoint) const { + if (!mProximityInfoState) { + return true; + } + if (mExactOnly) { + return mProximityInfoState->getPrimaryCodePointAt(mPointIndex) == codePoint; + } + const ProximityType matchedId = mProximityInfoState->getProximityType( + mPointIndex, codePoint, true /* checkProximityChars */); + return ProximityInfoUtils::isMatchOrProximityChar(matchedId); + } +}; +} // namespace latinime +#endif // LATINIME_DIC_NODE_PROXIMITY_FILTER_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 9bf7eceb5..6c7f6667a 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -14,18 +14,17 @@ * limitations under the License. */ +#include "suggest/core/dicnode/dic_node_utils.h" + #include <cstring> -#include <vector> #include "suggest/core/dicnode/dic_node.h" -#include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dicnode/dic_node_proximity_filter.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/binary_dictionary_info.h" #include "suggest/core/dictionary/binary_format.h" #include "suggest/core/dictionary/multi_bigram_map.h" #include "suggest/core/dictionary/probability_utils.h" -#include "suggest/core/layout/proximity_info.h" -#include "suggest/core/layout/proximity_info_state.h" #include "suggest/core/policy/dictionary_structure_policy.h" #include "utils/char_utils.h" @@ -57,21 +56,20 @@ namespace latinime { /////////////////////////////////// /* static */ void DicNodeUtils::createAndGetPassingChildNode(DicNode *dicNode, - const ProximityInfoState *pInfoState, const int pointIndex, const bool exactOnly, + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes) { // Passing multiple chars node. No need to traverse child const int codePoint = dicNode->getNodeTypedCodePoint(); const int baseLowerCaseCodePoint = CharUtils::toBaseLowerCase(codePoint); - const bool isMatch = isMatchedNodeCodePoint(pInfoState, pointIndex, exactOnly, codePoint); - if (isMatch || CharUtils::isIntentionalOmissionCodePoint(baseLowerCaseCodePoint)) { + if (!childrenFilter->isFilteredOut(codePoint) + || CharUtils::isIntentionalOmissionCodePoint(baseLowerCaseCodePoint)) { childDicNodes->pushPassingChild(dicNode); } } /* static */ int DicNodeUtils::createAndGetLeavingChildNode(DicNode *dicNode, int pos, const BinaryDictionaryInfo *const binaryDictionaryInfo, - const ProximityInfoState *pInfoState, const int pointIndex, const bool exactOnly, - const std::vector<int> *const codePointsFilter, const ProximityInfo *const pInfo, + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes) { int nextPos = pos; const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer( @@ -80,6 +78,7 @@ namespace latinime { const bool isTerminal = (0 != (BinaryFormat::FLAG_IS_TERMINAL & flags)); const bool hasChildren = BinaryFormat::hasChildrenInFlags(flags); const bool hasShortcuts = (0 != (BinaryFormat::FLAG_HAS_SHORTCUT_TARGETS & flags)); + const bool isBlacklistedOrNotAWord = BinaryFormat::hasBlacklistedOrNotAWordFlag(flags); int codePoint = BinaryFormat::getCodePointAndForwardPointer( binaryDictionaryInfo->getDictRoot(), &pos); @@ -110,50 +109,18 @@ namespace latinime { const int siblingPos = BinaryFormat::skipChildrenPosAndAttributes( binaryDictionaryInfo->getDictRoot(), flags, pos); - if (isDicNodeFilteredOut(mergedNodeCodePoints[0], pInfo, codePointsFilter)) { - return siblingPos; - } - if (!isMatchedNodeCodePoint(pInfoState, pointIndex, exactOnly, mergedNodeCodePoints[0])) { + if (childrenFilter->isFilteredOut(mergedNodeCodePoints[0])) { return siblingPos; } - childDicNodes->pushLeavingChild(dicNode, nextPos, flags, childrenPos, attributesPos, - probability, isTerminal, hasChildren, mergedNodeCodePointCount, mergedNodeCodePoints); + childDicNodes->pushLeavingChild(dicNode, nextPos, childrenPos, attributesPos, + probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, + mergedNodeCodePointCount, mergedNodeCodePoints); return siblingPos; } -/* static */ bool DicNodeUtils::isDicNodeFilteredOut(const int nodeCodePoint, - const ProximityInfo *const pInfo, const std::vector<int> *const codePointsFilter) { - const int filterSize = codePointsFilter ? codePointsFilter->size() : 0; - if (filterSize <= 0) { - return false; - } - if (pInfo && (pInfo->getKeyIndexOf(nodeCodePoint) == NOT_AN_INDEX - || CharUtils::isIntentionalOmissionCodePoint(nodeCodePoint))) { - // If normalized nodeCodePoint is not on the keyboard or skippable, this child is never - // filtered. - return false; - } - const int lowerCodePoint = CharUtils::toLowerCase(nodeCodePoint); - const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint); - // TODO: Avoid linear search - for (int i = 0; i < filterSize; ++i) { - // Checking if a normalized code point is in filter characters when pInfo is not - // null. When pInfo is null, nodeCodePoint is used to check filtering without - // normalizing. - if ((pInfo && ((*codePointsFilter)[i] == lowerCodePoint - || (*codePointsFilter)[i] == baseLowerCodePoint)) - || (!pInfo && (*codePointsFilter)[i] == nodeCodePoint)) { - return false; - } - } - return true; -} - /* static */ void DicNodeUtils::createAndGetAllLeavingChildNodes(DicNode *dicNode, const BinaryDictionaryInfo *const binaryDictionaryInfo, - const ProximityInfoState *pInfoState, const int pointIndex, const bool exactOnly, - const std::vector<int> *const codePointsFilter, const ProximityInfo *const pInfo, - DicNodeVector *childDicNodes) { + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes) { if (!dicNode->hasChildren()) { return; } @@ -161,14 +128,8 @@ namespace latinime { const int childCount = BinaryFormat::getGroupCountAndForwardPointer( binaryDictionaryInfo->getDictRoot(), &nextPos); for (int i = 0; i < childCount; i++) { - const int filterSize = codePointsFilter ? codePointsFilter->size() : 0; nextPos = createAndGetLeavingChildNode(dicNode, nextPos, binaryDictionaryInfo, - pInfoState, pointIndex, exactOnly, codePointsFilter, pInfo, - childDicNodes); - if (!pInfo && filterSize > 0 && childDicNodes->exceeds(filterSize)) { - // All code points have been found. - break; - } + childrenFilter, childDicNodes); } } @@ -184,13 +145,12 @@ namespace latinime { if (dicNode->isTotalInputSizeExceedingLimit()) { return; } + const DicNodeProximityFilter childrenFilter(pInfoState, pointIndex, exactOnly); if (!dicNode->isLeavingNode()) { - DicNodeUtils::createAndGetPassingChildNode(dicNode, pInfoState, pointIndex, exactOnly, - childDicNodes); + DicNodeUtils::createAndGetPassingChildNode(dicNode, &childrenFilter, childDicNodes); } else { DicNodeUtils::createAndGetAllLeavingChildNodes( - dicNode, binaryDictionaryInfo, pInfoState, pointIndex, exactOnly, - 0 /* codePointsFilter */, 0 /* pInfo */, childDicNodes); + dicNode, binaryDictionaryInfo, &childrenFilter, childDicNodes); } } @@ -230,23 +190,6 @@ namespace latinime { return ProbabilityUtils::backoff(unigramProbability); } -/////////////////////////////////////// -// Bigram / Unigram dictionary utils // -/////////////////////////////////////// - -/* static */ bool DicNodeUtils::isMatchedNodeCodePoint(const ProximityInfoState *pInfoState, - const int pointIndex, const bool exactOnly, const int nodeCodePoint) { - if (!pInfoState) { - return true; - } - if (exactOnly) { - return pInfoState->getPrimaryCodePointAt(pointIndex) == nodeCodePoint; - } - const ProximityType matchedId = pInfoState->getProximityType(pointIndex, nodeCodePoint, - true /* checkProximityChars */); - return isProximityChar(matchedId); -} - //////////////// // Char utils // //////////////// diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h index d526975ce..7b567b582 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h @@ -18,7 +18,6 @@ #define LATINIME_DIC_NODE_UTILS_H #include <stdint.h> -#include <vector> #include "defines.h" @@ -26,8 +25,8 @@ namespace latinime { class BinaryDictionaryInfo; class DicNode; +class DicNodeProximityFilter; class DicNodeVector; -class ProximityInfo; class ProximityInfoState; class MultiBigramMap; @@ -44,19 +43,12 @@ class DicNodeUtils { const BinaryDictionaryInfo *const binaryDictionaryInfo, DicNodeVector *childDicNodes); static float getBigramNodeImprobability(const BinaryDictionaryInfo *const binaryDictionaryInfo, const DicNode *const node, MultiBigramMap *const multiBigramMap); - static bool isDicNodeFilteredOut(const int nodeCodePoint, const ProximityInfo *const pInfo, - const std::vector<int> *const codePointsFilter); // TODO: Move to private static void getProximityChildDicNodes(DicNode *dicNode, const BinaryDictionaryInfo *const binaryDictionaryInfo, const ProximityInfoState *pInfoState, const int pointIndex, bool exactOnly, DicNodeVector *childDicNodes); - // TODO: Move to proximity info - static bool isProximityChar(ProximityType type) { - return type == MATCH_CHAR || type == PROXIMITY_CHAR || type == ADDITIONAL_PROXIMITY_CHAR; - } - private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeUtils); // Max number of bigrams to look up @@ -64,22 +56,14 @@ class DicNodeUtils { static int getBigramNodeProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo, const DicNode *const node, MultiBigramMap *multiBigramMap); - static void createAndGetPassingChildNode(DicNode *dicNode, const ProximityInfoState *pInfoState, - const int pointIndex, const bool exactOnly, DicNodeVector *childDicNodes); + static void createAndGetPassingChildNode(DicNode *dicNode, + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes); static void createAndGetAllLeavingChildNodes(DicNode *dicNode, const BinaryDictionaryInfo *const binaryDictionaryInfo, - const ProximityInfoState *pInfoState, const int pointIndex, const bool exactOnly, - const std::vector<int> *const codePointsFilter, - const ProximityInfo *const pInfo, DicNodeVector *childDicNodes); + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes); static int createAndGetLeavingChildNode(DicNode *dicNode, int pos, const BinaryDictionaryInfo *const binaryDictionaryInfo, - const ProximityInfoState *pInfoState, const int pointIndex, - const bool exactOnly, const std::vector<int> *const codePointsFilter, - const ProximityInfo *const pInfo, DicNodeVector *childDicNodes); - - // TODO: Move to proximity info - static bool isMatchedNodeCodePoint(const ProximityInfoState *pInfoState, const int pointIndex, - const bool exactOnly, const int nodeCodePoint); + const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes); }; } // namespace latinime #endif // LATINIME_DIC_NODE_UTILS_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_vector.h b/native/jni/src/suggest/core/dicnode/dic_node_vector.h index 9641cc19c..5ac4eeaf4 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_vector.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_vector.h @@ -62,14 +62,15 @@ class DicNodeVector { mDicNodes.back().initAsPassingChild(dicNode); } - void pushLeavingChild(DicNode *dicNode, const int pos, const uint8_t flags, - const int childrenPos, const int attributesPos, const int probability, - const bool isTerminal, const bool hasChildren, const uint16_t mergedNodeCodePointCount, - const int *const mergedNodeCodePoints) { + void pushLeavingChild(DicNode *dicNode, const int pos, const int childrenPos, + const int attributesPos, const int probability, const bool isTerminal, + const bool hasChildren, const bool isBlacklistedOrNotAWord, + const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { ASSERT(!mLock); mDicNodes.push_back(mEmptyNode); - mDicNodes.back().initAsChild(dicNode, pos, flags, childrenPos, attributesPos, probability, - isTerminal, hasChildren, mergedNodeCodePointCount, mergedNodeCodePoints); + mDicNodes.back().initAsChild(dicNode, pos, childrenPos, attributesPos, probability, + isTerminal, hasChildren, isBlacklistedOrNotAWord, mergedNodeCodePointCount, + mergedNodeCodePoints); } DicNode *operator[](const int id) { diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp index bbb4ca3f0..5d14a0554 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp @@ -27,17 +27,13 @@ const int BinaryDictionaryFormatUtils::DICTIONARY_MINIMUM_SIZE = 4; /** * Format versions */ -// Originally, format version 1 had a 16-bit magic number, then the version number `01' -// then options that must be 0. Hence the first 32-bits of the format are always as follow -// and it's okay to consider them a magic number as a whole. -const uint32_t BinaryDictionaryFormatUtils::FORMAT_VERSION_1_MAGIC_NUMBER = 0x78B10100; // The versions of Latin IME that only handle format version 1 only test for the magic // number, so we had to change it so that version 2 files would be rejected by older // implementations. On this occasion, we made the magic number 32 bits long. -const uint32_t BinaryDictionaryFormatUtils::FORMAT_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; +const uint32_t BinaryDictionaryFormatUtils::HEADER_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; // Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12 -const int BinaryDictionaryFormatUtils::FORMAT_VERSION_2_MINIMUM_SIZE = 12; +const int BinaryDictionaryFormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12; /* static */ BinaryDictionaryFormatUtils::FORMAT_VERSION BinaryDictionaryFormatUtils::detectFormatVersion(const uint8_t *const dict, @@ -50,31 +46,28 @@ const int BinaryDictionaryFormatUtils::FORMAT_VERSION_2_MINIMUM_SIZE = 12; } const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0); switch (magicNumber) { - case FORMAT_VERSION_1_MAGIC_NUMBER: - // Format 1 header is exactly 5 bytes long and looks like: - // Magic number (2 bytes) 0x78 0xB1 - // Version number (1 byte) 0x01 - // Options (2 bytes) must be 0x00 0x00 - return VERSION_1; - case FORMAT_VERSION_2_MAGIC_NUMBER: - // Version 2 dictionaries are at least 12 bytes long. - // If this dictionary has the version 2 magic number but is less than 12 bytes long, - // then it's an unknown format and we need to avoid confidently reading the next bytes. - if (dictSize < FORMAT_VERSION_2_MINIMUM_SIZE) { + case HEADER_VERSION_2_MAGIC_NUMBER: + // Version 2 header are at least 12 bytes long. + // If this header has the version 2 magic number but is less than 12 bytes long, + // then it's an unknown format and we need to avoid confidently reading the next bytes. + if (dictSize < HEADER_VERSION_2_MINIMUM_SIZE) { + return UNKNOWN_VERSION; + } + // Version 2 header is as follows: + // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE + // Version number (2 bytes) + // Options (2 bytes) + // Header size (4 bytes) : integer, big endian + if (ByteArrayUtils::readUint16(dict, 4) == 2) { + return VERSION_2; + } else if (ByteArrayUtils::readUint16(dict, 4) == 3) { + // TODO: Support version 3 dictionary. + return UNKNOWN_VERSION; + } else { + return UNKNOWN_VERSION; + } + default: return UNKNOWN_VERSION; - } - // Format 2 header is as follows: - // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE - // Version number (2 bytes) 0x00 0x02 - // Options (2 bytes) - // Header size (4 bytes) : integer, big endian - if (ByteArrayUtils::readUint16(dict, 4) == 2) { - return VERSION_2; - } else { - return UNKNOWN_VERSION; - } - default: - return UNKNOWN_VERSION; } } diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h index 33618b9f0..830684c70 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h @@ -33,10 +33,9 @@ namespace latinime { */ class BinaryDictionaryFormatUtils { public: - // TODO: Remove obsolete version logic enum FORMAT_VERSION { - VERSION_1, VERSION_2, + VERSION_3, UNKNOWN_VERSION }; @@ -46,9 +45,8 @@ class BinaryDictionaryFormatUtils { DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryFormatUtils); static const int DICTIONARY_MINIMUM_SIZE; - static const uint32_t FORMAT_VERSION_1_MAGIC_NUMBER; - static const uint32_t FORMAT_VERSION_2_MAGIC_NUMBER; - static const int FORMAT_VERSION_2_MINIMUM_SIZE; + static const uint32_t HEADER_VERSION_2_MAGIC_NUMBER; + static const int HEADER_VERSION_2_MINIMUM_SIZE; }; } // namespace latinime #endif /* LATINIME_BINARY_DICTIONARY_FORMAT_UTILS_H */ diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h index 6dba0b266..240512bce 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h @@ -53,6 +53,20 @@ class BinaryDictionaryHeader { return mMultiWordCostMultiplier; } + AK_FORCE_INLINE void readHeaderValueOrQuestionMark(const char *const key, + int *outValue, int outValueSize) const { + if (outValueSize <= 0) return; + if (outValueSize == 1) { + outValue[0] = '\0'; + return; + } + if (!BinaryDictionaryHeaderReadingUtils::readHeaderValue(mBinaryDictionaryInfo, + key, outValue, outValueSize)) { + outValue[0] = '?'; + outValue[1] = '\0'; + } + } + private: DISALLOW_COPY_AND_ASSIGN(BinaryDictionaryHeader); diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp index 2c9593144..a57b0f859 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp @@ -26,12 +26,10 @@ namespace latinime { const int BinaryDictionaryHeaderReadingUtils::MAX_OPTION_KEY_LENGTH = 256; -const int BinaryDictionaryHeaderReadingUtils::FORMAT_VERSION_1_HEADER_SIZE = 5; - -const int BinaryDictionaryHeaderReadingUtils::VERSION_2_MAGIC_NUMBER_SIZE = 4; -const int BinaryDictionaryHeaderReadingUtils::VERSION_2_DICTIONARY_VERSION_SIZE = 2; -const int BinaryDictionaryHeaderReadingUtils::VERSION_2_DICTIONARY_FLAG_SIZE = 2; -const int BinaryDictionaryHeaderReadingUtils::VERSION_2_DICTIONARY_HEADER_SIZE_SIZE = 4; +const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_MAGIC_NUMBER_SIZE = 4; +const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_DICTIONARY_VERSION_SIZE = 2; +const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_FLAG_SIZE = 2; +const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_SIZE_FIELD_SIZE = 4; const BinaryDictionaryHeaderReadingUtils::DictionaryFlags BinaryDictionaryHeaderReadingUtils::NO_FLAGS = 0; @@ -47,15 +45,13 @@ const BinaryDictionaryHeaderReadingUtils::DictionaryFlags /* static */ int BinaryDictionaryHeaderReadingUtils::getHeaderSize( const BinaryDictionaryInfo *const binaryDictionaryInfo) { - switch (binaryDictionaryInfo->getFormat()) { - case BinaryDictionaryFormatUtils::VERSION_1: - return FORMAT_VERSION_1_HEADER_SIZE; - case BinaryDictionaryFormatUtils::VERSION_2: + switch (getHeaderVersion(binaryDictionaryInfo->getFormat())) { + case HEADER_VERSION_2: // See the format of the header in the comment in // BinaryDictionaryFormatUtils::detectFormatVersion() return ByteArrayUtils::readUint32(binaryDictionaryInfo->getDictBuf(), - VERSION_2_MAGIC_NUMBER_SIZE + VERSION_2_DICTIONARY_VERSION_SIZE - + VERSION_2_DICTIONARY_FLAG_SIZE); + VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE + + VERSION_2_HEADER_FLAG_SIZE); default: return S_INT_MAX; } @@ -64,12 +60,10 @@ const BinaryDictionaryHeaderReadingUtils::DictionaryFlags /* static */ BinaryDictionaryHeaderReadingUtils::DictionaryFlags BinaryDictionaryHeaderReadingUtils::getFlags( const BinaryDictionaryInfo *const binaryDictionaryInfo) { - switch (binaryDictionaryInfo->getFormat()) { - case BinaryDictionaryFormatUtils::VERSION_1: - return NO_FLAGS; - case BinaryDictionaryFormatUtils::VERSION_2: + switch (getHeaderVersion(binaryDictionaryInfo->getFormat())) { + case HEADER_VERSION_2: return ByteArrayUtils::readUint16(binaryDictionaryInfo->getDictBuf(), - VERSION_2_MAGIC_NUMBER_SIZE + VERSION_2_DICTIONARY_VERSION_SIZE); + VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE); default: return NO_FLAGS; } @@ -79,17 +73,23 @@ const BinaryDictionaryHeaderReadingUtils::DictionaryFlags /* static */ bool BinaryDictionaryHeaderReadingUtils::readHeaderValue( const BinaryDictionaryInfo *const binaryDictionaryInfo, const char *const key, int *outValue, const int outValueSize) { - if (outValueSize <= 0 || !hasHeaderAttributes(binaryDictionaryInfo->getFormat())) { + if (outValueSize <= 0) { return false; } const int headerSize = getHeaderSize(binaryDictionaryInfo); int pos = getHeaderOptionsPosition(binaryDictionaryInfo->getFormat()); + if (pos == NOT_A_DICT_POS) { + // The header doesn't have header options. + return false; + } while (pos < headerSize) { if(ByteArrayUtils::compareStringInBufferWithCharArray( binaryDictionaryInfo->getDictBuf(), key, headerSize - pos, &pos) == 0) { // The key was found. - ByteArrayUtils::readStringAndAdvancePosition( + const int length = ByteArrayUtils::readStringAndAdvancePosition( binaryDictionaryInfo->getDictBuf(), outValueSize, outValue, &pos); + // Add a 0 terminator to the string. + outValue[length < outValueSize ? length : outValueSize - 1] = '\0'; return true; } ByteArrayUtils::advancePositionToBehindString( diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h index 49ed2b9cc..61748227e 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h @@ -48,27 +48,15 @@ class BinaryDictionaryHeaderReadingUtils { return (flags & FRENCH_LIGATURE_PROCESSING_FLAG) != 0; } - static AK_FORCE_INLINE bool hasHeaderAttributes( - const BinaryDictionaryFormatUtils::FORMAT_VERSION format) { - // Only format 2 and above have header attributes as {key,value} string pairs. - switch (format) { - case BinaryDictionaryFormatUtils::VERSION_2: - return true; - break; - default: - return false; - } - } - static AK_FORCE_INLINE int getHeaderOptionsPosition( - const BinaryDictionaryFormatUtils::FORMAT_VERSION format) { - switch (format) { - case BinaryDictionaryFormatUtils::VERSION_2: - return VERSION_2_MAGIC_NUMBER_SIZE + VERSION_2_DICTIONARY_VERSION_SIZE - + VERSION_2_DICTIONARY_FLAG_SIZE + VERSION_2_DICTIONARY_HEADER_SIZE_SIZE; + const BinaryDictionaryFormatUtils::FORMAT_VERSION dictionaryFormat) { + switch (getHeaderVersion(dictionaryFormat)) { + case HEADER_VERSION_2: + return VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE + + VERSION_2_HEADER_FLAG_SIZE + VERSION_2_HEADER_SIZE_FIELD_SIZE; break; default: - return 0; + return NOT_A_DICT_POS; } } @@ -82,12 +70,15 @@ class BinaryDictionaryHeaderReadingUtils { private: DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryHeaderReadingUtils); - static const int FORMAT_VERSION_1_HEADER_SIZE; + enum HEADER_VERSION { + HEADER_VERSION_2, + UNKNOWN_HEADER_VERSION + }; - static const int VERSION_2_MAGIC_NUMBER_SIZE; - static const int VERSION_2_DICTIONARY_VERSION_SIZE; - static const int VERSION_2_DICTIONARY_FLAG_SIZE; - static const int VERSION_2_DICTIONARY_HEADER_SIZE_SIZE; + static const int VERSION_2_HEADER_MAGIC_NUMBER_SIZE; + static const int VERSION_2_HEADER_DICTIONARY_VERSION_SIZE; + static const int VERSION_2_HEADER_FLAG_SIZE; + static const int VERSION_2_HEADER_SIZE_FIELD_SIZE; static const DictionaryFlags NO_FLAGS; // Flags for special processing @@ -97,6 +88,18 @@ class BinaryDictionaryHeaderReadingUtils { static const DictionaryFlags SUPPORTS_DYNAMIC_UPDATE_FLAG; static const DictionaryFlags FRENCH_LIGATURE_PROCESSING_FLAG; static const DictionaryFlags CONTAINS_BIGRAMS_FLAG; + + static HEADER_VERSION getHeaderVersion( + const BinaryDictionaryFormatUtils::FORMAT_VERSION formatVersion) { + switch(formatVersion) { + case BinaryDictionaryFormatUtils::VERSION_2: + // Fall through + case BinaryDictionaryFormatUtils::VERSION_3: + return HEADER_VERSION_2; + default: + return UNKNOWN_HEADER_VERSION; + } + } }; } #endif /* LATINIME_DICTIONARY_HEADER_READING_UTILS_H */ diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h index 7cb31440a..cbea18f90 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h @@ -20,23 +20,27 @@ #include <stdint.h> #include "defines.h" +#include "jni.h" #include "suggest/core/dictionary/binary_dictionary_format_utils.h" #include "suggest/core/dictionary/binary_dictionary_header.h" #include "suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h" +#include "utils/log_utils.h" namespace latinime { class BinaryDictionaryInfo { public: - BinaryDictionaryInfo(const uint8_t *const dictBuf, const int dictSize, const int mmapFd, - const int dictBufOffset, const bool isUpdatable) + AK_FORCE_INLINE BinaryDictionaryInfo(JNIEnv *env, const uint8_t *const dictBuf, + const int dictSize, const int mmapFd, const int dictBufOffset, const bool isUpdatable) : mDictBuf(dictBuf), mDictSize(dictSize), mMmapFd(mmapFd), mDictBufOffset(dictBufOffset), mIsUpdatable(isUpdatable), mDictionaryFormat(BinaryDictionaryFormatUtils::detectFormatVersion( mDictBuf, mDictSize)), mDictionaryHeader(this), mDictRoot(mDictBuf + mDictionaryHeader.getSize()), mStructurePolicy(DictionaryStructurePolicyFactory::getDictionaryStructurePolicy( - mDictionaryFormat)) {} + mDictionaryFormat)) { + logDictionaryInfo(env); + } AK_FORCE_INLINE const uint8_t *getDictBuf() const { return mDictBuf; @@ -88,6 +92,33 @@ class BinaryDictionaryInfo { const BinaryDictionaryHeader mDictionaryHeader; const uint8_t *const mDictRoot; const DictionaryStructurePolicy *const mStructurePolicy; + + AK_FORCE_INLINE void logDictionaryInfo(JNIEnv *const env) const { + const int BUFFER_SIZE = 16; + int dictionaryIdCodePointBuffer[BUFFER_SIZE]; + int versionStringCodePointBuffer[BUFFER_SIZE]; + int dateStringCodePointBuffer[BUFFER_SIZE]; + mDictionaryHeader.readHeaderValueOrQuestionMark("dictionary", + dictionaryIdCodePointBuffer, BUFFER_SIZE); + mDictionaryHeader.readHeaderValueOrQuestionMark("version", + versionStringCodePointBuffer, BUFFER_SIZE); + mDictionaryHeader.readHeaderValueOrQuestionMark("date", + dateStringCodePointBuffer, BUFFER_SIZE); + + char dictionaryIdCharBuffer[BUFFER_SIZE]; + char versionStringCharBuffer[BUFFER_SIZE]; + char dateStringCharBuffer[BUFFER_SIZE]; + intArrayToCharArray(dictionaryIdCodePointBuffer, BUFFER_SIZE, + dictionaryIdCharBuffer, BUFFER_SIZE); + intArrayToCharArray(versionStringCodePointBuffer, BUFFER_SIZE, + versionStringCharBuffer, BUFFER_SIZE); + intArrayToCharArray(dateStringCodePointBuffer, BUFFER_SIZE, + dateStringCharBuffer, BUFFER_SIZE); + + LogUtils::logToJava(env, + "Dictionary info: dictionary = %s ; version = %s ; date = %s ; filesize = %i", + dictionaryIdCharBuffer, versionStringCharBuffer, dateStringCharBuffer, mDictSize); + } }; } #endif /* LATINIME_BINARY_DICTIONARY_INFO_H */ diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 675b54972..f520a75b1 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -22,6 +22,7 @@ #include <stdint.h> #include "defines.h" +#include "jni.h" #include "suggest/core/dictionary/bigram_dictionary.h" #include "suggest/core/dictionary/binary_format.h" #include "suggest/core/session/dic_traverse_session.h" @@ -32,8 +33,9 @@ namespace latinime { -Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset, bool isUpdatable) - : mBinaryDictionaryInfo(static_cast<const uint8_t *>(dict), dictSize, mmapFd, +Dictionary::Dictionary(JNIEnv *env, void *dict, int dictSize, int mmapFd, + int dictBufOffset, bool isUpdatable) + : mBinaryDictionaryInfo(env, static_cast<const uint8_t *>(dict), dictSize, mmapFd, dictBufOffset, isUpdatable), mBigramDictionary(new BigramDictionary(&mBinaryDictionaryInfo)), mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())), diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 94579c200..1bf24a85b 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -20,6 +20,7 @@ #include <stdint.h> #include "defines.h" +#include "jni.h" #include "suggest/core/dictionary/binary_dictionary_info.h" namespace latinime { @@ -52,7 +53,8 @@ class Dictionary { static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; static const int KIND_FLAG_EXACT_MATCH = 0x40000000; - Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset, bool isUpdatable); + Dictionary(JNIEnv *env, void *dict, int dictSize, int mmapFd, int dictBufOffset, + bool isUpdatable); int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, diff --git a/native/jni/src/suggest/core/dictionary/terminal_attributes.h b/native/jni/src/suggest/core/dictionary/terminal_attributes.h index cec47081e..0da6504eb 100644 --- a/native/jni/src/suggest/core/dictionary/terminal_attributes.h +++ b/native/jni/src/suggest/core/dictionary/terminal_attributes.h @@ -21,7 +21,6 @@ #include "suggest/core/dictionary/binary_dictionary_info.h" #include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h" -#include "suggest/core/dictionary/binary_format.h" namespace latinime { @@ -71,28 +70,23 @@ class TerminalAttributes { }; TerminalAttributes(const BinaryDictionaryInfo *const binaryDictionaryInfo, - const uint8_t nodeFlags, const int shortcutPos) - : mBinaryDictionaryInfo(binaryDictionaryInfo), - mNodeFlags(nodeFlags), mShortcutListSizePos(shortcutPos) {} + const int shortcutPos) + : mBinaryDictionaryInfo(binaryDictionaryInfo), mShortcutListSizePos(shortcutPos) {} inline ShortcutIterator getShortcutIterator() const { - // The size of the shortcuts is stored here so that the whole shortcut chunk can be - // skipped quickly, so we ignore it. int shortcutPos = mShortcutListSizePos; - BinaryDictionaryTerminalAttributesReadingUtils::getShortcutListSizeAndForwardPointer( - mBinaryDictionaryInfo, &shortcutPos); - const bool hasShortcutList = 0 != (mNodeFlags & BinaryFormat::FLAG_HAS_SHORTCUT_TARGETS); + const bool hasShortcutList = shortcutPos != NOT_A_DICT_POS; + if (hasShortcutList) { + BinaryDictionaryTerminalAttributesReadingUtils::getShortcutListSizeAndForwardPointer( + mBinaryDictionaryInfo, &shortcutPos); + } + // shortcutPos is never used if hasShortcutList is false. return ShortcutIterator(mBinaryDictionaryInfo, shortcutPos, hasShortcutList); } - bool isBlacklistedOrNotAWord() const { - return BinaryFormat::hasBlacklistedOrNotAWordFlag(mNodeFlags); - } - private: DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalAttributes); const BinaryDictionaryInfo *const mBinaryDictionaryInfo; - const uint8_t mNodeFlags; const int mShortcutListSizePos; }; } // namespace latinime diff --git a/native/jni/src/suggest/core/layout/proximity_info_utils.h b/native/jni/src/suggest/core/layout/proximity_info_utils.h index 54f7539d1..0e28560fc 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_utils.h @@ -117,6 +117,10 @@ class ProximityInfoUtils { return getSquaredDistanceFloat(x, y, projectionX, projectionY); } + static AK_FORCE_INLINE bool isMatchOrProximityChar(const ProximityType type) { + return type == MATCH_CHAR || type == PROXIMITY_CHAR || type == ADDITIONAL_PROXIMITY_CHAR; + } + // Normal distribution N(u, sigma^2). struct NormalDistribution { public: diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h index c6f66f231..f26d7149e 100644 --- a/native/jni/src/suggest/core/policy/traversal.h +++ b/native/jni/src/suggest/core/policy/traversal.h @@ -45,7 +45,7 @@ class Traversal { const DicNode *const dicNode) const = 0; virtual bool needsToTraverseAllUserInput() const = 0; virtual float getMaxSpatialDistance() const = 0; - virtual bool allowPartialCommit() const = 0; + virtual bool autoCorrectsToMultiWordSuggestionIfTop() const = 0; virtual int getDefaultExpandDicNodeSize() const = 0; virtual int getMaxCacheSize() const = 0; virtual bool isPossibleOmissionChildNode(const DicTraverseSession *const traverseSession, diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 173a612be..c6da6f003 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -36,7 +36,6 @@ namespace latinime { const int Suggest::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; const int Suggest::MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE = 2; const float Suggest::AUTOCORRECT_CLASSIFICATION_THRESHOLD = 0.33f; -const int Suggest::FINAL_SCORE_PENALTY_FOR_NOT_BEST_EXACT_MATCHED_WORD = 1; /** * Returns a set of suggestions for the given input touch points. The commitPoint argument indicates @@ -85,9 +84,9 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession, int commitPo if (!traverseSession->getProximityInfoState(0)->isUsed()) { return; } - if (TRAVERSAL->allowPartialCommit()) { - commitPoint = 0; - } + + // Never auto partial commit for now. + commitPoint = 0; if (traverseSession->getInputSize() > MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE && traverseSession->isContinuousSuggestionPossible()) { @@ -149,8 +148,17 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen &doubleLetterTerminalIndex, &doubleLetterLevel); int maxScore = S_INT_MIN; - int bestExactMatchedNodeTerminalIndex = -1; - int bestExactMatchedNodeOutputWordIndex = -1; + // Force autocorrection for obvious long multi-word suggestions when the top suggestion is + // a long multiple words suggestion. + // TODO: Implement a smarter auto-commit method for handling multi-word suggestions. + // traverseSession->isPartiallyCommited() always returns false because we never auto partial + // commit for now. + const bool forceCommitMultiWords = (terminalSize > 0) ? + TRAVERSAL->autoCorrectsToMultiWordSuggestionIfTop() + && (traverseSession->isPartiallyCommited() + || (traverseSession->getInputSize() + >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT + && terminals[0].hasMultipleWords())) : false; // Output suggestion results here for (int terminalIndex = 0; terminalIndex < terminalSize && outputWordIndex < MAX_RESULTS; ++terminalIndex) { @@ -162,8 +170,6 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel); const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight) + doubleLetterCost; - const TerminalAttributes terminalAttributes(traverseSession->getBinaryDictionaryInfo(), - terminalDicNode->getFlags(), terminalDicNode->getAttributesPos()); const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0; const bool isExactMatch = terminalDicNode->isExactMatch(); const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase(); @@ -176,60 +182,43 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen | (isSafeExactMatch ? Dictionary::KIND_FLAG_EXACT_MATCH : 0); // Entries that are blacklisted or do not represent a word should not be output. - const bool isValidWord = !terminalAttributes.isBlacklistedOrNotAWord(); + const bool isValidWord = !terminalDicNode->isBlacklistedOrNotAWord(); // Increase output score of top typing suggestion to ensure autocorrection. // TODO: Better integration with java side autocorrection logic. - // Force autocorrection for obvious long multi-word suggestions. - const bool isForceCommitMultiWords = TRAVERSAL->allowPartialCommit() - && (traverseSession->isPartiallyCommited() - || (traverseSession->getInputSize() >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT - && terminalDicNode->hasMultipleWords())); - const int finalScore = SCORING->calculateFinalScore( compoundDistance, traverseSession->getInputSize(), - isForceCommitMultiWords || (isValidWord && SCORING->doesAutoCorrectValidWord())); + terminalDicNode->isExactMatch() + || (forceCommitMultiWords && terminalDicNode->hasMultipleWords()) + || (isValidWord && SCORING->doesAutoCorrectValidWord())); maxScore = max(maxScore, finalScore); - if (TRAVERSAL->allowPartialCommit()) { - // Index for top typing suggestion should be 0. - if (isValidWord && outputWordIndex == 0) { - terminalDicNode->outputSpacePositionsResult(spaceIndices); - } + // TODO: Implement a smarter auto-commit method for handling multi-word suggestions. + // Index for top typing suggestion should be 0. + if (isValidWord && outputWordIndex == 0) { + terminalDicNode->outputSpacePositionsResult(spaceIndices); } // Don't output invalid words. However, we still need to submit their shortcuts if any. if (isValidWord) { outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags; frequencies[outputWordIndex] = finalScore; - if (isSafeExactMatch) { - // Demote exact matches that are not the highest probable node among all exact - // matches. - const bool isBestTerminal = bestExactMatchedNodeTerminalIndex < 0 - || terminals[bestExactMatchedNodeTerminalIndex].getProbability() - < terminalDicNode->getProbability(); - const int outputWordIndexToBeDemoted = isBestTerminal ? - bestExactMatchedNodeOutputWordIndex : outputWordIndex; - if (outputWordIndexToBeDemoted >= 0) { - frequencies[outputWordIndexToBeDemoted] -= - FINAL_SCORE_PENALTY_FOR_NOT_BEST_EXACT_MATCHED_WORD; - } - if (isBestTerminal) { - // Updates the best exact matched node index. - bestExactMatchedNodeTerminalIndex = terminalIndex; - // Updates the best exact matched output word index. - bestExactMatchedNodeOutputWordIndex = outputWordIndex; - } - } // Populate the outputChars array with the suggested word. const int startIndex = outputWordIndex * MAX_WORD_LENGTH; terminalDicNode->outputResult(&outputCodePoints[startIndex]); ++outputWordIndex; } - const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode); - outputWordIndex = ShortcutUtils::outputShortcuts(&terminalAttributes, outputWordIndex, - finalScore, outputCodePoints, frequencies, outputTypes, sameAsTyped); + if (!terminalDicNode->hasMultipleWords()) { + const TerminalAttributes terminalAttributes(traverseSession->getBinaryDictionaryInfo(), + terminalDicNode->getAttributesPos()); + // Shortcut is not supported for multiple words suggestions. + // TODO: Check shortcuts during traversal for multiple words suggestions. + const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode); + outputWordIndex = ShortcutUtils::outputShortcuts(&terminalAttributes, outputWordIndex, + finalScore, outputCodePoints, frequencies, outputTypes, sameAsTyped); + + } DicNode::managedDelete(terminalDicNode); } diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h index 752bde9ac..875cbe4e0 100644 --- a/native/jni/src/suggest/core/suggest.h +++ b/native/jni/src/suggest/core/suggest.h @@ -82,8 +82,6 @@ class Suggest : public SuggestInterface { // Threshold for autocorrection classifier static const float AUTOCORRECT_CLASSIFICATION_THRESHOLD; - // Final score penalty to exact match words that are not the most probable exact match. - static const int FINAL_SCORE_PENALTY_FOR_NOT_BEST_EXACT_MATCHED_WORD; const Traversal *const TRAVERSAL; const Scoring *const SCORING; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h index 5070651cb..70dad67e8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h @@ -30,10 +30,11 @@ class DictionaryStructurePolicyFactory { static const DictionaryStructurePolicy *getDictionaryStructurePolicy( const BinaryDictionaryFormatUtils::FORMAT_VERSION dictionaryFormat) { switch (dictionaryFormat) { - case BinaryDictionaryFormatUtils::VERSION_1: - // Fall through case BinaryDictionaryFormatUtils::VERSION_2: return PatriciaTriePolicy::getInstance(); + case BinaryDictionaryFormatUtils::VERSION_3: + // TODO: support version 3 dictionaries. + return 0; default: ASSERT(false); return 0; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index 5ae396e64..ef144e00a 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -23,6 +23,7 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/layout/proximity_info_state.h" +#include "suggest/core/layout/proximity_info_utils.h" #include "suggest/core/policy/traversal.h" #include "suggest/core/session/dic_traverse_session.h" #include "suggest/policyimpl/typing/scoring_params.h" @@ -136,7 +137,7 @@ class TypingTraversal : public Traversal { return ScoringParams::MAX_SPATIAL_DISTANCE; } - AK_FORCE_INLINE bool allowPartialCommit() const { + AK_FORCE_INLINE bool autoCorrectsToMultiWordSuggestionIfTop() const { return true; } @@ -159,7 +160,7 @@ class TypingTraversal : public Traversal { const DicNode *const dicNode) const { const ProximityType proximityType = getProximityType(traverseSession, parentDicNode, dicNode); - if (!DicNodeUtils::isProximityChar(proximityType)) { + if (!ProximityInfoUtils::isMatchOrProximityChar(proximityType)) { return false; } return true; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index e098f353e..830aa80de 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -169,12 +169,7 @@ class TypingWeighting : public Weighting { float getTerminalLanguageCost(const DicTraverseSession *const traverseSession, const DicNode *const dicNode, const float dicNodeLanguageImprobability) const { - // We promote exact matches here to prevent them from being pruned. The final score of - // exact match nodes might be demoted later in Suggest::outputSuggestions if there are - // multiple exact matches. - const float languageImprobability = (dicNode->isExactMatch()) ? - 0.0f : dicNodeLanguageImprobability; - return languageImprobability * ScoringParams::DISTANCE_WEIGHT_LANGUAGE; + return dicNodeLanguageImprobability * ScoringParams::DISTANCE_WEIGHT_LANGUAGE; } AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const { diff --git a/native/jni/src/utils/log_utils.cpp b/native/jni/src/utils/log_utils.cpp new file mode 100644 index 000000000..5ab2b2862 --- /dev/null +++ b/native/jni/src/utils/log_utils.cpp @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#include "log_utils.h" + +#include <cstdio> +#include <stdarg.h> + +#include "defines.h" + +namespace latinime { + /* static */ void LogUtils::logToJava(JNIEnv *const env, const char *const format, ...) { + static const char *TAG = "LatinIME:LogUtils"; + const jclass androidUtilLogClass = env->FindClass("android/util/Log"); + if (!androidUtilLogClass) { + // If we can't find the class, we are probably in off-device testing, and + // it's expected. Regardless, logging is not essential to functionality, so + // we should just return. However, FindClass has thrown an exception behind + // our back and there is no way to prevent it from doing that, so we clear + // the exception before we return. + env->ExceptionClear(); + return; + } + const jmethodID logDotIMethodId = env->GetStaticMethodID(androidUtilLogClass, "i", + "(Ljava/lang/String;Ljava/lang/String;)I"); + if (!logDotIMethodId) { + env->ExceptionClear(); + if (androidUtilLogClass) env->DeleteLocalRef(androidUtilLogClass); + return; + } + const jstring javaTag = env->NewStringUTF(TAG); + + static const int DEFAULT_LINE_SIZE = 128; + char fixedSizeCString[DEFAULT_LINE_SIZE]; + va_list argList; + va_start(argList, format); + // Get the necessary size. Add 1 for the 0 terminator. + const int size = vsnprintf(fixedSizeCString, DEFAULT_LINE_SIZE, format, argList) + 1; + va_end(argList); + + jstring javaString; + if (size <= DEFAULT_LINE_SIZE) { + // The buffer was large enough. + javaString = env->NewStringUTF(fixedSizeCString); + } else { + // The buffer was not large enough. + va_start(argList, format); + char variableSizeCString[size]; + vsnprintf(variableSizeCString, size, format, argList); + va_end(argList); + javaString = env->NewStringUTF(variableSizeCString); + } + + env->CallStaticIntMethod(androidUtilLogClass, logDotIMethodId, javaTag, javaString); + if (javaString) env->DeleteLocalRef(javaString); + if (javaTag) env->DeleteLocalRef(javaTag); + if (androidUtilLogClass) env->DeleteLocalRef(androidUtilLogClass); + } +} diff --git a/native/jni/src/utils/log_utils.h b/native/jni/src/utils/log_utils.h new file mode 100644 index 000000000..6ac16d91a --- /dev/null +++ b/native/jni/src/utils/log_utils.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef LATINIME_LOG_UTILS_H +#define LATINIME_LOG_UTILS_H + +#include "defines.h" +#include "jni.h" + +namespace latinime { + +class LogUtils { + public: + static void logToJava(JNIEnv *const env, const char *const format, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif // __GNUC__ + ; + + private: + DISALLOW_COPY_AND_ASSIGN(LogUtils); +}; +} // namespace latinime +#endif // LATINIME_LOG_UTILS_H diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java index b193e66dc..9ad81c01d 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java @@ -60,7 +60,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Chording input in shift locked. public void testChordingShiftLocked() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press shift key and hold, enter into choring shift state. @@ -119,7 +119,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -137,7 +137,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -196,7 +196,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -216,7 +216,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -397,29 +397,29 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { public void testLongPressShiftAndChording() { // Long press shift key, enter maybe shift locked. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet (not shift locked). releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Long press shift key, enter maybe alphabet. - longPressKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); + longPressShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); // Release shift key, back to shift locked (not alphabet). releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED); // Long press shift key, enter alphabet - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Long press shift key, enter maybe alphabet. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet shifted (not alphabet). @@ -430,7 +430,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in automatic shifted. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter maybe shift locked. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet (not shift locked). @@ -449,7 +449,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // releaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); // // // Long press shift key, enter alphabet shift locked. -// longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, +// longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, // ALPHABET_SHIFT_LOCKED); // // First shift key tap. // pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java index d5b9d1dfd..c7ac76d93 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java @@ -91,7 +91,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Switching between alphabet shift locked and symbols. public void testAlphabetShiftLockedAndSymbols() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. @@ -133,7 +133,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Automatic switch back to alphabet shift locked test by space key. public void testSwitchBackBySpaceShiftLocked() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. @@ -196,13 +196,13 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in alphabet. loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release letter key, remain in shift locked. pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); @@ -212,16 +212,16 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); @@ -231,7 +231,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in automatic shifted. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); @@ -293,12 +293,12 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { updateShiftState(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Update shift state, remained in alphabet shift locked. updateShiftState(ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release "?123" key, enter into symbols. @@ -326,12 +326,12 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Update shift state, remained in alphabet shift locked (not automatic shifted). updateShiftState(ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Load keyboard, should be in automatic shifted. @@ -383,7 +383,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> shift key + letter -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -441,7 +441,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> shift key + letter -> cancel -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -500,7 +500,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -517,7 +517,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -574,7 +574,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -592,7 +592,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -651,7 +651,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -670,7 +670,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -733,7 +733,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -753,7 +753,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -777,7 +777,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Change focus to new text field. loadKeyboard(ALPHABET_UNSHIFTED); @@ -808,7 +808,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Change focus to new text field. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); @@ -852,7 +852,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> rotate -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Rotate device, remain in alphabet shift locked. rotateDevice(ALPHABET_SHIFT_LOCKED); @@ -936,7 +936,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { secondPressAndReleaseKey('J', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key to enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java index 6991d05fa..3ffd0a96a 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java @@ -34,6 +34,11 @@ public class KeyboardStateTestsBase extends AndroidTestCase loadKeyboard(ALPHABET_UNSHIFTED); } + /** + * Set auto caps mode. + * + * @param autoCaps the auto cap mode. + */ public void setAutoCapsMode(final int autoCaps) { mSwitcher.setAutoCapsMode(autoCaps); } @@ -44,17 +49,32 @@ public class KeyboardStateTestsBase extends AndroidTestCase expected == actual); } + /** + * Emulate update keyboard shift state. + * + * @param afterUpdate the keyboard state after updating the keyboard shift state. + */ public void updateShiftState(final int afterUpdate) { mSwitcher.updateShiftState(); assertLayout("afterUpdate", afterUpdate, mSwitcher.getLayoutId()); } + /** + * Emulate load default keyboard. + * + * @param afterLoad the keyboard state after loading default keyboard. + */ public void loadKeyboard(final int afterLoad) { mSwitcher.loadKeyboard(); mSwitcher.updateShiftState(); assertLayout("afterLoad", afterLoad, mSwitcher.getLayoutId()); } + /** + * Emulate rotate device. + * + * @param afterRotate the keyboard state after rotating device. + */ public void rotateDevice(final int afterRotate) { mSwitcher.saveKeyboardState(); mSwitcher.loadKeyboard(); @@ -67,45 +87,97 @@ public class KeyboardStateTestsBase extends AndroidTestCase assertLayout("afterPress", afterPress, mSwitcher.getLayoutId()); } + /** + * Emulate key press. + * + * @param code the key code to press. + * @param afterPress the keyboard state after pressing the key. + */ public void pressKey(final int code, final int afterPress) { mSwitcher.expireDoubleTapTimeout(); pressKeyWithoutTimerExpire(code, true, afterPress); } + /** + * Emulate key release and register. + * + * @param code the key code to release and register + * @param afterRelease the keyboard state after releasing the key. + */ public void releaseKey(final int code, final int afterRelease) { mSwitcher.onCodeInput(code); mSwitcher.onReleaseKey(code, NOT_SLIDING); assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId()); } + /** + * Emulate key press and release. + * + * @param code the key code to press and release. + * @param afterPress the keyboard state after pressing the key. + * @param afterRelease the keyboard state after releasing the key. + */ public void pressAndReleaseKey(final int code, final int afterPress, final int afterRelease) { pressKey(code, afterPress); releaseKey(code, afterRelease); } + /** + * Emulate chording key press. + * + * @param code the chording key code. + * @param afterPress the keyboard state after pressing chording key. + */ public void chordingPressKey(final int code, final int afterPress) { mSwitcher.expireDoubleTapTimeout(); pressKeyWithoutTimerExpire(code, false, afterPress); } + /** + * Emulate chording key release. + * + * @param code the cording key code. + * @param afterRelease the keyboard state after releasing chording key. + */ public void chordingReleaseKey(final int code, final int afterRelease) { mSwitcher.onCodeInput(code); mSwitcher.onReleaseKey(code, NOT_SLIDING); assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId()); } + /** + * Emulate chording key press and release. + * + * @param code the chording key code. + * @param afterPress the keyboard state after pressing chording key. + * @param afterRelease the keyboard state after releasing chording key. + */ public void chordingPressAndReleaseKey(final int code, final int afterPress, final int afterRelease) { chordingPressKey(code, afterPress); chordingReleaseKey(code, afterRelease); } + /** + * Emulate start of the sliding key input. + * + * @param code the key code to start sliding. + * @param afterPress the keyboard state after pressing the key. + * @param afterSlide the keyboard state after releasing the key with sliding input. + */ public void pressAndSlideFromKey(final int code, final int afterPress, final int afterSlide) { pressKey(code, afterPress); mSwitcher.onReleaseKey(code, SLIDING); assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId()); } + /** + * Emulate end of the sliding key input. + * + * @param code the key code to stop sliding. + * @param afterPress the keyboard state after pressing the key. + * @param afterSlide the keyboard state after releasing the key and stop sliding. + */ public void stopSlidingOnKey(final int code, final int afterPress, final int afterSlide) { pressKey(code, afterPress); mSwitcher.onCodeInput(code); @@ -114,32 +186,67 @@ public class KeyboardStateTestsBase extends AndroidTestCase assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId()); } + /** + * Emulate cancel the sliding key input. + * + * @param afterCancelSliding the keyboard state after canceling sliding input. + */ public void stopSlidingAndCancel(final int afterCancelSliding) { mSwitcher.onFinishSlidingInput(); assertLayout("afterCancelSliding", afterCancelSliding, mSwitcher.getLayoutId()); } - public void longPressKey(final int code, final int afterPress, final int afterLongPress) { - pressKey(code, afterPress); + /** + * Emulate long press shift key. + * + * @param afterPress the keyboard state after pressing shift key. + * @param afterLongPress the keyboard state after long press fired. + */ + public void longPressShiftKey(final int afterPress, final int afterLongPress) { // Long press shift key will register {@link Constants#CODE_CAPS_LOCK}. See // {@link R.xml#key_styles_common} and its baseForShiftKeyStyle. We thus emulate the - // behavior here. - final int longPressCode = code == CODE_SHIFT ? Constants.CODE_CAPSLOCK : code; - mSwitcher.onCodeInput(longPressCode); + // behavior that is implemented in {@link MainKeyboardView#onLongPress(PointerTracker)}. + pressKey(Constants.CODE_SHIFT, afterPress); + mSwitcher.onPressKey(Constants.CODE_CAPSLOCK, true /* isSinglePointer */); + mSwitcher.onCodeInput(Constants.CODE_CAPSLOCK); assertLayout("afterLongPress", afterLongPress, mSwitcher.getLayoutId()); } - public void longPressAndReleaseKey(final int code, final int afterPress, - final int afterLongPress, final int afterRelease) { - longPressKey(code, afterPress, afterLongPress); - releaseKey(code, afterRelease); - } - - public void secondPressKey(int code, int afterPress) { + /** + * Emulate long press shift key and release. + * + * @param afterPress the keyboard state after pressing shift key. + * @param afterLongPress the keyboard state after long press fired. + * @param afterRelease the keyboard state after shift key is released. + */ + public void longPressAndReleaseShiftKey(final int afterPress, final int afterLongPress, + final int afterRelease) { + // Long press shift key will register {@link Constants#CODE_CAPS_LOCK}. See + // {@link R.xml#key_styles_common} and its baseForShiftKeyStyle. We thus emulate the + // behavior that is implemented in {@link MainKeyboardView#onLongPress(PointerTracker)}. + longPressShiftKey(afterPress, afterLongPress); + releaseKey(Constants.CODE_CAPSLOCK, afterRelease); + } + + /** + * Emulate the second press of the double tap. + * + * @param code the key code to double tap. + * @param afterPress the keyboard state after pressing the second tap. + */ + public void secondPressKey(final int code, final int afterPress) { pressKeyWithoutTimerExpire(code, true, afterPress); } - public void secondPressAndReleaseKey(int code, int afterPress, int afterRelease) { + /** + * Emulate the second tap of the double tap. + * + * @param code the key code to double tap. + * @param afterPress the keyboard state after pressing the second tap. + * @param afterRelease the keyboard state after releasing the second tap. + */ + public void secondPressAndReleaseKey(final int code, final int afterPress, + final int afterRelease) { secondPressKey(code, afterPress); releaseKey(code, afterRelease); } diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index 0e077bbbe..c0dd9933c 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.utils.TextRange; + import android.inputmethodservice.InputMethodService; import android.os.Parcel; import android.test.AndroidTestCase; @@ -30,8 +32,6 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; -import com.android.inputmethod.latin.RichInputConnection.Range; - import java.util.Locale; @SmallTest @@ -169,7 +169,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et)); et.startOffset = 0; et.selectionStart = 7; - Range r; + TextRange r; ic.beginBatchEdit(); // basic case @@ -241,7 +241,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 10 /* start */, 16 /* end */, 0 /* flags */); mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); - Range r; + TextRange r; SuggestionSpan[] suggestions; r = ic.getWordRangeAtCursor(" ", 0); diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java new file mode 100644 index 000000000..1434c6b63 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -0,0 +1,93 @@ +/* + * 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 tests for WordComposer. + */ +@SmallTest +public class WordComposerTests extends AndroidTestCase { + public void testMoveCursor() { + final WordComposer wc = new WordComposer(); + final String STR_WITHIN_BMP = "abcdef"; + wc.setComposingWord(STR_WITHIN_BMP, null); + assertEquals(wc.size(), + STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length())); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + wc.setCursorPositionWithinWord(2); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor to after the 'd' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(2)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor to after the 'e' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertEquals(wc.size(), 6); + // Move the cursor to after the 'f' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor past the end of the word + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15)); + + // \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())); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(6)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3)); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(2); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java index cca81a0e0..55f163255 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java @@ -51,7 +51,8 @@ import java.util.Set; @LargeTest public class BinaryDictIOTests extends AndroidTestCase { private static final String TAG = BinaryDictIOTests.class.getSimpleName(); - private static final int MAX_UNIGRAMS = 100; + private static final int DEFAULT_MAX_UNIGRAMS = 100; + private static final int DEFAULT_CODE_POINT_SET_SIZE = 50; private static final int UNIGRAM_FREQ = 10; private static final int BIGRAM_FREQ = 50; private static final int TOLERANCE_OF_BIGRAM_FREQ = 5; @@ -73,13 +74,16 @@ public class BinaryDictIOTests extends AndroidTestCase { new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */); public BinaryDictIOTests() { - super(); + this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); + } - final long time = System.currentTimeMillis(); - Log.e(TAG, "Testing dictionary: seed is " + time); - final Random random = new Random(time); + public BinaryDictIOTests(final long seed, final int maxUnigrams) { + super(); + Log.e(TAG, "Testing dictionary: seed is " + seed); + final Random random = new Random(seed); sWords.clear(); - generateWords(MAX_UNIGRAMS, random); + final int[] codePointSet = generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random); + generateWords(maxUnigrams, random, codePointSet); for (int i = 0; i < sWords.size(); ++i) { sChainBigrams.put(i, new ArrayList<Integer>()); @@ -94,6 +98,23 @@ public class BinaryDictIOTests extends AndroidTestCase { } } + private int[] generateCodePointSet(final int codePointSetSize, final Random random) { + final int[] codePointSet = new int[codePointSetSize]; + for (int i = codePointSet.length - 1; i >= 0; ) { + final int r = Math.abs(random.nextInt()); + if (r < 0) continue; + // Don't insert 0~0x20, but insert any other code point. + // Code points are in the range 0~0x10FFFF. + final int candidateCodePoint = (int)(0x20 + r % (Character.MAX_CODE_POINT - 0x20)); + // Code points between MIN_ and MAX_SURROGATE are not valid on their own. + if (candidateCodePoint >= Character.MIN_SURROGATE + && candidateCodePoint <= Character.MAX_SURROGATE) continue; + codePointSet[i] = candidateCodePoint; + --i; + } + return codePointSet; + } + // Utilities for test /** @@ -129,28 +150,29 @@ public class BinaryDictIOTests extends AndroidTestCase { /** * Generates a random word. */ - private String generateWord(final Random random) { - StringBuilder builder = new StringBuilder("a"); - int count = random.nextInt() % 30; // Arbitrarily 30 chars max - while (count > 0) { - final long r = Math.abs(random.nextInt()); - if (r < 0) continue; - // Don't insert 0~0x20, but insert any other code point. - // Code points are in the range 0~0x10FFFF. - final int candidateCodePoint = (int)(0x20 + r % (Character.MAX_CODE_POINT - 0x20)); - // Code points between MIN_ and MAX_SURROGATE are not valid on their own. - if (candidateCodePoint >= Character.MIN_SURROGATE - && candidateCodePoint <= Character.MAX_SURROGATE) continue; - builder.appendCodePoint(candidateCodePoint); - --count; + private String generateWord(final Random random, final int[] codePointSet) { + StringBuilder builder = new StringBuilder(); + // 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward + // longer words. This should be closer to natural language, and more importantly, it will + // exercise the algorithms in dicttool much more. + final int count = 1 + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5); + while (builder.length() < count) { + builder.appendCodePoint(codePointSet[Math.abs(random.nextInt()) % codePointSet.length]); } return builder.toString(); } - private void generateWords(final int number, final Random random) { + private void generateWords(final int number, final Random random, final int[] codePointSet) { final Set<String> wordSet = CollectionUtils.newHashSet(); while (wordSet.size() < number) { - wordSet.add(generateWord(random)); + wordSet.add(generateWord(random, codePointSet)); } sWords.addAll(wordSet); } @@ -558,8 +580,9 @@ public class BinaryDictIOTests extends AndroidTestCase { // Test a word that isn't contained within the dictionary. final Random random = new Random((int)System.currentTimeMillis()); + final int[] codePointSet = generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random); for (int i = 0; i < 1000; ++i) { - final String word = generateWord(random); + final String word = generateWord(random, codePointSet); if (sWords.indexOf(word) != -1) continue; runGetTerminalPosition(buffer, word, i, false); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java index d33142ce0..9331da44b 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java @@ -44,9 +44,10 @@ 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 int MAX_UNIGRAMS = 1500; private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); + public static final int DEFAULT_MAX_UNIGRAMS = 1500; + private final int mMaxUnigrams; private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", @@ -57,15 +58,17 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { }; public BinaryDictIOUtilsTests() { - this(System.currentTimeMillis()); + // 1500 is the default max unigrams + this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); } - public BinaryDictIOUtilsTests(final long seed) { + public BinaryDictIOUtilsTests(final long seed, final int maxUnigrams) { super(); - Log.d(TAG, "Seed for test is " + seed); + 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 < MAX_UNIGRAMS; ++i) { + for (int i = 0; i < maxUnigrams; ++i) { sWords.add(generateWord(random.nextInt())); } } @@ -395,6 +398,6 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms."); Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms."); - Log.d(TAG, "avg = " + ((double)sum/MAX_UNIGRAMS/1000000) + " ms."); + Log.d(TAG, "avg = " + ((double)sum/mMaxUnigrams/1000000) + " ms."); } } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java index 7b311c3ec..cacee5268 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java @@ -72,15 +72,21 @@ public class Dicttool { return command; } - private void execute(final String[] arguments) { + /** + * Executes the specified command with the specified arguments. + * @param arguments the arguments passed to dicttool. + * @return 0 for success, an error code otherwise (always 1 at the moment) + */ + private int execute(final String[] arguments) { final Command command = getCommand(arguments); try { command.run(); + return 0; } catch (Exception e) { System.out.println("Exception while processing command " + command.getClass().getSimpleName() + " : " + e); e.printStackTrace(); - return; + return 1; } } @@ -89,6 +95,7 @@ public class Dicttool { help(); return; } - new Dicttool().execute(arguments); + // Exit with the success/error code from #execute() as status. + System.exit(new Dicttool().execute(arguments)); } } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java index 827c5e3a9..972b6e7e6 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java @@ -16,36 +16,93 @@ package com.android.inputmethod.latin.dicttool; +import com.android.inputmethod.latin.makedict.BinaryDictIOTests; import com.android.inputmethod.latin.makedict.BinaryDictIOUtilsTests; import com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest; import com.android.inputmethod.latin.makedict.FusionDictionaryTest; -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; -import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; /** * Dicttool command implementing self-tests. */ public class Test extends Dicttool.Command { public static final String COMMAND = "test"; + private long mSeed = System.currentTimeMillis(); + private int mMaxUnigrams = BinaryDictIOUtilsTests.DEFAULT_MAX_UNIGRAMS; + + private static final Class<?>[] sClassesToTest = { + BinaryDictOffdeviceUtilsTests.class, + FusionDictionaryTest.class, + BinaryDictInputOutputTest.class, + BinaryDictIOUtilsTests.class, + BinaryDictIOTests.class + }; + private ArrayList<Method> mAllTestMethods = new ArrayList<Method>(); + private ArrayList<String> mUsedTestMethods = new ArrayList<String>(); public Test() { + for (final Class<?> c : sClassesToTest) { + for (final Method m : c.getDeclaredMethods()) { + if (m.getName().startsWith("test") && Void.TYPE == m.getReturnType() + && 0 == m.getParameterTypes().length) { + mAllTestMethods.add(m); + } + } + } } @Override public String getHelp() { - return "test"; + final StringBuilder s = new StringBuilder("test [-s seed] [-m maxUnigrams] [testName...]\n" + + "If seed is not specified, the current time is used.\nTest list is:\n"); + for (final Method m : mAllTestMethods) { + s.append(" "); + s.append(m.getName()); + s.append("\n"); + } + return s.toString(); } @Override - public void run() throws IOException, UnsupportedFormatException { - test(); + public void run() throws IllegalAccessException, InstantiationException, + InvocationTargetException { + int i = 0; + while (i < mArgs.length) { + final String arg = mArgs[i++]; + if ("-s".equals(arg)) { + mSeed = Long.parseLong(mArgs[i++]); + } else if ("-m".equals(arg)) { + mMaxUnigrams = Integer.parseInt(mArgs[i++]); + } else { + mUsedTestMethods.add(arg); + } + } + runChosenTests(); } - private void test() throws IOException, UnsupportedFormatException { - new BinaryDictOffdeviceUtilsTests().testGetRawDictWorks(); - new FusionDictionaryTest().testFusion(); - new BinaryDictInputOutputTest().testFlattenNodes(); - new BinaryDictIOUtilsTests().testRandomWords(); + private void runChosenTests() throws IllegalAccessException, InstantiationException, + InvocationTargetException { + for (final Method m : mAllTestMethods) { + final Class<?> declaringClass = m.getDeclaringClass(); + if (!mUsedTestMethods.isEmpty() && !mUsedTestMethods.contains(m.getName())) continue; + // Some of the test classes expose a two-argument constructor, taking a long as a + // seed for Random, and an int for a vocabulary size to test the dictionary with. They + // correspond respectively to the -s and -m numerical arguments to this command, which + // are stored in mSeed and mMaxUnigrams. If the two-arguments constructor is present, + // then invoke it; otherwise, invoke the default constructor. + Constructor<?> twoArgsConstructor = null; + try { + twoArgsConstructor = declaringClass.getDeclaredConstructor(Long.TYPE, Integer.TYPE); + } catch (NoSuchMethodException e) { + // No constructor with two args + } + final Object instance = null == twoArgsConstructor ? declaringClass.newInstance() + : twoArgsConstructor.newInstance(mSeed, mMaxUnigrams); + m.invoke(instance); + } } } diff --git a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl index 2fc97b5c6..479a766fb 100644 --- a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl +++ b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl @@ -20,7 +20,7 @@ import android.content.Context; import android.content.res.Resources; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.HashMap; diff --git a/tools/maketext/res/values-az/donottranslate-more-keys.xml b/tools/maketext/res/values-az/donottranslate-more-keys.xml new file mode 100644 index 000000000..db1784c17 --- /dev/null +++ b/tools/maketext/res/values-az/donottranslate-more-keys.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX --> + <string name="more_keys_for_a">â</string> + <!-- U+0259: "ə" LATIN SMALL LETTER SCHWA --> + <string name="more_keys_for_e">ə</string> + <!-- 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 --> + <string name="more_keys_for_i">ı,î,ï,ì,í,į,ī</string> + <!-- 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 --> + <string name="more_keys_for_o">ö,ô,œ,ò,ó,õ,ø,ō</string> + <!-- 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 --> + <string name="more_keys_for_u">ü,û,ù,ú,ū</string> + <!-- 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 --> + <string name="more_keys_for_s">ş,ß,ś,š</string> + <!-- U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE --> + <string name="more_keys_for_g">ğ</string> + <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE + U+010D: "č" LATIN SMALL LETTER C WITH CARON --> + <string name="more_keys_for_c">ç,ć,č</string> +</resources> diff --git a/tools/maketext/res/values-kk/donottranslate-more-keys.xml b/tools/maketext/res/values-kk/donottranslate-more-keys.xml new file mode 100644 index 000000000..0e953ff21 --- /dev/null +++ b/tools/maketext/res/values-kk/donottranslate-more-keys.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> + <string name="keylabel_for_east_slavic_row1_9">щ</string> + <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> + <string name="keylabel_for_east_slavic_row1_12">ъ</string> + <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> + <string name="keylabel_for_east_slavic_row2_1">ы</string> + <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> + <string name="keylabel_for_east_slavic_row2_11">э</string> + <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> + <string name="keylabel_for_east_slavic_row3_5">и</string> + <!-- U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U + U+04B1: "ұ" CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE --> + <string name="more_keys_for_cyrillic_u">ү,ұ</string> + <!-- U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER --> + <string name="more_keys_for_cyrillic_ka">қ</string> + <!-- U+0451: "ё" CYRILLIC SMALL LETTER IO --> + <string name="more_keys_for_cyrillic_ie">ё</string> + <!-- U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER --> + <string name="more_keys_for_cyrillic_en">ң</string> + <!-- U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE --> + <string name="more_keys_for_cyrillic_ghe">ғ</string> + <!-- U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I --> + <string name="more_keys_for_east_slavic_row2_1">і</string> + <!-- U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA --> + <string name="more_keys_for_cyrillic_a">ә</string> + <!-- U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O --> + <string name="more_keys_for_cyrillic_o">ө</string> + <!-- U+04BB: "һ" CYRILLIC SMALL LETTER SHHA --> + <string name="more_keys_for_east_slavic_row2_11">һ</string> + <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> + <string name="more_keys_for_cyrillic_soft_sign">ъ</string> + <!-- Label for "switch to alphabetic" key. + U+0410: "А" CYRILLIC CAPITAL LETTER A + U+0411: "Б" CYRILLIC CAPITAL LETTER BE + U+0412: "В" CYRILLIC CAPITAL LETTER VE --> + <string name="label_to_alpha_key">АБВ</string> +</resources> diff --git a/tools/maketext/res/values/donottranslate-more-keys.xml b/tools/maketext/res/values/donottranslate-more-keys.xml index c2b7513f3..b53a36917 100644 --- a/tools/maketext/res/values/donottranslate-more-keys.xml +++ b/tools/maketext/res/values/donottranslate-more-keys.xml @@ -49,11 +49,14 @@ <string name="keylabel_for_east_slavic_row2_11"></string> <string name="keylabel_for_east_slavic_row3_5"></string> <string name="more_keys_for_cyrillic_u"></string> + <string name="more_keys_for_cyrillic_ka"></string> <string name="more_keys_for_cyrillic_en"></string> <string name="more_keys_for_cyrillic_ghe"></string> <string name="more_keys_for_east_slavic_row2_1"></string> + <string name="more_keys_for_cyrillic_a"></string> <string name="more_keys_for_cyrillic_o"></string> <string name="more_keys_for_cyrillic_soft_sign"></string> + <string name="more_keys_for_east_slavic_row2_11"></string> <string name="keylabel_for_south_slavic_row1_6"></string> <string name="keylabel_for_south_slavic_row2_11"></string> <string name="keylabel_for_south_slavic_row3_1"></string> |