diff options
Diffstat (limited to 'java')
102 files changed, 2767 insertions, 1374 deletions
diff --git a/java/Android.mk b/java/Android.mk index 52cc18b26..364973bea 100644 --- a/java/Android.mk +++ b/java/Android.mk @@ -28,9 +28,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime # We want to install libjni_latinime.so to the system partition if LatinIME gets installed. LOCAL_REQUIRED_MODULES := libjni_latinime -LOCAL_STATIC_JAVA_LIBRARIES := android-common -LOCAL_STATIC_JAVA_LIBRARIES += inputmethod-common -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4 # Do not compress dictionary files to mmap dict data runtime LOCAL_AAPT_FLAGS := -0 .dict diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 6b823829f..3e80de22a 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -18,6 +18,8 @@ coreApp="true" package="com.android.inputmethod.latin"> + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" /> + <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_USER_DICTIONARY" /> @@ -26,7 +28,8 @@ <application android:label="@string/aosp_android_keyboard_ime_name" android:icon="@drawable/ic_ime_settings" android:backupAgent="BackupAgent" - android:killAfterRestore="false"> + android:killAfterRestore="false" + android:supportsRtl="true"> <service android:name="LatinIME" android:label="@string/aosp_android_keyboard_ime_name" diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 0a5d048ef..be692bfdf 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Gebaseer op vorige woord"</string> <string name="gesture_input" msgid="3310827802759290774">"Gebaarinvoer"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Voer \'n woord in deur die letters van \'n woord te trek"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Wys gebaarspoor"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Wys gebaar se woord"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Wys swewende voorskouwoord saam met die gebaar"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gestoor"</string> <string name="label_go_key" msgid="1635148082137219148">"Gaan"</string> <string name="label_next_key" msgid="362972844525672568">"Volgende"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Soek"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Verander taal"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift geaktiveer"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kasslot geaktiveer"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift gedeaktiveer"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index ab3b71c1c..f4a9de617 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"በቀዳሚው ቃል ላይ የተመሠረተ"</string> <string name="gesture_input" msgid="3310827802759290774">"የእጅ ምልክት ግብዓት"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"የአንድ ቃል ፊደሎችን በመከታተል አንድ ቃል አስገባ"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"ምልክት የሚሄድበት መንገድ አሳይ"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"የምልክት ቃል አሳይ"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"ተንሳፋፊ የቅድመ እይታ ቃል ከምልክት ጋር አሳይ"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ተቀምጧል"</string> <string name="label_go_key" msgid="1635148082137219148">"ሂድ"</string> <string name="label_next_key" msgid="362972844525672568">"በመቀጠል"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"ተመለስ"</string> <string name="spoken_description_search" msgid="1247236163755920808">"ፍለጋ"</string> <string name="spoken_description_dot" msgid="40711082435231673">"ነጥብ"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"ቋንቋ ቀይር"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"ቀጣይ"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"ቀዳሚ"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"ቅያር ቁልፍ ነቅቷል"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"አቢያት ማድረጊያ ነቅቷል"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ቅያር ተሰናክሏል"</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 8407f923e..29d046e76 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"استنادًا إلى الكلمة السابقة"</string> <string name="gesture_input" msgid="3310827802759290774">"إدخال الإيماءة"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"يمكنك إدخال كلمة من خلال تتبع أحرف كلمة ما"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"عرض مسار الإيماءة"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"عرض كلمة الإيماءة"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"عرض كلمة معاينة متحركة مع الإيماءة"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : تم الحفظ"</string> <string name="label_go_key" msgid="1635148082137219148">"تنفيذ"</string> <string name="label_next_key" msgid="362972844525672568">"التالي"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"رجوع"</string> <string name="spoken_description_search" msgid="1247236163755920808">"بحث"</string> <string name="spoken_description_dot" msgid="40711082435231673">"نقطة"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"تبديل اللغة"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"التالي"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"السابق"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"تم تمكين Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"تم تمكين Caps lock"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"تم تعطيل Shift"</string> diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml index 9301750e3..30217fb32 100644 --- a/java/res/values-be/strings.xml +++ b/java/res/values-be/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На аснове папярэдняга слова"</string> <string name="gesture_input" msgid="3310827802759290774">"Уваход жэстам"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Увядзiце слова, адсочваючы лiтары"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Паказаць след жэста"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Паказаць слова жэста"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Паказаць плаваючы прагляд слова з жэстам"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Захаваныя"</string> <string name="label_go_key" msgid="1635148082137219148">"Пачаць"</string> <string name="label_next_key" msgid="362972844525672568">"Далей"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Увод"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Кропка"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пераключыць мову"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Далей"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift уключаны"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock уключаны"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift адключаны"</string> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 6061e3c52..dc25205ce 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Въз основа на предишната дума"</string> <string name="gesture_input" msgid="3310827802759290774">"Въвеждане чрез жест"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Въвеждане на дума чрез проследяване на буквите й"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Следа на жестовете: Показване"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Показване на дума при жестове"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показване на дума с плаваща визуализация при жест"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Запазено"</string> <string name="label_go_key" msgid="1635148082137219148">"Старт"</string> <string name="label_next_key" msgid="362972844525672568">"Напред"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Търсене"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Смяна на езика"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Следващ"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Предишен"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"„Shift“ е активиран"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"„Caps Lock“ е активиран"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"„Shift“ е деактивиран"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index 865a391a0..57b55d310 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"En funció de la paraula anterior"</string> <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Dibuixa les lletres d\'una paraula per escriure-la"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra el recorregut del gest"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostra paraules en fer gestos"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostra la paraula de visualització prèvia flotant en fer gestos"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: desada"</string> <string name="label_go_key" msgid="1635148082137219148">"Vés"</string> <string name="label_next_key" msgid="362972844525672568">"Següent"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Retorn"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Canvia l\'idioma"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Següent"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maj activat"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloq Maj activat"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maj desactivat"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 1d9c8e38c..930cf13db 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základě předchozího slova"</string> <string name="gesture_input" msgid="3310827802759290774">"Zadávání gesty"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Napište slovo zadáním jeho písmen."</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovat stopu gesta"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Zobrazovat slovo gesta"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zobrazovat plovoucí náhled slova gesta"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Uloženo"</string> <string name="label_go_key" msgid="1635148082137219148">"Přejít"</string> <string name="label_next_key" msgid="362972844525672568">"Další"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"vyhledávací tlačítko"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Tečka"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Přepnout jazyk"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Další"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Předchozí"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Klávesa Shift je aktivní"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Klávesa Caps Lock je aktivní"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Klávesa Shift je neaktivní"</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index ca0938740..f04c43d16 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baseret på tidligere ord"</string> <string name="gesture_input" msgid="3310827802759290774">"Berøringsindtastning"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Indtast et ord ved at skrive bogstaverne for et ord med fingeren"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis spor af berøring"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Vis berøringsord"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vis flydende eksempelord under berøring"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string> <string name="label_go_key" msgid="1635148082137219148">"Gå"</string> <string name="label_next_key" msgid="362972844525672568">"Næste"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Tilbage"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Søg"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punktum"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Skift sprog"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Næste"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift er aktiveret"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock er aktiveret"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift er deaktiveret"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 437fc80f4..59d8e7357 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Auf Grundlage des vorherigen Wortes"</string> <string name="gesture_input" msgid="3310827802759290774">"Bewegungseingabe"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Wort durch Nachzeichnen der Buchstaben eingeben"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Spur der Bewegung anzeigen"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Wort bei Bewegung anzeigen"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vorgeschlagenes Wort bei Bewegung anzeigen"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string> <string name="label_go_key" msgid="1635148082137219148">"Los"</string> <string name="label_next_key" msgid="362972844525672568">"Weiter"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Eingabe"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Suchen"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Aufzählungspunkt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Sprache wechseln"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Nächste"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorherige"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Umschalttaste aktiviert"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Feststelltaste aktiviert"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Umschalttaste deaktiviert"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 4ecde7b7c..2803ed258 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Βάσει προηγούμενης λέξης"</string> <string name="gesture_input" msgid="3310827802759290774">"Καταχώριση κίνησης"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Καταχώριση μιας λέξης με εντοπισμό των γραμμάτων μιας λέξης"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Εμφάνιση διαδρομής χειρονομίας"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Εμφάνιση λέξης χειρονομίας"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Εμφάνιση κινούμενης προεπισκόπησης λέξης με χειρονομία"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string> <string name="label_go_key" msgid="1635148082137219148">"Μετ."</string> <string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Πλήκτρο Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Αναζήτηση"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Κουκκίδα"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Αλλαγή γλώσσας"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Επόμενο"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Προηγούμενο"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Το Shift ενεργοποιημένο"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Το Caps lock είναι ενεργοποιημένο"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Το Shift είναι απενεργοποιημένο"</string> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index e505f46d2..4d960d7d6 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Based on previous word"</string> <string name="gesture_input" msgid="3310827802759290774">"Gesture input"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Input a word by tracing the letters of a word"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Show gesture trail"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Show gesture word"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Show floating preview word with gesture"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string> <string name="label_go_key" msgid="1635148082137219148">"Go"</string> <string name="label_next_key" msgid="362972844525672568">"Next"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Search"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Switch language"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Next"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Previous"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift enabled"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock enabled"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift disabled"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 4187b87b0..42efb2001 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string> <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Ingresa una palabra trazando sus letras."</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido de gesto"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palabra de gesto"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palabra de vista previa flotante al realizar gestos"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Siguiente"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Volver"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Se activó el modo Mayúscula."</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Se activó el bloqueo de mayúsculas."</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Se desactivó el modo Mayúscula"</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 9de4fcf57..802864547 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string> <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Trazar las letras de una palabra para introducirla"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido del gesto"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palabra del gesto"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar la palabra de vista previa flotante al realizar un gesto"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Sig."</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Tecla Intro"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Mayúsculas habilitadas"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloqueo de mayúsculas habilitado"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Mayúsculas inhabilitadas"</string> diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml index cedbde055..152c0300b 100644 --- a/java/res/values-et/strings.xml +++ b/java/res/values-et/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Eelmise sõna põhjal"</string> <string name="gesture_input" msgid="3310827802759290774">"Liigutusega sisest."</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Sisestage sõna, kirjutades sõna tähed sõrmega"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Näita liigutuse jälge"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Näita liigutuse sõna"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Ujuva sõna eelvaate näitamine koos liigutusega"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : salvestatud"</string> <string name="label_go_key" msgid="1635148082137219148">"Mine"</string> <string name="label_next_key" msgid="362972844525672568">"Edasi"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Tagasi"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Otsing"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keele vahetamine"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Järgmine"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Eelmine"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tõstuklahv on lubatud"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Suurtähelukk on lubatud"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tõstuklahv on keelatud"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index f1bd870e2..4b1422984 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"بر اساس کلمه قبلی"</string> <string name="gesture_input" msgid="3310827802759290774">"ورودی اشاره"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"با دنبال کردن حروف یک کلمه، کلمه را وارد کنید"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"نمایش نسخه آزمایشی حرکت"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"نمایش کلمه در طول ورودی حرکتی"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"نمایش کلمه پیشنمایش متحرک با حرکت"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ذخیره شد"</string> <string name="label_go_key" msgid="1635148082137219148">"برو"</string> <string name="label_next_key" msgid="362972844525672568">"بعدی"</string> @@ -94,6 +97,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"جستجو"</string> <string name="spoken_description_dot" msgid="40711082435231673">"نقطه"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"تغییر زبان"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"بعدی"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"قبلی"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift فعال است"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock فعال شد"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift غیرفعال است"</string> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 898d522ba..28eeceddd 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Perustuu edelliseen sanan"</string> <string name="gesture_input" msgid="3310827802759290774">"Eleiden syöttö"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Syötä sana piirtämällä kirjaimet sormella"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Näytä eleen jälki"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Näytä elesanat"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Näytä eleen yhteydessä kelluva esikatselusana"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Tallennettu"</string> <string name="label_go_key" msgid="1635148082137219148">"Siirry"</string> <string name="label_next_key" msgid="362972844525672568">"Seur."</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Haku"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Piste"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Vaihda kieli"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Seuraava"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Edellinen"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Vaihto päällä"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock päällä"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Vaihto pois käytöstä"</string> diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml index 8cf2516a6..5288bd7d1 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/donottranslate.xml @@ -25,5 +25,7 @@ <!-- Symbols that should promote magic spaces into real space --> <string name="phantom_space_promoting_symbols">;:!?([*&@{<>+=|</string> <!-- Symbols that do NOT separate words --> - <string name="symbols_excluded_from_word_separators">\'</string> + <!-- Note that this is identical to the default value, but since the above ones are different + and those variables only make sense together, this is kept here for readability. --> + <string name="symbols_excluded_from_word_separators">\'-</string> </resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 63c36af4c..a8556e870 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Suggestions basées sur le mot précédent"</string> <string name="gesture_input" msgid="3310827802759290774">"Saisie gestuelle"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Saisir un mot en traçant ses lettres avec le doigt"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Afficher le tracé du geste"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Afficher un mot lors du geste"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Afficher un aperçu flottant du mot lors de la saisie gestuelle"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Suiv."</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Entrée"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Point"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Changer de langue"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Touche suivante"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Touche précédente"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Touche Maj activée"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Verrouillage des majuscules activé"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Touche Maj désactivée"</string> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 22c9efb1f..254c2d29b 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"पिछले शब्द के आधार पर"</string> <string name="gesture_input" msgid="3310827802759290774">"जेस्चर इनपुट"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"किसी शब्द के अक्षरों को ट्रेस करके कोई शब्द इनपुट करें"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"जेस्चर ट्रेल दिखाएं"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"जेस्चर शब्द दिखाएं"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"जेस्चर के साथ फ़्लोटिंग पूर्वावलोकन शब्द दिखाएं"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: सहेजा गया"</string> <string name="label_go_key" msgid="1635148082137219148">"जाएं"</string> <string name="label_next_key" msgid="362972844525672568">"अगला"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"रिटर्न"</string> <string name="spoken_description_search" msgid="1247236163755920808">"खोजें"</string> <string name="spoken_description_dot" msgid="40711082435231673">"बिंदु"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"भाषा स्विच करें"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"अगला"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"पिछला"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift सक्षम किया गया"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock सक्षम किया गया"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift अक्षम किया गया"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index e7c508d90..cb208804e 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na temelju prethodne riječi"</string> <string name="gesture_input" msgid="3310827802759290774">"Unos pokretom"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Unos riječi ispisivanjem slova riječi"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži trag pokreta"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Prikaži riječ pokreta"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Prikaži lebdeći pregled riječi uz pokret"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Spremljeno"</string> <string name="label_go_key" msgid="1635148082137219148">"Idi"</string> <string name="label_next_key" msgid="362972844525672568">"Dalje"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Pretraživanje"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Točka"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Promijeni jezik"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Sljedeće"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Prethodno"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Omogućena tipka Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Omogućeno pisanje velikih slova"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Onemogućena tipka Shift"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 637b5e482..62d1dd187 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Az előző szó alapján"</string> <string name="gesture_input" msgid="3310827802759290774">"Kézi bevitel"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Szó beírása a betűk megrajzolásával"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mozdulat irányának mutatása"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mozdulatot leíró szó mutatása"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mozdulatot leíró szó mutatása lebegő előnézetben"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : mentve"</string> <string name="label_go_key" msgid="1635148082137219148">"Ugrás"</string> <string name="label_next_key" msgid="362972844525672568">"Tovább"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Keresés"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Pont"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Nyelvek felcserélése"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Következő"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Előző"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift bekapcsolva"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock bekapcsolva"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift kikapcsolva"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index d5c6d48a4..8273d5483 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan kata sebelumnya"</string> <string name="gesture_input" msgid="3310827802759290774">"Masukan isyarat"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Masukkan kata dengan melacak huruf dari sebuah kata"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Tampilkan jalur isyarat"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Tampilkan kata isyarat"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Tampilkan kata pratinjau melayang dengan isyarat"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Telah disimpan"</string> <string name="label_go_key" msgid="1635148082137219148">"Buka"</string> <string name="label_next_key" msgid="362972844525672568">"Berikutnya"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Kembali"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Telusuri"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Ganti bahasa"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Berikutnya"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift diaktifkan"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock diaktifkan"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift dinonaktifkan"</string> diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml deleted file mode 100644 index 58e94361b..000000000 --- a/java/res/values-it/donottranslate.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2009, 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"> - <!-- Symbols that do NOT separate words --> - <string name="symbols_excluded_from_word_separators"></string> -</resources> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index efa02b23a..d529b8aad 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"In base alla parola precedente"</string> <string name="gesture_input" msgid="3310827802759290774">"Inserimento con gesti"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Inserisci una parola tracciandone le lettere"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra traccia con gesto"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostra parola con gesto"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostra parola di anteprima floating con gesto"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string> <string name="label_go_key" msgid="1635148082137219148">"Vai"</string> <string name="label_next_key" msgid="362972844525672568">"Avanti"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Invio"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Pallino"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambia lingua"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Successivo"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Precedente"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maiuscolo attivo"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Blocco maiuscole attivo"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maiuscolo disattivato"</string> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index b182ffd32..5c10716ce 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"בהתבסס על המילה הקודמת"</string> <string name="gesture_input" msgid="3310827802759290774">"קלט מחווה"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"הזן מילה על ידי החלקת האצבע מאות לאות"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"הצג שובל מחווה"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"הצג מילת מחווה"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"הצג תצוגה מקדימה צפה של המילה בזמן המחווה"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : נשמרה"</string> <string name="label_go_key" msgid="1635148082137219148">"בצע"</string> <string name="label_next_key" msgid="362972844525672568">"הבא"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"חזור"</string> <string name="spoken_description_search" msgid="1247236163755920808">"חיפוש"</string> <string name="spoken_description_dot" msgid="40711082435231673">"נקודה"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"החלף שפה"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"הבא"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"הקודם"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift מופעל"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock מופעל"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift מושבת"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 9d31d5cec..fae12ab4d 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"前の語句に基づいた入力候補表示"</string> <string name="gesture_input" msgid="3310827802759290774">"ジェスチャー入力"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"単語の文字をトレースして単語を入力"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"ジェスチャートレイルを表示"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"ジェスチャーワードを表示"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"ジェスチャーでプレビューワードをフローティング表示できます"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string> <string name="label_go_key" msgid="1635148082137219148">"実行"</string> <string name="label_next_key" msgid="362972844525672568">"次へ"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"検索"</string> <string name="spoken_description_dot" msgid="40711082435231673">"中点"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"言語を切り替え"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"次へ"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"前へ"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift有効"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock有効"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift解除"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index ad9d6e303..9049034c3 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"이전 단어에 기반한 추천"</string> <string name="gesture_input" msgid="3310827802759290774">"동작 입력"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"한번에 문자를 그려서 단어 입력"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"동작 흔적 표시"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"동작 단어 표시"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"동작에 따라 단어 미리보기 표시"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string> <string name="label_go_key" msgid="1635148082137219148">"이동"</string> <string name="label_next_key" msgid="362972844525672568">"다음"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"리턴 키"</string> <string name="spoken_description_search" msgid="1247236163755920808">"검색"</string> <string name="spoken_description_dot" msgid="40711082435231673">"점"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"언어 전환"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"다음"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"이전"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 사용"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock 사용"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 사용중지"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index e52452e30..f3b900382 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pagal ankstesnį žodį"</string> <string name="gesture_input" msgid="3310827802759290774">"Įvestis gestais"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Įveskite žodį brėždami jo raides"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Rodyti gestų kelią"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Rodyti gesto žodį"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Rodyti kintantį peržiūros žodį gestu"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: išsaugota"</string> <string name="label_go_key" msgid="1635148082137219148">"Pradėti"</string> <string name="label_next_key" msgid="362972844525672568">"Kitas"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Grįžti"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Ieškoti"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Taškas"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keisti kalbą"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Kitas"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Ankstesnis"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Įgalintas antrasis lygis"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Įgalintos didžiosios raidės"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Antrasis lygis išjungtas"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 274dd2f27..55be05218 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pamatojoties uz iepriekšējo vārdu"</string> <string name="gesture_input" msgid="3310827802759290774">"Ievade ar žestu"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Ievadiet vārdu, norādot tā burtus."</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Rādīt žesta pēdas"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Rādīt žesta vārdu"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Rādīt peldošo priekšskatījuma vārdu ar žestu"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: saglabāts"</string> <string name="label_go_key" msgid="1635148082137219148">"Sākt"</string> <string name="label_next_key" msgid="362972844525672568">"Tālāk"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Ievadīšanas taustiņš"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Meklēt"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punkts"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mainīt valodu"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Nākamā"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Iepriekšējā"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pārslēgšanas režīms iespējots"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Burtslēgs iespējots"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Pārslēgšanas režīms atspējots"</string> diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index 5100bb901..4f0f19bfc 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan perkataan sebelumnya"</string> <string name="gesture_input" msgid="3310827802759290774">"Input gerak isyarat"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Masukkan perkataan dengan menyurih huruf perkataan itu."</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Tunjukkan jejak gerak isyarat"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Tunjukkan perkataan gerak isyarat"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Tunjukkan perkataan pratonton terapung dengan gerak isyarat"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string> <string name="label_go_key" msgid="1635148082137219148">"Pergi"</string> <string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Carian"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Tukar bahasa"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Seterusnya"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kunci anjak didayakan"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kunci huruf besar didayakan"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kunci anjak dilumpuhkan"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index be609226a..f603dfaa1 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Basert på det forrige ordet"</string> <string name="gesture_input" msgid="3310827802759290774">"Bevegelseskommandoer"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Angi et ord ved å skrive bokstavene på skjermen med fingeren"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis bevegelsesspor"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Vis bevegelsesord"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vis flytende forhåndsvisningsord under bevegelser"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string> <string name="label_go_key" msgid="1635148082137219148">"Utfør"</string> <string name="label_next_key" msgid="362972844525672568">"Neste"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Søk"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Prikk"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Bytt språk"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Neste"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift er aktivert"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock er aktivert"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift er deaktivert"</string> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index dd2608668..33816f870 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Op basis van het vorige woord"</string> <string name="gesture_input" msgid="3310827802759290774">"Invoer met gebaren"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Voer een woord in door de letters van een woord te volgen"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Gebarenspoor weergeven"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Woord met gebaar weergeven"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zwevend voorbeeldwoord met gebaar weergeven"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string> <string name="label_go_key" msgid="1635148082137219148">"Start"</string> <string name="label_next_key" msgid="362972844525672568">"Verder"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Zoeken"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Stip"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Taal wijzigen"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ingeschakeld"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock ingeschakeld"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift uitgeschakeld"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index bc2e3ca1a..7e15566ac 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podstawie poprzedniego słowa"</string> <string name="gesture_input" msgid="3310827802759290774">"Wprowadzanie gestem"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Podaj słowo, pisząc litery palcem"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Pokazuj ślad gestu"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Podpowiadaj przy pisaniu gestem"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Pokazuj pływający podgląd słowa podczas pisania gestem"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Dalej"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Szukaj"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Przełącz język"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Dalej"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Wstecz"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift włączony"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock włączony"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift wyłączony"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 48a010a6a..fcab817bb 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string> <string name="gesture_input" msgid="3310827802759290774">"Introd. por gestos"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra, desenhando as letras de uma palavra"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra gesto"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palavra de visualização flutuante com gesto"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Avançar"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mudar de idioma"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Seguinte"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index fc5ccaa00..b4830675b 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string> <string name="gesture_input" msgid="3310827802759290774">"Entrada por gesto"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra traçando suas letras"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra do gesto"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar visualização flutuante da palavra com gesto"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Avançar"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Voltar"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Alterar idioma"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Próximo"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string> diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index dceeaf4a2..25d58732d 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -96,6 +96,12 @@ <skip /> <!-- no translation found for gesture_input_summary (7019742443455085809) --> <skip /> + <!-- no translation found for gesture_preview_trail (3802333369335722221) --> + <skip /> + <!-- no translation found for gesture_floating_preview_text (6859416520117939680) --> + <skip /> + <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) --> + <skip /> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Memorisà"</string> <string name="label_go_key" msgid="1635148082137219148">"Dai"</string> <string name="label_next_key" msgid="362972844525672568">"Vinavant"</string> @@ -151,6 +157,12 @@ <skip /> <!-- no translation found for spoken_description_dot (40711082435231673) --> <skip /> + <!-- no translation found for spoken_description_language_switch (5507091328222331316) --> + <skip /> + <!-- no translation found for spoken_description_action_next (8636078276664150324) --> + <skip /> + <!-- no translation found for spoken_description_action_previous (800872415009336208) --> + <skip /> <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) --> <skip /> <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) --> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index d0473464c..e47ed63f4 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Bazate pe cuvântul precedent"</string> <string name="gesture_input" msgid="3310827802759290774">"Utilizaţi gesturi"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Introduceţi un cuvânt desenând literele acestuia"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Se afişează urma gestului"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Sugestie cuvinte la gesturi"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Se afişează previzualizarea cuvântului flotant la gesturi"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Înainte"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Căutaţi"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punct"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Schimbaţi limba"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Înainte"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Înapoi"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tasta Shift a fost activată"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Tasta Caps Lock a fost activată"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tasta Shift a fost dezactivată"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 7e12ddb22..f780650ab 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Подсказки, основанные на предыдущих словах"</string> <string name="gesture_input" msgid="3310827802759290774">"Ввод жестами"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Перемещайте палец от буквы к букве, чтобы составить слово"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Рисовать линию"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Подсказывать слово"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показывать подсказку во время ввода"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: сохранено"</string> <string name="label_go_key" msgid="1635148082137219148">"Поиск"</string> <string name="label_next_key" msgid="362972844525672568">"Далее"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Клавиша \"Ввод\""</string> <string name="spoken_description_search" msgid="1247236163755920808">"Поиск"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Сменить язык"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Далее"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Верхний регистр включен"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock включен"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Верхний регистр отключен"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 619f41fc5..4aa0c5ce8 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základe predchádzajúceho slova"</string> <string name="gesture_input" msgid="3310827802759290774">"Zadávanie gestami"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Napíšte slovo zadaním jeho písmen"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovať stopu gesta"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Zobrazovať slovo gesta"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zobrazovať plávajúcu ukážku slova gesta"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uložené"</string> <string name="label_go_key" msgid="1635148082137219148">"Hľadať"</string> <string name="label_next_key" msgid="362972844525672568">"Ďalej"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Hľadať"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Bodka"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Prepnúť jazyk"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Ďalšie"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Predchádzajúce"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kláves Shift je povolený"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kláves Caps Lock je povolený"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kláves Shift je zakázaný"</string> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 00c3aad5e..d21e585c3 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podlagi prejšnje besede"</string> <string name="gesture_input" msgid="3310827802759290774">"Vnos s potezo"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Vnašanje besede z drsenjem po zaslonu od črke do črke"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži pot poteze"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Pokaži besedo za potezo"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Pokaži plavajoči predogled besede za potezo"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string> <string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string> <string name="label_next_key" msgid="362972844525672568">"Naprej"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Vračalka"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Iskanje"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Pika"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Preklop jezika"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Naprej"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Nazaj"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Način »Shift« je omogočen"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Način »Caps Lock« je omogočen"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Način »Shift« je onemogočen"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 7febdf0dc..3b209b305 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основу претходне речи"</string> <string name="gesture_input" msgid="3310827802759290774">"Унос покретом"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Унесите реч исписивањем слова речи"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Прикажи траг покрета"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Прикажи реч уз покрет"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Приказ плутајућег прегледа речи уз покрет"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Сачувано"</string> <string name="label_go_key" msgid="1635148082137219148">"Иди"</string> <string name="label_next_key" msgid="362972844525672568">"Следеће"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Претражи"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Тачка"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пребаци језик"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Следеће"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Претходно"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift је омогућен"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock је омогућен"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift је онемогућен"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 301b35813..71ba6204d 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baserat på föregående ord"</string> <string name="gesture_input" msgid="3310827802759290774">"Gestinmatning"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Skriv ett ord för hand genom att rita bokstäverna i ordet"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Visa spår efter rörelse"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Visa svävande ord för rörelse"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Visa förhandsgranskning av svävande ord med rörelse"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string> <string name="label_go_key" msgid="1635148082137219148">"Kör"</string> <string name="label_next_key" msgid="362972844525672568">"Nästa"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Retur"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Sök"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Byt språk"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Nästa"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Föregående"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift är aktiverat"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock är aktiverat"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift är inaktiverat"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index eed8e994f..ec960bcd4 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Kulingana na neno la awali"</string> <string name="gesture_input" msgid="3310827802759290774">"Ingizo la ishara"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Ingiza neno kwa kufuatilia herufi za neno"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Onyesha njia ya ishara"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Onyesha neno la ishara"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Onyesha neno hakiki linaloelea lililo na 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> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Rudi"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Tafuta"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Nukta"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Badili lugha"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Inayofuata"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Iliyotangulia"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift imewezeshwa"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock imewezeshwa"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift imelemazwa"</string> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index 619169c52..e296623b2 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -19,6 +19,8 @@ --> <resources> + <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET7} --> + <integer name="config_device_form_factor">1</integer> <bool name="config_enable_show_voice_key_option">false</bool> <bool name="config_enable_show_popup_on_keypress_option">false</bool> <bool name="config_enable_bigram_suggestions_option">false</bool> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index 27cb9ac21..346fa9979 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -19,6 +19,8 @@ --> <resources> + <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET10} --> + <integer name="config_device_form_factor">2</integer> <bool name="config_enable_show_voice_key_option">false</bool> <bool name="config_enable_show_popup_on_keypress_option">false</bool> <bool name="config_enable_bigram_suggestions_option">false</bool> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index cad003f55..4e8d2e221 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"ตามคำก่อนหน้า"</string> <string name="gesture_input" msgid="3310827802759290774">"ป้อนท่าทางสัมผัส"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"ป้อนคำโดยลากนิ้วเป็นรูปตัวอักษรที่อยู่ในคำ โดยไม่ยกนิ้วขึ้น"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"แสดงรอยทางเดินของท่าทาง"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"แสดงคำแนะนำท่าทาง"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"แสดงตัวอย่างคำแนะนำพร้อมด้วยท่าทาง"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string> <string name="label_go_key" msgid="1635148082137219148">"ไป"</string> <string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"ค้นหา"</string> <string name="spoken_description_dot" msgid="40711082435231673">"เครื่องหมายจุด"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"เปลี่ยนภาษา"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"ถัดไป"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"ก่อนหน้า"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"เปิดใช้งาน Shift แล้ว"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"เปิดใช้งาน Caps Lock แล้ว"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ปิดใช้งาน Shift แล้ว"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index 8ebd9c742..a2b46a66b 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Batay sa nakaraang salita"</string> <string name="gesture_input" msgid="3310827802759290774">"Pagpasok ng galaw"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Magpasok ng salita sa gamit ang pagsulat sa mga titik ng salita"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Ipakita ang trail ng galaw"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Ipakita ang salita ng galaw"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Ipakita ang lumulutang na preview ng salita na may galaw"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string> <string name="label_go_key" msgid="1635148082137219148">"Punta"</string> <string name="label_next_key" msgid="362972844525672568">"Susunod"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Bumalik"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Paghahanap"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Tuldok"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Magpalit ng wika"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Susunod"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Nakaraan"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pinagana ang shift"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Pinagana ang caps lock"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Hindi pinagana ang shift"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 32d8d9882..f9eb59831 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Önceki kelimeye dayanarak"</string> <string name="gesture_input" msgid="3310827802759290774">"Hareket girişi"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Bir kelimeyi harflerini izleyerek girin"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Hareket izini göster"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Hareketle yazılan kelimeyi göster"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Hareketle kayan önizleme kelimesini göster"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string> <string name="label_go_key" msgid="1635148082137219148">"Git"</string> <string name="label_next_key" msgid="362972844525672568">"İleri"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Ara"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Nokta"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Dili değiştir"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Sonraki"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Önceki"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Üst karakter etkin"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Büyük harf kilidi etkin"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Üst karakter devre dışı"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index d0b3b46c5..d62f42082 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основі попереднього слова"</string> <string name="gesture_input" msgid="3310827802759290774">"Введення жестами"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Введіть слово, малюючи літери, з яких воно складається"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Показувати слід жестів"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Показувати слово для жесту"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показувати спливаючий перегляд слова під час жесту"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : збережено"</string> <string name="label_go_key" msgid="1635148082137219148">"Іти"</string> <string name="label_next_key" msgid="362972844525672568">"Далі"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Клавіша Return"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Крапка"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Змінити мову"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Далі"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift увімкнено"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock увімкнено"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift вимкнено"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index e4ffa913c..13f51ad77 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Dựa trên từ trước đó"</string> <string name="gesture_input" msgid="3310827802759290774">"Nhập bằng cử chỉ"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Nhập từ bằng cách lần theo các chữ cái của từ"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Hiển thị vệt cử chỉ"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Hiển thị từ theo cử chỉ"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Hiển thị từ xem trước nổi bằng cử chỉ"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Đã lưu"</string> <string name="label_go_key" msgid="1635148082137219148">"Tìm"</string> <string name="label_next_key" msgid="362972844525672568">"Tiếp theo"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Quay lại"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Tìm kiếm"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Dấu chấm"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Chuyển ngôn ngữ"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Tiếp theo"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Trước"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Đã bật Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Đã bật Caps lock"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Đã tắt Shift"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 1361da6f1..c608694ad 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"根据上一个字词提供建议"</string> <string name="gesture_input" msgid="3310827802759290774">"手势输入"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"连笔书写输入字词"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"显示手指操作轨迹"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"显示手指操作文字"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"进行手指操作时显示浮动预览文字"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string> <string name="label_go_key" msgid="1635148082137219148">"开始"</string> <string name="label_next_key" msgid="362972844525672568">"下一步"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"返回"</string> <string name="spoken_description_search" msgid="1247236163755920808">"搜索"</string> <string name="spoken_description_dot" msgid="40711082435231673">"点"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"切换语言"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"下一个"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"上一个"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 模式已启用"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大写锁定已启用"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 模式已停用"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index efab7088e..120ae330f 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"根據上一個字詞產生"</string> <string name="gesture_input" msgid="3310827802759290774">"手勢輸入"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"以手指寫出字詞中字母的方式來輸入字詞"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"顯示手勢軌跡"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"顯示手勢字詞"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"使用手勢輸入時顯示浮動預覽字詞"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string> <string name="label_go_key" msgid="1635148082137219148">"開始"</string> <string name="label_next_key" msgid="362972844525672568">"繼續"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"返回"</string> <string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string> <string name="spoken_description_dot" msgid="40711082435231673">"點"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"切換語言"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"下一步"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"上一步"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 鍵已啟用"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大寫鎖定已啟用"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 鍵已停用"</string> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 58fc37c56..2b2a5872c 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -60,6 +60,9 @@ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Ngokususela egameni langaphambilini"</string> <string name="gesture_input" msgid="3310827802759290774">"Okokufaka kokuthinta"</string> <string name="gesture_input_summary" msgid="7019742443455085809">"Faka igama ngokulandela ngomkhondo izinhlamvu zegama"</string> + <string name="gesture_preview_trail" msgid="3802333369335722221">"Bonisa i-trail yokuthinta"</string> + <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Bonisa igama lokuthinta"</string> + <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Bonisa igama lokuhlola kuqala elintantayo nokuthinta"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kulondoloziwe"</string> <string name="label_go_key" msgid="1635148082137219148">"Iya"</string> <string name="label_next_key" msgid="362972844525672568">"Okulandelayo"</string> @@ -90,6 +93,9 @@ <string name="spoken_description_return" msgid="8178083177238315647">"Buyisela"</string> <string name="spoken_description_search" msgid="1247236163755920808">"Sesha"</string> <string name="spoken_description_dot" msgid="40711082435231673">"Icashazi"</string> + <string name="spoken_description_language_switch" msgid="5507091328222331316">"Shintsha ulimi"</string> + <string name="spoken_description_action_next" msgid="8636078276664150324">"Okulandelayo"</string> + <string name="spoken_description_action_previous" msgid="800872415009336208">"Okwangaphambilini"</string> <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"U-Shift uvunyelwe"</string> <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Ofeleba bavunyelwe"</string> <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"U-Shift uvimbelwe"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 1d52e46bb..02e8eeb3b 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -118,6 +118,18 @@ <enum name="italic" value="2" /> <enum name="boldItalic" value="3" /> </attr> + + <attr name="gestureFloatingPreviewTextSize" format="dimension" /> + <attr name="gestureFloatingPreviewTextColor" format="color" /> + <attr name="gestureFloatingPreviewTextOffset" format="dimension" /> + <attr name="gestureFloatingPreviewTextShadingColor" format="color" /> + <attr name="gestureFloatingPreviewTextShadingBorder" format="dimension" /> + <attr name="gestureFloatingPreviewTextShadowColor" format="color" /> + <attr name="gestureFloatingPreviewTextShadowBorder" format="dimension" /> + <attr name="gestureFloatingPreviewTextConnectorColor" format="color" /> + <attr name="gestureFloatingPreviewTextConnectorWidth" format="dimension" /> + <attr name="gesturePreviewTrailColor" format="color" /> + <attr name="gesturePreviewTrailWidth" format="dimension" /> </declare-styleable> <declare-styleable name="MainKeyboardView"> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 50f46c3f5..e5575e7ae 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -19,6 +19,8 @@ --> <resources> + <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_PHONE} --> + <integer name="config_device_form_factor">0</integer> <bool name="config_use_fullscreen_mode">false</bool> <bool name="config_enable_show_voice_key_option">true</bool> <bool name="config_enable_show_popup_on_keypress_option">true</bool> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index 925eb55fa..c59bad302 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -93,4 +93,12 @@ <dimen name="more_suggestions_hint_text_size">27dp</dimen> <integer name="suggestions_count_in_strip">3</integer> <integer name="center_suggestion_percentile">36</integer> + + <!-- Gesture preview parameters --> + <dimen name="gesture_preview_trail_width">2.5dp</dimen> + <dimen name="gesture_floating_preview_text_size">35dp</dimen> + <dimen name="gesture_floating_preview_text_offset">75dp</dimen> + <dimen name="gesture_floating_preview_text_shadow_border">17.5dp</dimen> + <dimen name="gesture_floating_preview_text_shading_border">7.5dp</dimen> + <dimen name="gesture_floating_preview_text_connector_width">1.0dp</dimen> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 12abf8469..07b3f31c7 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -111,10 +111,16 @@ <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. --> <string name="bigram_prediction_summary">Based on previous word</string> - <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=20]--> + <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]--> <string name="gesture_input">Gesture input</string> <!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]--> <string name="gesture_input_summary">Input a word by tracing the letters of a word</string> + <!-- Option to enable gesture trail preview. The user can see a trail of the gesture during gesture input. [CHAR LIMIT=30]--> + <string name="gesture_preview_trail">Show gesture trail</string> + <!-- Option to enable gesture floating text preview. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=30]--> + <string name="gesture_floating_preview_text">Show gesture word</string> + <!-- Description for "gesture_floating_preview_text" option. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=65]--> + <string name="gesture_floating_preview_text_summary">Show floating preview word with gesture</string> <!-- Indicates that a word has been added to the dictionary --> <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string> @@ -181,6 +187,12 @@ <string name="spoken_description_search">Search</string> <!-- Spoken description for the "U+2022" (BULLET) keyboard key. --> <string name="spoken_description_dot">Dot</string> + <!-- Spoken description for the "Switch language" keyboard key. --> + <string name="spoken_description_language_switch">Switch language</string> + <!-- Spoken description for the "Next" action keyboard key. --> + <string name="spoken_description_action_next">Next</string> + <!-- Spoken description for the "Previous" action keyboard key. --> + <string name="spoken_description_action_previous">Previous</string> <!-- Spoken feedback after turning "Shift" mode on. --> <string name="spoken_description_shiftmode_on">Shift enabled</string> @@ -303,6 +315,15 @@ <!-- Description for English (United States) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25] This should be identical to subtype_en_US aside from the trailing (%s). --> <string name="subtype_with_layout_en_US">English (US) (<xliff:g id="layout">%s</xliff:g>)</string> + <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. + Description for Serbian Cyrillic keyboard subtype [CHAR LIMIT=25] + <string name="subtype_serbian_cyrillic">Serbian (Cyrillic)</string> + Description for Serbian Latin keyboard subtype [CHAR LIMIT=25] + <string name="subtype_serbian_latin">Serbian (Latin)</string> + Description for Serbian Latin keyboard subtype with explicit keyboard layout [CHAR LIMIT=25] + This should be identical to subtype_serbian_latin aside from the trailing (%s). + <string name="subtype_with_layout_sr-Latn">Serbian (Latin) (<xliff:g id="layout">%s</xliff:g>)</string> + --> <!-- Description for language agnostic keyboard subtype [CHAR LIMIT=25] --> <string name="subtype_no_language">No language</string> <!-- Description for language agnostic QWERTY keyboard subtype [CHAR LIMIT=25] --> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index 8797fd7aa..955a27631 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<resources> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Theme "Basic" --> <style name="Keyboard"> <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> @@ -67,6 +67,18 @@ <item name="shadowColor">#BB000000</item> <item name="shadowRadius">2.75</item> <item name="backgroundDimAlpha">128</item> + <!-- android:color/holo_blue_light=#FF33B5E5 --> + <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item> + <item name="gestureFloatingPreviewTextColor">@android:color/white</item> + <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item> + <item name="gestureFloatingPreviewTextShadingColor">@android:color/holo_blue_light</item> + <item name="gestureFloatingPreviewTextShadingBorder">@dimen/gesture_floating_preview_text_shading_border</item> + <item name="gestureFloatingPreviewTextShadowColor">#FF252525</item> + <item name="gestureFloatingPreviewTextShadowBorder">@dimen/gesture_floating_preview_text_shadow_border</item> + <item name="gestureFloatingPreviewTextConnectorColor">@android:color/white</item> + <item name="gestureFloatingPreviewTextConnectorWidth">@dimen/gesture_floating_preview_text_connector_width</item> + <item name="gesturePreviewTrailColor">@android:color/holo_blue_light</item> + <item name="gesturePreviewTrailWidth">@dimen/gesture_preview_trail_width</item> <!-- Common attributes of MainKeyboardView --> <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item> <item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item> diff --git a/java/res/xml-sw768dp/key_space.xml b/java/res/xml-sw768dp/key_space.xml index 8968f080a..58e71d807 100644 --- a/java/res/xml-sw768dp/key_space.xml +++ b/java/res/xml-sw768dp/key_space.xml @@ -24,15 +24,36 @@ <switch> <case latin:languageCode="fa" + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="24.141%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageCode="fa" + latin:languageSwitchKeyEnabled="false" > <Key latin:keyStyle="spaceKeyStyle" latin:keyWidth="32.188%p" /> - <!-- U+200C: "" ZERO WIDTH NON-JOINER - U+200D: "" ZERO WIDTH JOINER --> <Key latin:keyStyle="zwnjKeyStyle" /> </case> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="32.188%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> <default> <Key latin:keyStyle="spaceKeyStyle" diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml index 7afe5848a..537e76800 100644 --- a/java/res/xml-sw768dp/key_styles_common.xml +++ b/java/res/xml-sw768dp/key_styles_common.xml @@ -76,7 +76,7 @@ <key-style latin:styleName="spaceKeyStyle" latin:code="!code/key_space" - latin:keyActionFlags="noKeyPreview" /> + latin:keyActionFlags="noKeyPreview|enableLongPress" /> <!-- U+200C: ZERO WIDTH NON-JOINER U+200D: ZERO WIDTH JOINER --> <key-style @@ -100,6 +100,12 @@ latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> <key-style + latin:styleName="languageSwitchKeyStyle" + latin:code="!code/key_language_switch" + latin:keyIcon="!icon/language_switch_key" + latin:keyActionFlags="noKeyPreview|altCodeWhileTyping|enableLongPress" + latin:altCode="!code/key_space" /> + <key-style latin:styleName="settingsKeyStyle" latin:code="!code/key_settings" latin:keyIcon="!icon/settings_key" diff --git a/java/res/xml-sw768dp/rows_east_slavic.xml b/java/res/xml-sw768dp/rows_east_slavic.xml index 0316c76f6..a4287f162 100644 --- a/java/res/xml-sw768dp/rows_east_slavic.xml +++ b/java/res/xml-sw768dp/rows_east_slavic.xml @@ -33,9 +33,8 @@ <include latin:keyboardLayout="@xml/rowkeys_east_slavic1" latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" /> - <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> <Key - latin:keyLabel="ъ" /> + latin:keyLabel="!text/keylabel_for_east_slavic_row1_12" /> <Key latin:keyStyle="deleteKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 7f8a23a0e..acdf7645f 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -22,10 +22,12 @@ <!-- Supported subtypes keyboard_locale: script_name/keyboard_layout_set[:keyboard_locale] + af: Afrikaans/qwerty ar: Arabic/arabic be: Belarusian/east_slavic bg: Bulgarian/bulgarian bg: Bulgarian/bulgarian_bds + ca: Catalan/spanish cs: Czech/qwertz da: Danish/nordic de: German/qwertz @@ -41,14 +43,16 @@ hi: Hindi/hindi hr: Croatian/qwertz hu: Hungarian/qwertz + in: Indonesian/qwerty # "id" is official language code of Indonesian. is: Icelandic/qwerty it: Italian/qwerty - iw: Hebrew/hebrew + iw: Hebrew/hebrew # "he" is official language code of Hebrew. ka: Georgian/georgian ky: Kyrgyz/east_slavic lt: Lithuanian/qwerty lv: Latvian/qwerty mk: Macedonian/south_slavic + ms: Malay/qwerty nb: Norwegian Bokmål/nordic nl: Dutch/qwerty pl: Polish/qwerty @@ -59,11 +63,15 @@ sk: Slovak/qwerty sl: Slovenian/qwerty sr: Serbian/south_slavic + (sr-Latn: Serbian/qwerty) # not yet implemented. sv: Swedish/nordic + sw: Swahili/qwerty th: Thai/thai + tl: Tagalog/spanish tr: Turkish/qwerty uk: Ukrainian/east_slavic vi: Vietnamese/qwerty + zu: Zulu/qwerty zz: QWERTY/qwerty --> <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. --> @@ -86,6 +94,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="af" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" @@ -110,6 +124,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="ca" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -186,6 +206,13 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> + <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. --> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:imeSubtypeLocale="in" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="is" @@ -237,6 +264,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="ms" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="nb" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -295,6 +328,20 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> + <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_serbian_cyrillic" + android:imeSubtypeLocale="sr" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_serbian_latin" + android:imeSubtypeLocale="sr-Latn" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> + --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="sv" @@ -303,12 +350,24 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="sw" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="th" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=thai" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="tl" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="tr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -326,6 +385,12 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:imeSubtypeLocale="zu" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_no_language_qwerty" android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 4f11cb7e5..ef6be3eed 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -102,6 +102,12 @@ android:title="@string/advanced_settings" android:summary="@string/advanced_settings_summary"> <CheckBoxPreference + android:key="pref_key_use_contacts_dict" + android:title="@string/use_contacts_dict" + android:summary="@string/use_contacts_dict_summary" + android:persistent="true" + android:defaultValue="true" /> + <CheckBoxPreference android:key="pref_suppress_language_switch_key" android:title="@string/suppress_language_switch_key" android:persistent="true" @@ -120,18 +126,23 @@ <ListPreference android:key="pref_key_preview_popup_dismiss_delay" android:title="@string/key_preview_popup_dismiss_delay" /> - <CheckBoxPreference - android:key="pref_key_use_contacts_dict" - android:title="@string/use_contacts_dict" - android:summary="@string/use_contacts_dict_summary" - android:persistent="true" - android:defaultValue="true" /> <PreferenceScreen android:key="pref_vibration_duration_settings" android:title="@string/prefs_keypress_vibration_duration_settings"/> <PreferenceScreen android:key="pref_keypress_sound_volume" android:title="@string/prefs_keypress_sound_volume_settings" /> + <CheckBoxPreference + android:key="pref_gesture_preview_trail" + android:title="@string/gesture_preview_trail" + android:persistent="true" + android:defaultValue="true" /> + <CheckBoxPreference + android:key="pref_gesture_floating_preview_text" + android:title="@string/gesture_floating_preview_text" + android:summary="@string/gesture_floating_preview_text_summary" + android:persistent="true" + android:defaultValue="true" /> </PreferenceScreen> </PreferenceCategory> </PreferenceScreen> diff --git a/java/res/xml/rowkeys_east_slavic1.xml b/java/res/xml/rowkeys_east_slavic1.xml index 00cb6a973..c1b43bd36 100644 --- a/java/res/xml/rowkeys_east_slavic1.xml +++ b/java/res/xml/rowkeys_east_slavic1.xml @@ -47,7 +47,7 @@ latin:keyLabel="е" latin:keyHintLabel="5" latin:additionalMoreKeys="5" - latin:moreKeys="!text/more_keys_for_cyrillic_ye" /> + latin:moreKeys="!text/more_keys_for_cyrillic_ie" /> <!-- U+043D: "н" CYRILLIC SMALL LETTER EN --> <Key latin:keyLabel="н" @@ -58,7 +58,8 @@ <Key latin:keyLabel="г" latin:keyHintLabel="7" - latin:additionalMoreKeys="7" /> + latin:additionalMoreKeys="7" + latin:moreKeys="!text/more_keys_for_cyrillic_ghe" /> <!-- U+0448: "ш" CYRILLIC SMALL LETTER SHA --> <Key latin:keyLabel="ш" @@ -75,6 +76,5 @@ latin:additionalMoreKeys="0" /> <!-- U+0445: "х" CYRILLIC SMALL LETTER HA --> <Key - latin:keyLabel="х" - latin:moreKeys="!text/more_keys_for_cyrillic_ha" /> + latin:keyLabel="х" /> </merge> diff --git a/java/res/xml/rowkeys_east_slavic2.xml b/java/res/xml/rowkeys_east_slavic2.xml index c635af2d9..9743727c1 100644 --- a/java/res/xml/rowkeys_east_slavic2.xml +++ b/java/res/xml/rowkeys_east_slavic2.xml @@ -52,7 +52,6 @@ <!-- U+0436: "ж" CYRILLIC SMALL LETTER ZHE --> <Key latin:keyLabel="ж" /> - <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> <Key - latin:keyLabel="э" /> + latin:keyLabel="!text/keylabel_for_east_slavic_row2_11" /> </merge> diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 5ffd94a43..9b74070af 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -60,10 +60,8 @@ public class KeyCodeDescriptionMapper { // Manual label substitutions for key labels with no string resource mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); - // Symbols that most TTS engines can't speak - mKeyCodeMap.put(' ', R.string.spoken_description_space); - // Special non-character codes defined in Keyboard + mKeyCodeMap.put(Keyboard.CODE_SPACE, R.string.spoken_description_space); mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return); mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings); @@ -71,6 +69,9 @@ public class KeyCodeDescriptionMapper { mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic); mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab); + mKeyCodeMap.put(Keyboard.CODE_LANGUAGE_SWITCH, R.string.spoken_description_language_switch); + mKeyCodeMap.put(Keyboard.CODE_ACTION_NEXT, R.string.spoken_description_action_next); + mKeyCodeMap.put(Keyboard.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous); } /** @@ -274,8 +275,7 @@ public class KeyCodeDescriptionMapper { return context.getString(OBSCURED_KEY_RES_ID); } - final int resId = mKeyCodeMap.get(code); - if (resId != 0) { + if (mKeyCodeMap.indexOfKey(code) >= 0) { return context.getString(mKeyCodeMap.get(code)); } else if (isDefinedNonCtrl) { return Character.toString((char) code); diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java new file mode 100644 index 000000000..0befa7a66 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.inputmethodservice.InputMethodService; + +import java.lang.reflect.Method; + +public class InputMethodServiceCompatUtils { + private static final Method METHOD_enableHardwareAcceleration = + CompatUtils.getMethod(InputMethodService.class, "enableHardwareAcceleration"); + + private InputMethodServiceCompatUtils() { + // This utility class is not publicly instantiable. + } + + public static boolean enableHardwareAcceleration(InputMethodService ims) { + return (Boolean)CompatUtils.invoke(ims, false, METHOD_enableHardwareAcceleration); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index e1e1ca9cf..178c9ff05 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -414,8 +414,14 @@ public class Key { @Override public String toString() { - return String.format("%s/%s %d,%d %dx%d %s/%s/%s", - Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel, + final String label; + if (StringUtils.codePointCount(mLabel) == 1 && mLabel.codePointAt(0) == mCode) { + label = ""; + } else { + label = "/" + mLabel; + } + return String.format("%s%s %d,%d %dx%d %s/%s/%s", + Keyboard.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel, KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 9abc79d3c..97d88af4a 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -25,7 +25,6 @@ public class KeyDetector { private Keyboard mKeyboard; private int mCorrectionX; private int mCorrectionY; - private boolean mProximityCorrectOn; /** * This class handles key detection. @@ -38,8 +37,9 @@ public class KeyDetector { } public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) { - if (keyboard == null) + if (keyboard == null) { throw new NullPointerException(); + } mCorrectionX = (int)correctionX; mCorrectionY = (int)correctionY; mKeyboard = keyboard; @@ -59,19 +59,9 @@ public class KeyDetector { } public Keyboard getKeyboard() { - if (mKeyboard == null) - throw new IllegalStateException("keyboard isn't set"); return mKeyboard; } - public void setProximityCorrectionEnabled(boolean enabled) { - mProximityCorrectOn = enabled; - } - - public boolean isProximityCorrectionEnabled() { - return mProximityCorrectOn; - } - public boolean alwaysAllowsSlidingInput() { return false; } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index f1a35b212..3abe890cb 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -604,9 +604,6 @@ public class Keyboard { } public float getKeyX(TypedArray keyAttr) { - final int widthType = Builder.getEnumValue(keyAttr, - R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); - final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding; if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index b54c72687..1e5277345 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -55,10 +55,15 @@ public class KeyboardId { public static final int ELEMENT_PHONE_SYMBOLS = 8; public static final int ELEMENT_NUMBER = 9; + public static final int FORM_FACTOR_PHONE = 0; + public static final int FORM_FACTOR_TABLET7 = 1; + public static final int FORM_FACTOR_TABLET10 = 2; + private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1; public final InputMethodSubtype mSubtype; public final Locale mLocale; + public final int mDeviceFormFactor; public final int mOrientation; public final int mWidth; public final int mMode; @@ -72,11 +77,12 @@ public class KeyboardId { private final int mHashCode; - public KeyboardId(int elementId, InputMethodSubtype subtype, int orientation, int width, - int mode, EditorInfo editorInfo, boolean clobberSettingsKey, boolean shortcutKeyEnabled, - boolean hasShortcutKey, boolean languageSwitchKeyEnabled) { + public KeyboardId(int elementId, InputMethodSubtype subtype, int deviceFormFactor, + int orientation, int width, int mode, EditorInfo editorInfo, boolean clobberSettingsKey, + boolean shortcutKeyEnabled, boolean hasShortcutKey, boolean languageSwitchKeyEnabled) { mSubtype = subtype; mLocale = SubtypeLocale.getSubtypeLocale(subtype); + mDeviceFormFactor = deviceFormFactor; mOrientation = orientation; mWidth = width; mMode = mode; @@ -94,6 +100,7 @@ public class KeyboardId { private static int computeHashCode(KeyboardId id) { return Arrays.hashCode(new Object[] { + id.mDeviceFormFactor, id.mOrientation, id.mElementId, id.mMode, @@ -115,7 +122,8 @@ public class KeyboardId { private boolean equals(KeyboardId other) { if (other == this) return true; - return other.mOrientation == mOrientation + return other.mDeviceFormFactor == mDeviceFormFactor + && other.mOrientation == mOrientation && other.mElementId == mElementId && other.mMode == mMode && other.mWidth == mWidth @@ -184,11 +192,11 @@ public class KeyboardId { @Override public String toString() { - return String.format("[%s %s:%s %s%d %s %s %s%s%s%s%s%s%s%s]", + return String.format("[%s %s:%s %s-%s:%d %s %s %s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), - (mOrientation == 1 ? "port" : "land"), mWidth, + deviceFormFactor(mDeviceFormFactor), (mOrientation == 1 ? "port" : "land"), mWidth, modeName(mMode), imeAction(), (navigateNext() ? "navigateNext" : ""), @@ -226,6 +234,15 @@ public class KeyboardId { } } + public static String deviceFormFactor(int devoceFormFactor) { + switch (devoceFormFactor) { + case FORM_FACTOR_PHONE: return "phone"; + case FORM_FACTOR_TABLET7: return "tablet7"; + case FORM_FACTOR_TABLET10: return "tablet10"; + default: return null; + } + } + public static String modeName(int mode) { switch (mode) { case MODE_TEXT: return "text"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index aab89a3e5..64b3f0952 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -115,6 +115,7 @@ public class KeyboardLayoutSet { boolean mNoSettingsKey; boolean mLanguageSwitchKeyEnabled; InputMethodSubtype mSubtype; + int mDeviceFormFactor; int mOrientation; int mWidth; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. @@ -211,9 +212,10 @@ public class KeyboardLayoutSet { final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype); final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage; final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain); - return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mOrientation, - params.mWidth, params.mMode, params.mEditorInfo, params.mNoSettingsKey, - voiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled); + return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor, + params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo, + params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey, + params.mLanguageSwitchKeyEnabled); } public static class Builder { @@ -239,9 +241,11 @@ public class KeyboardLayoutSet { mPackageName, NO_SETTINGS_KEY, mEditorInfo); } - public Builder setScreenGeometry(int orientation, int widthPixels) { - mParams.mOrientation = orientation; - mParams.mWidth = widthPixels; + public Builder setScreenGeometry(int deviceFormFactor, int orientation, int widthPixels) { + final Params params = mParams; + params.mDeviceFormFactor = deviceFormFactor; + params.mOrientation = orientation; + params.mWidth = widthPixels; return this; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index d637ab5f1..dc84763c1 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -74,7 +74,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { private MainKeyboardView mKeyboardView; private LatinIME mLatinIME; private Resources mResources; - private SettingsValues mCurrentSettingsValues; private KeyboardState mState; @@ -136,11 +135,11 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { } public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) { - mCurrentSettingsValues = settingsValues; final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( mThemeContext, editorInfo); - builder.setScreenGeometry(mThemeContext.getResources().getConfiguration().orientation, - mThemeContext.getResources().getDisplayMetrics().widthPixels); + final Resources res = mThemeContext.getResources(); + builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor), + res.getConfiguration().orientation, res.getDisplayMetrics().widthPixels); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), @@ -171,20 +170,20 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { } private void setKeyboard(final Keyboard keyboard) { - final Keyboard oldKeyboard = mKeyboardView.getKeyboard(); - mKeyboardView.setGestureInputEnabled(mCurrentSettingsValues.mGestureInputEnabled); - mKeyboardView.setKeyboard(keyboard); + final MainKeyboardView keyboardView = mKeyboardView; + final Keyboard oldKeyboard = keyboardView.getKeyboard(); + keyboardView.setKeyboard(keyboard); mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding); - mKeyboardView.setKeyPreviewPopupEnabled( + keyboardView.setKeyPreviewPopupEnabled( SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources), SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources)); - mKeyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); - mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); + keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); + keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage( keyboard.mId.mLocale); - mKeyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, + keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true)); } @@ -350,7 +349,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { return mKeyboardView; } - public View onCreateInputView() { + public View onCreateInputView(boolean isHardwareAcceleratedDrawingEnabled) { if (mKeyboardView != null) { mKeyboardView.closing(); } @@ -373,6 +372,10 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { } mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); + if (isHardwareAcceleratedDrawingEnabled) { + mKeyboardView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? + } mKeyboardView.setKeyboardActionListener(mLatinIME); if (mForceNonDistinctMultitouch) { mKeyboardView.setDistinctMultitouch(false); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index f751fa53c..69e4d9805 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -25,7 +25,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.Region.Op; +import android.graphics.Region; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Message; @@ -35,9 +35,9 @@ import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.inputmethod.keyboard.internal.PreviewPlacerView; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -97,9 +97,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // The maximum key label width in the proportion to the key width. private static final float MAX_LABEL_RATIO = 0.90f; - private final static int GESTURE_DRAWING_WIDTH = 5; - private final static int GESTURE_DRAWING_COLOR = 0xff33b5e5; - // Main keyboard private Keyboard mKeyboard; protected final KeyDrawParams mKeyDrawParams; @@ -109,10 +106,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { protected final KeyPreviewDrawParams mKeyPreviewDrawParams; private boolean mShowKeyPreviewPopup = true; private int mDelayAfterPreview; - private ViewGroup mPreviewPlacer; + private final PreviewPlacerView mPreviewPlacerView; - /** True if the gesture input is enabled. */ - protected boolean mGestureInputEnabled; + /** True if {@link KeyboardView} should handle gesture events. */ + protected boolean mShouldHandleGesture; // Drawing /** True if the entire keyboard needs to be dimmed. */ @@ -123,16 +120,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private boolean mInvalidateAllKeys; /** The keys that should be drawn */ private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>(); - /** The region of invalidated keys */ - private final Rect mInvalidatedKeysRect = new Rect(); - /** The region of invalidated gestures */ - private final Rect mInvalidatedGesturesRect = new Rect(); + /** The working rectangle variable */ + private final Rect mWorkingRect = new Rect(); /** The keyboard bitmap buffer for faster updates */ - private Bitmap mBuffer; + /** The clip region to draw keys */ + private final Region mClipRegion = new Region(); + private Bitmap mOffscreenBuffer; /** The canvas for the above mutable keyboard bitmap */ - private Canvas mCanvas; + private Canvas mOffscreenCanvas; private final Paint mPaint = new Paint(); - private final Paint mGesturePaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); // This sparse array caches key label text height in pixel indexed by key label text size. private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>(); @@ -261,10 +257,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } public void updateKeyHeight(int keyHeight) { - if (mKeyLetterRatio >= 0.0f) + if (mKeyLetterRatio >= 0.0f) { mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); - if (mKeyLabelRatio >= 0.0f) + } + if (mKeyLabelRatio >= 0.0f) { mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio); + } mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio); mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio); mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio); @@ -346,13 +344,16 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } public void updateKeyHeight(int keyHeight) { - mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio); - mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); + if (mPreviewTextRatio >= 0.0f) { + mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio); + } + if (mKeyLetterRatio >= 0.0f) { + mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); + } } private static void setAlpha(Drawable drawable, int alpha) { - if (drawable == null) - return; + if (drawable == null) return; drawable.setAlpha(alpha); } } @@ -377,18 +378,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { R.styleable.KeyboardView_verticalCorrection, 0); mMoreKeysLayout = a.getResourceId(R.styleable.KeyboardView_moreKeysLayout, 0); mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0); + mPreviewPlacerView = new PreviewPlacerView(context, a); a.recycle(); mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; mPaint.setAntiAlias(true); - - // TODO: These paint parameters should be specified via attribute of the view and styleable. - mGesturePaint.setAntiAlias(true); - mGesturePaint.setStyle(Paint.Style.STROKE); - mGesturePaint.setStrokeJoin(Paint.Join.ROUND); - mGesturePaint.setColor(GESTURE_DRAWING_COLOR); - mGesturePaint.setStrokeWidth(GESTURE_DRAWING_WIDTH); } // Read fraction value in TypedArray as float. @@ -443,8 +438,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { return mShowKeyPreviewPopup; } - public void setGestureInputEnabled(boolean gestureInputEnabled) { - mGestureInputEnabled = gestureInputEnabled; + public void setGestureHandlingMode(boolean shouldHandleGesture, + boolean drawsGesturePreviewTrail, boolean drawsGestureFloatingPreviewText) { + mShouldHandleGesture = shouldHandleGesture; + mPreviewPlacerView.setGesturePreviewMode( + drawsGesturePreviewTrail, drawsGestureFloatingPreviewText); } @Override @@ -461,65 +459,107 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mBufferNeedsUpdate || mBuffer == null) { + if (canvas.isHardwareAccelerated()) { + onDrawKeyboard(canvas); + return; + } + if (mBufferNeedsUpdate || mOffscreenBuffer == null) { mBufferNeedsUpdate = false; - onBufferDraw(); + if (maybeAllocateOffscreenBuffer()) { + mInvalidateAllKeys = true; + // TODO: Stop using the offscreen canvas even when in software rendering + if (mOffscreenCanvas != null) { + mOffscreenCanvas.setBitmap(mOffscreenBuffer); + } else { + mOffscreenCanvas = new Canvas(mOffscreenBuffer); + } + } + onDrawKeyboard(mOffscreenCanvas); } - canvas.drawBitmap(mBuffer, 0, 0, null); + canvas.drawBitmap(mOffscreenBuffer, 0, 0, null); } - private void onBufferDraw() { + private boolean maybeAllocateOffscreenBuffer() { final int width = getWidth(); final int height = getHeight(); - if (width == 0 || height == 0) - return; - if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) { - if (mBuffer != null) - mBuffer.recycle(); - mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mInvalidateAllKeys = true; - if (mCanvas != null) { - mCanvas.setBitmap(mBuffer); - } else { - mCanvas = new Canvas(mBuffer); - } + if (width == 0 || height == 0) { + return false; } + if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width + && mOffscreenBuffer.getHeight() == height) { + return false; + } + freeOffscreenBuffer(); + mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + return true; + } + private void freeOffscreenBuffer() { + if (mOffscreenBuffer != null) { + mOffscreenBuffer.recycle(); + mOffscreenBuffer = null; + } + } + + private void onDrawKeyboard(final Canvas canvas) { if (mKeyboard == null) return; - final Canvas canvas = mCanvas; + final int width = getWidth(); + final int height = getHeight(); final Paint paint = mPaint; final KeyDrawParams params = mKeyDrawParams; - if (mInvalidateAllKeys || mInvalidatedKeys.isEmpty()) { - mInvalidatedKeysRect.set(0, 0, width, height); - canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE); - canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); + // Calculate clip region and set. + final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty(); + final boolean isHardwareAccelerated = canvas.isHardwareAccelerated(); + // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. + if (drawAllKeys || isHardwareAccelerated) { + mClipRegion.set(0, 0, width, height); + } else { + mClipRegion.setEmpty(); + for (final Key key : mInvalidatedKeys) { + if (mKeyboard.hasKey(key)) { + final int x = key.mX + getPaddingLeft(); + final int y = key.mY + getPaddingTop(); + mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); + mClipRegion.union(mWorkingRect); + } + } + } + if (!isHardwareAccelerated) { + canvas.clipRegion(mClipRegion, Region.Op.REPLACE); + } + + // Draw keyboard background. + canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); + final Drawable background = getBackground(); + if (background != null) { + background.draw(canvas); + } + + // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. + if (drawAllKeys || isHardwareAccelerated) { // Draw all keys. for (final Key key : mKeyboard.mKeys) { onDrawKey(key, canvas, paint, params); } - if (mNeedsToDimEntireKeyboard) { - drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint); - } } else { // Draw invalidated keys. for (final Key key : mInvalidatedKeys) { - if (!mKeyboard.hasKey(key)) { - continue; - } - final int x = key.mX + getPaddingLeft(); - final int y = key.mY + getPaddingTop(); - mInvalidatedKeysRect.set(x, y, x + key.mWidth, y + key.mHeight); - canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE); - canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); - onDrawKey(key, canvas, paint, params); - if (mNeedsToDimEntireKeyboard) { - drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint); + if (mKeyboard.hasKey(key)) { + onDrawKey(key, canvas, paint, params); } } } + // Overlay a dark rectangle to dim. + if (mNeedsToDimEntireKeyboard) { + paint.setColor(Color.BLACK); + paint.setAlpha(mBackgroundDimAlpha); + // Note: clipRegion() above is in effect if it was called. + canvas.drawRect(0, 0, width, height, paint); + } + // ResearchLogging indicator. // TODO: Reimplement using a keyboard background image specific to the ResearchLogger, // and remove this call. @@ -528,7 +568,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } mInvalidatedKeys.clear(); - mInvalidatedKeysRect.setEmpty(); mInvalidateAllKeys = false; } @@ -853,13 +892,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { canvas.translate(-x, -y); } - // Overlay a dark rectangle to dim. - private static void drawDimRectangle(Canvas canvas, Rect rect, int alpha, Paint paint) { - paint.setColor(Color.BLACK); - paint.setAlpha(alpha); - canvas.drawRect(rect, paint); - } - public Paint newDefaultLabelPaint() { final Paint paint = new Paint(); paint.setAntiAlias(true); @@ -888,58 +920,33 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); } - private static class PreviewView extends RelativeLayout { - KeyPreviewDrawParams mParams; - Paint mGesturePaint; - - public PreviewView(Context context, KeyPreviewDrawParams params, Paint gesturePaint) { - super(context); - setWillNotDraw(false); - mParams = params; - mGesturePaint = gesturePaint; - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.translate(mParams.mCoordinates[0], mParams.mCoordinates[1]); - PointerTracker.drawGestureTrailForAllPointerTrackers(canvas, mGesturePaint); - canvas.translate(-mParams.mCoordinates[0], -mParams.mCoordinates[1]); - } - } - private void addKeyPreview(TextView keyPreview) { - if (mPreviewPlacer == null) { - createPreviewPlacer(); - } - mPreviewPlacer.addView( - keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0)); + locatePreviewPlacerView(); + mPreviewPlacerView.addView( + keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); } - private void createPreviewPlacer() { - mPreviewPlacer = new PreviewView(getContext(), mKeyPreviewDrawParams, mGesturePaint); + private void locatePreviewPlacerView() { + if (mPreviewPlacerView.getParent() != null) { + return; + } + final int[] viewOrigin = new int[2]; + getLocationInWindow(viewOrigin); + mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]); final ViewGroup windowContentView = (ViewGroup)getRootView().findViewById(android.R.id.content); - windowContentView.addView(mPreviewPlacer); + windowContentView.addView(mPreviewPlacerView); + } + + public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) { + locatePreviewPlacerView(); + mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText); } @Override public void showGestureTrail(PointerTracker tracker) { - if (mPreviewPlacer == null) { - createPreviewPlacer(); - } - final Rect r = tracker.getBoundingBox(); - if (!r.isEmpty()) { - // Invalidate the rectangular region encompassing the gesture. This is needed because - // past points along the gesture will fade and gradually disappear. - final KeyPreviewDrawParams params = mKeyPreviewDrawParams; - mInvalidatedGesturesRect.set(r); - mInvalidatedGesturesRect.offset(params.mCoordinates[0], params.mCoordinates[1]); - mInvalidatedGesturesRect.inset(-GESTURE_DRAWING_WIDTH, -GESTURE_DRAWING_WIDTH); - mPreviewPlacer.invalidate(mInvalidatedGesturesRect); - } else { - mPreviewPlacer.invalidate(); - } + locatePreviewPlacerView(); + mPreviewPlacerView.invalidatePointer(tracker); } @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16 @@ -1055,9 +1062,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mInvalidatedKeys.add(key); final int x = key.mX + getPaddingLeft(); final int y = key.mY + getPaddingTop(); - mInvalidatedKeysRect.union(x, y, x + key.mWidth, y + key.mHeight); + mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); mBufferNeedsUpdate = true; - invalidate(mInvalidatedKeysRect); + invalidate(mWorkingRect); } public void closing() { @@ -1082,12 +1089,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); closing(); - if (mPreviewPlacer != null) { - mPreviewPlacer.removeAllViews(); - } - if (mBuffer != null) { - mBuffer.recycle(); - mBuffer = null; - } + mPreviewPlacerView.removeAllViews(); + freeOffscreenBuffer(); } } diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 8c234e4e6..79459083f 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -43,6 +43,7 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; @@ -153,8 +154,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key } break; case MSG_TYPING_STATE_EXPIRED: - cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator, - keyboardView.mAltCodeKeyWhileTypingFadeinAnimator); + startWhileTypingFadeinAnimation(keyboardView); break; } } @@ -228,7 +228,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key removeMessages(MSG_LONGPRESS_KEY); } - public static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, + private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, final ObjectAnimator animatorToStart) { float startFraction = 0.0f; if (animatorToCancel.isStarted()) { @@ -240,18 +240,39 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key animatorToStart.setCurrentPlayTime(startTime); } + private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) { + cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator, + keyboardView.mAltCodeKeyWhileTypingFadeinAnimator); + } + + private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) { + cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator, + keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator); + } + @Override - public void startTypingStateTimer() { + public void startTypingStateTimer(Key typedKey) { + if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) { + return; + } + final boolean isTyping = isTypingState(); removeMessages(MSG_TYPING_STATE_EXPIRED); + final MainKeyboardView keyboardView = getOuterInstance(); + + // When user hits the space or the enter key, just cancel the while-typing timer. + final int typedCode = typedKey.mCode; + if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) { + startWhileTypingFadeinAnimation(keyboardView); + return; + } + sendMessageDelayed( obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout); if (isTyping) { return; } - final MainKeyboardView keyboardView = getOuterInstance(); - cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator, - keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator); + startWhileTypingFadeoutAnimation(keyboardView); } @Override @@ -461,7 +482,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); - PointerTracker.setKeyDetector(mKeyDetector, mGestureInputEnabled); + PointerTracker.setKeyDetector(mKeyDetector, mShouldHandleGesture); mTouchScreenRegulator.setKeyboard(keyboard); mMoreKeysPanelCache.clear(); @@ -479,6 +500,14 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard); } + @Override + public void setGestureHandlingMode(final boolean shouldHandleGesture, + boolean drawsGesturePreviewTrail, boolean drawsGestureFloatingPreviewText) { + super.setGestureHandlingMode(shouldHandleGesture, drawsGesturePreviewTrail, + drawsGestureFloatingPreviewText); + PointerTracker.setKeyDetector(mKeyDetector, shouldHandleGesture); + } + /** * Returns whether the device has distinct multi-touch panel. * @return true if the device has distinct multi-touch panel. @@ -491,23 +520,6 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key mHasDistinctMultitouch = hasDistinctMultitouch; } - /** - * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key - * codes for adjacent keys. When disabled, only the primary key code will be - * reported. - * @param enabled whether or not the proximity correction is enabled - */ - public void setProximityCorrectionEnabled(boolean enabled) { - mKeyDetector.setProximityCorrectionEnabled(enabled); - } - - /** - * Returns true if proximity correction is enabled. - */ - public boolean isProximityCorrectionEnabled() { - return mKeyDetector.isProximityCorrectionEnabled(); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java index cd4e3001e..a183546dd 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java @@ -39,7 +39,11 @@ public class MoreKeysDetector extends KeyDetector { Key nearestKey = null; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; - for (final Key key : getKeyboard().mKeys) { + final Keyboard keyboard = getKeyboard(); + if (keyboard == null) { + throw new NullPointerException("Keyboard isn't set"); + } + for (final Key key : keyboard.mKeys) { final int dist = key.squaredDistanceToEdge(touchX, touchY); if (dist < nearestDist) { nearestKey = key; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 4a5ecf986..e7e11f481 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Rect; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; @@ -42,8 +41,8 @@ public class PointerTracker { private static final boolean DEBUG_LISTENER = false; private static boolean DEBUG_MODE = LatinImeLogger.sDBG; - // TODO: There should be an option to turn on/off the gesture input. - private static boolean sIsGestureEnabled = true; + /** True if {@link PointerTracker}s should handle gesture events. */ + private static boolean sShouldHandleGesture = false; private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec @@ -83,7 +82,7 @@ public class PointerTracker { } public interface TimerProxy { - public void startTypingStateTimer(); + public void startTypingStateTimer(Key typedKey); public boolean isTypingState(); public void startKeyRepeatTimer(PointerTracker tracker); public void startLongPressTimer(PointerTracker tracker); @@ -96,7 +95,7 @@ public class PointerTracker { public static class Adapter implements TimerProxy { @Override - public void startTypingStateTimer() {} + public void startTypingStateTimer(Key typedKey) {} @Override public boolean isTypingState() { return false; } @Override @@ -199,7 +198,7 @@ public class PointerTracker { sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT); - updateGestureInputEnabledState(null, false /* gestureInputEnabled */); + updateGestureHandlingMode(null, false /* shouldHandleGesture */); } public static void setParameters(MainKeyboardView.PointerTrackerParams params) { @@ -208,14 +207,13 @@ public class PointerTracker { params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); } - private static void updateGestureInputEnabledState(Keyboard keyboard, - boolean gestureInputEnabled) { - if (!gestureInputEnabled + private static void updateGestureHandlingMode(Keyboard keyboard, boolean shouldHandleGesture) { + if (!shouldHandleGesture || AccessibilityUtils.getInstance().isTouchExplorationEnabled() || (keyboard != null && keyboard.mId.passwordInput())) { - sIsGestureEnabled = false; + sShouldHandleGesture = false; } else { - sIsGestureEnabled = true; + sShouldHandleGesture = true; } } @@ -243,7 +241,7 @@ public class PointerTracker { } } - public static void setKeyDetector(KeyDetector keyDetector, boolean gestureInputEnabledByUser) { + public static void setKeyDetector(KeyDetector keyDetector, boolean shouldHandleGesture) { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); @@ -252,7 +250,7 @@ public class PointerTracker { tracker.mKeyboardLayoutHasBeenChanged = true; } final Keyboard keyboard = keyDetector.getKeyboard(); - updateGestureInputEnabledState(keyboard, gestureInputEnabledByUser); + updateGestureHandlingMode(keyboard, shouldHandleGesture); } public static void dismissAllKeyPreviews() { @@ -297,17 +295,6 @@ public class PointerTracker { sAggregratedPointers.reset(); } - // TODO: To handle multi-touch gestures we may want to move this method to - // {@link PointerTrackerQueue}. - public static void drawGestureTrailForAllPointerTrackers(Canvas canvas, Paint paint) { - final int trackersSize = sTrackers.size(); - for (int i = 0; i < trackersSize; ++i) { - final PointerTracker tracker = sTrackers.get(i); - tracker.mGestureStroke.drawGestureTrail(canvas, paint, tracker.getLastX(), - tracker.getLastY()); - } - } - private PointerTracker(int id, KeyEventHandler handler) { if (handler == null) throw new NullPointerException(); @@ -342,9 +329,7 @@ public class PointerTracker { mListener.onPressKey(key.mCode); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; mKeyboardLayoutHasBeenChanged = false; - if (!key.altCodeWhileTyping() && !key.isModifier()) { - mTimerProxy.startTypingStateTimer(); - } + mTimerProxy.startTypingStateTimer(key); return keyboardLayoutHasBeenChanged; } return false; @@ -423,7 +408,7 @@ public class PointerTracker { if (mDrawingProxy != null) { setReleasedKeyGraphics(mCurrentKey); } - mCurrentKey = newKey; + // Keep {@link #mCurrentKey} that comes from previous keyboard. } final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4; mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; @@ -525,6 +510,12 @@ public class PointerTracker { mDrawingProxy.invalidateKey(key); } + public void drawGestureTrail(Canvas canvas, Paint paint) { + if (mInGesture) { + mGestureStroke.drawGestureTrail(canvas, paint, mLastX, mLastY); + } + } + public int getLastX() { return mLastX; } @@ -536,9 +527,6 @@ public class PointerTracker { public long getDownTime() { return mDownTime; } - public Rect getBoundingBox() { - return mGestureStroke.getBoundingBox(); - } private Key onDownKey(int x, int y, long eventTime) { mDownTime = eventTime; @@ -669,8 +657,8 @@ public class PointerTracker { if (queue != null && queue.size() == 1) { mIsPossibleGesture = false; // A gesture should start only from the letter key. - if (sIsGestureEnabled && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel && key != null - && Keyboard.isLetterCode(key.mCode)) { + if (sShouldHandleGesture && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel + && key != null && Keyboard.isLetterCode(key.mCode)) { mIsPossibleGesture = true; // TODO: pointer times should be relative to first down even in entire batch input // instead of resetting to 0 for each new down event. @@ -714,7 +702,7 @@ public class PointerTracker { private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime, boolean isHistorical, Key key) { final int gestureTime = (int)(eventTime - tracker.getDownTime()); - if (sIsGestureEnabled && mIsPossibleGesture) { + if (sShouldHandleGesture && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; stroke.addPoint(x, y, gestureTime, isHistorical); if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) { @@ -804,13 +792,16 @@ public class PointerTracker { final int dx = x - lastX; final int dy = y - lastY; final int lastMoveSquared = dx * dx + dy * dy; + // TODO: Should find a way to balance gesture detection and this hack. if (sNeedsPhantomSuddenMoveEventHack - && lastMoveSquared >= mKeyQuarterWidthSquared) { + && lastMoveSquared >= mKeyQuarterWidthSquared + && !mIsPossibleGesture) { if (DEBUG_MODE) { Log.w(TAG, String.format("onMoveEvent:" + " phantom sudden move event is translated to " + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y)); } + // TODO: This should be moved to outside of this nested if-clause? if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); } @@ -826,7 +817,9 @@ public class PointerTracker { && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { onUpEventInternal(x, y, eventTime); } - mKeyAlreadyProcessed = true; + if (!mIsPossibleGesture) { + mKeyAlreadyProcessed = true; + } setReleasedKeyGraphics(oldKey); } } @@ -842,7 +835,9 @@ public class PointerTracker { if (mIsAllowedSlidingKeyInput) { onMoveToNewKey(key, x, y); } else { - mKeyAlreadyProcessed = true; + if (!mIsPossibleGesture) { + mKeyAlreadyProcessed = true; + } } } } @@ -881,6 +876,7 @@ public class PointerTracker { private void onUpEventInternal(int x, int y, long eventTime) { mTimerProxy.cancelKeyTimers(); mIsInSlidingKeyInput = false; + mIsPossibleGesture = false; // Release the last pressed key. setReleasedKeyGraphics(mCurrentKey); if (mIsShowingMoreKeysPanel) { @@ -958,9 +954,7 @@ public class PointerTracker { public void onRegisterKey(Key key) { if (key != null) { detectAndSendKey(key, key.mX, key.mY); - if (!key.altCodeWhileTyping() && !key.isModifier()) { - mTimerProxy.startTypingStateTimer(); - } + mTimerProxy.startTypingStateTimer(key); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index c16b70ef0..28d6c1d07 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -16,7 +16,6 @@ package com.android.inputmethod.keyboard.internal; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Rect; import android.util.FloatMath; import com.android.inputmethod.latin.Constants; @@ -41,7 +40,6 @@ public class GestureStroke { private int mMinGestureLength; private int mMinGestureLengthWhileInGesture; private int mMinGestureSampleLength; - private final Rect mDrawingRect = new Rect(); // TODO: Move some of these to resource. private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f; @@ -90,7 +88,6 @@ public class GestureStroke { mEventTimes.setLength(0); mXCoordinates.setLength(0); mYCoordinates.setLength(0); - mDrawingRect.setEmpty(); } private void updateLastPoint(final int x, final int y, final int time) { @@ -198,8 +195,4 @@ public class GestureStroke { } } } - - public Rect getBoundingBox() { - return mDrawingRect; - } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 53261205d..94a7b826f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -84,6 +84,19 @@ public class KeySpecParser { } mIconId = getIconId(moreKeySpec); } + + @Override + public String toString() { + final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel + : PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId)); + final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText + : Keyboard.printableCode(mCode)); + if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) { + return output; + } else { + return label + "|" + output; + } + } } private KeySpecParser() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index d732fc061..bec0f1fef 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -131,101 +131,102 @@ public final class KeyboardTextsSet { /* 23 */ "more_keys_for_nordic_row2_10", /* 24 */ "more_keys_for_nordic_row2_11", /* 25 */ "keylabel_for_east_slavic_row1_9", - /* 26 */ "keylabel_for_east_slavic_row2_1", - /* 27 */ "keylabel_for_east_slavic_row3_5", - /* 28 */ "more_keys_for_cyrillic_u", - /* 29 */ "more_keys_for_cyrillic_ye", - /* 30 */ "more_keys_for_cyrillic_en", - /* 31 */ "more_keys_for_cyrillic_ha", - /* 32 */ "more_keys_for_east_slavic_row2_1", - /* 33 */ "more_keys_for_cyrillic_o", - /* 34 */ "more_keys_for_cyrillic_soft_sign", - /* 35 */ "keylabel_for_south_slavic_row1_6", - /* 36 */ "keylabel_for_south_slavic_row2_11", - /* 37 */ "keylabel_for_south_slavic_row3_1", - /* 38 */ "keylabel_for_south_slavic_row3_8", - /* 39 */ "more_keys_for_cyrillic_ie", - /* 40 */ "more_keys_for_cyrillic_i", - /* 41 */ "more_keys_for_single_quote", - /* 42 */ "more_keys_for_double_quote", - /* 43 */ "more_keys_for_tablet_double_quote", - /* 44 */ "more_keys_for_currency_dollar", - /* 45 */ "more_keys_for_currency_euro", - /* 46 */ "more_keys_for_currency_pound", - /* 47 */ "more_keys_for_currency_general", - /* 48 */ "more_keys_for_punctuation", - /* 49 */ "more_keys_for_star", - /* 50 */ "more_keys_for_bullet", - /* 51 */ "more_keys_for_plus", - /* 52 */ "more_keys_for_left_parenthesis", - /* 53 */ "more_keys_for_right_parenthesis", - /* 54 */ "more_keys_for_less_than", - /* 55 */ "more_keys_for_greater_than", - /* 56 */ "more_keys_for_arabic_diacritics", - /* 57 */ "keyhintlabel_for_arabic_diacritics", - /* 58 */ "keylabel_for_symbols_1", - /* 59 */ "keylabel_for_symbols_2", - /* 60 */ "keylabel_for_symbols_3", - /* 61 */ "keylabel_for_symbols_4", - /* 62 */ "keylabel_for_symbols_5", - /* 63 */ "keylabel_for_symbols_6", - /* 64 */ "keylabel_for_symbols_7", - /* 65 */ "keylabel_for_symbols_8", - /* 66 */ "keylabel_for_symbols_9", - /* 67 */ "keylabel_for_symbols_0", - /* 68 */ "additional_more_keys_for_symbols_1", - /* 69 */ "additional_more_keys_for_symbols_2", - /* 70 */ "additional_more_keys_for_symbols_3", - /* 71 */ "additional_more_keys_for_symbols_4", - /* 72 */ "additional_more_keys_for_symbols_5", - /* 73 */ "additional_more_keys_for_symbols_6", - /* 74 */ "additional_more_keys_for_symbols_7", - /* 75 */ "additional_more_keys_for_symbols_8", - /* 76 */ "additional_more_keys_for_symbols_9", - /* 77 */ "additional_more_keys_for_symbols_0", - /* 78 */ "more_keys_for_symbols_1", - /* 79 */ "more_keys_for_symbols_2", - /* 80 */ "more_keys_for_symbols_3", - /* 81 */ "more_keys_for_symbols_4", - /* 82 */ "more_keys_for_symbols_5", - /* 83 */ "more_keys_for_symbols_6", - /* 84 */ "more_keys_for_symbols_7", - /* 85 */ "more_keys_for_symbols_8", - /* 86 */ "more_keys_for_symbols_9", - /* 87 */ "more_keys_for_symbols_0", - /* 88 */ "keylabel_for_comma", - /* 89 */ "more_keys_for_comma", - /* 90 */ "keylabel_for_symbols_question", - /* 91 */ "keylabel_for_symbols_semicolon", - /* 92 */ "keylabel_for_symbols_percent", - /* 93 */ "more_keys_for_symbols_exclamation", - /* 94 */ "more_keys_for_symbols_question", - /* 95 */ "more_keys_for_symbols_semicolon", - /* 96 */ "more_keys_for_symbols_percent", - /* 97 */ "keylabel_for_tablet_comma", - /* 98 */ "keyhintlabel_for_tablet_comma", - /* 99 */ "more_keys_for_tablet_comma", - /* 100 */ "keyhintlabel_for_tablet_period", - /* 101 */ "more_keys_for_tablet_period", - /* 102 */ "keylabel_for_apostrophe", - /* 103 */ "keyhintlabel_for_apostrophe", - /* 104 */ "more_keys_for_apostrophe", - /* 105 */ "more_keys_for_am_pm", - /* 106 */ "settings_as_more_key", - /* 107 */ "shortcut_as_more_key", - /* 108 */ "action_next_as_more_key", - /* 109 */ "action_previous_as_more_key", - /* 110 */ "label_to_more_symbol_key", - /* 111 */ "label_to_more_symbol_for_tablet_key", - /* 112 */ "label_tab_key", - /* 113 */ "label_to_phone_numeric_key", - /* 114 */ "label_to_phone_symbols_key", - /* 115 */ "label_time_am", - /* 116 */ "label_time_pm", - /* 117 */ "label_to_symbol_key_pcqwerty", - /* 118 */ "keylabel_for_popular_domain", - /* 119 */ "more_keys_for_popular_domain", - /* 120 */ "more_keys_for_smiley", + /* 26 */ "keylabel_for_east_slavic_row1_12", + /* 27 */ "keylabel_for_east_slavic_row2_1", + /* 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 */ "more_keys_for_single_quote", + /* 43 */ "more_keys_for_double_quote", + /* 44 */ "more_keys_for_tablet_double_quote", + /* 45 */ "more_keys_for_currency_dollar", + /* 46 */ "more_keys_for_currency_euro", + /* 47 */ "more_keys_for_currency_pound", + /* 48 */ "more_keys_for_currency_general", + /* 49 */ "more_keys_for_punctuation", + /* 50 */ "more_keys_for_star", + /* 51 */ "more_keys_for_bullet", + /* 52 */ "more_keys_for_plus", + /* 53 */ "more_keys_for_left_parenthesis", + /* 54 */ "more_keys_for_right_parenthesis", + /* 55 */ "more_keys_for_less_than", + /* 56 */ "more_keys_for_greater_than", + /* 57 */ "more_keys_for_arabic_diacritics", + /* 58 */ "keyhintlabel_for_arabic_diacritics", + /* 59 */ "keylabel_for_symbols_1", + /* 60 */ "keylabel_for_symbols_2", + /* 61 */ "keylabel_for_symbols_3", + /* 62 */ "keylabel_for_symbols_4", + /* 63 */ "keylabel_for_symbols_5", + /* 64 */ "keylabel_for_symbols_6", + /* 65 */ "keylabel_for_symbols_7", + /* 66 */ "keylabel_for_symbols_8", + /* 67 */ "keylabel_for_symbols_9", + /* 68 */ "keylabel_for_symbols_0", + /* 69 */ "additional_more_keys_for_symbols_1", + /* 70 */ "additional_more_keys_for_symbols_2", + /* 71 */ "additional_more_keys_for_symbols_3", + /* 72 */ "additional_more_keys_for_symbols_4", + /* 73 */ "additional_more_keys_for_symbols_5", + /* 74 */ "additional_more_keys_for_symbols_6", + /* 75 */ "additional_more_keys_for_symbols_7", + /* 76 */ "additional_more_keys_for_symbols_8", + /* 77 */ "additional_more_keys_for_symbols_9", + /* 78 */ "additional_more_keys_for_symbols_0", + /* 79 */ "more_keys_for_symbols_1", + /* 80 */ "more_keys_for_symbols_2", + /* 81 */ "more_keys_for_symbols_3", + /* 82 */ "more_keys_for_symbols_4", + /* 83 */ "more_keys_for_symbols_5", + /* 84 */ "more_keys_for_symbols_6", + /* 85 */ "more_keys_for_symbols_7", + /* 86 */ "more_keys_for_symbols_8", + /* 87 */ "more_keys_for_symbols_9", + /* 88 */ "more_keys_for_symbols_0", + /* 89 */ "keylabel_for_comma", + /* 90 */ "more_keys_for_comma", + /* 91 */ "keylabel_for_symbols_question", + /* 92 */ "keylabel_for_symbols_semicolon", + /* 93 */ "keylabel_for_symbols_percent", + /* 94 */ "more_keys_for_symbols_exclamation", + /* 95 */ "more_keys_for_symbols_question", + /* 96 */ "more_keys_for_symbols_semicolon", + /* 97 */ "more_keys_for_symbols_percent", + /* 98 */ "keylabel_for_tablet_comma", + /* 99 */ "keyhintlabel_for_tablet_comma", + /* 100 */ "more_keys_for_tablet_comma", + /* 101 */ "keyhintlabel_for_tablet_period", + /* 102 */ "more_keys_for_tablet_period", + /* 103 */ "keylabel_for_apostrophe", + /* 104 */ "keyhintlabel_for_apostrophe", + /* 105 */ "more_keys_for_apostrophe", + /* 106 */ "more_keys_for_am_pm", + /* 107 */ "settings_as_more_key", + /* 108 */ "shortcut_as_more_key", + /* 109 */ "action_next_as_more_key", + /* 110 */ "action_previous_as_more_key", + /* 111 */ "label_to_more_symbol_key", + /* 112 */ "label_to_more_symbol_for_tablet_key", + /* 113 */ "label_tab_key", + /* 114 */ "label_to_phone_numeric_key", + /* 115 */ "label_to_phone_symbols_key", + /* 116 */ "label_time_am", + /* 117 */ "label_time_pm", + /* 118 */ "label_to_symbol_key_pcqwerty", + /* 119 */ "keylabel_for_popular_domain", + /* 120 */ "more_keys_for_popular_domain", + /* 121 */ "more_keys_for_smiley", }; private static final String EMPTY = ""; @@ -236,41 +237,41 @@ 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, - /* ~40 */ - /* 41 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B", + EMPTY, EMPTY, EMPTY, + /* ~41 */ + /* 42 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_double_quote">!fixedColumnOrder!6,“,”,„,‟,«,»</string> - /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB", + /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«,»,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", + /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", // U+00A2: "¢" CENT SIGN // U+00A3: "£" POUND SIGN // U+20AC: "€" EURO SIGN // U+00A5: "¥" YEN SIGN // U+20B1: "₱" PESO SIGN - /* 44 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", - /* 45 */ "\u00A2,\u00A3,$,\u00A5,\u20B1", - /* 46 */ "\u00A2,$,\u20AC,\u00A5,\u20B1", - /* 47 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1", - /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + /* 46 */ "\u00A2,\u00A3,$,\u00A5,\u20B1", + /* 47 */ "\u00A2,$,\u20AC,\u00A5,\u20B1", + /* 48 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1", + /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR - /* 49 */ "\u2020,\u2021,\u2605", + /* 50 */ "\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 - /* 50 */ "\u266A,\u2665,\u2660,\u2666,\u2663", + /* 51 */ "\u266A,\u2665,\u2660,\u2666,\u2663", // U+00B1: "±" PLUS-MINUS SIGN - /* 51 */ "\u00B1", + /* 52 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 52 */ "!fixedColumnOrder!3,<,{,[", - /* 53 */ "!fixedColumnOrder!3,>,},]", + /* 53 */ "!fixedColumnOrder!3,<,{,[", + /* 54 */ "!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 @@ -286,95 +287,148 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 54 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", - /* 55 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", - /* 56 */ EMPTY, + /* 55 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", + /* 56 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", /* 57 */ EMPTY, - /* 58 */ "1", - /* 59 */ "2", - /* 60 */ "3", - /* 61 */ "4", - /* 62 */ "5", - /* 63 */ "6", - /* 64 */ "7", - /* 65 */ "8", - /* 66 */ "9", - /* 67 */ "0", - /* 68~ */ + /* 58 */ EMPTY, + /* 59 */ "1", + /* 60 */ "2", + /* 61 */ "3", + /* 62 */ "4", + /* 63 */ "5", + /* 64 */ "6", + /* 65 */ "7", + /* 66 */ "8", + /* 67 */ "9", + /* 68 */ "0", + /* 69~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~77 */ + /* ~78 */ // 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 - /* 78 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", + /* 79 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", // U+00B2: "²" SUPERSCRIPT TWO // U+2154: "⅔" VULGAR FRACTION TWO THIRDS - /* 79 */ "\u00B2,\u2154", + /* 80 */ "\u00B2,\u2154", // U+00B3: "³" SUPERSCRIPT THREE // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS - /* 80 */ "\u00B3,\u00BE,\u215C", + /* 81 */ "\u00B3,\u00BE,\u215C", // U+2074: "⁴" SUPERSCRIPT FOUR - /* 81 */ "\u2074", + /* 82 */ "\u2074", // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS - /* 82 */ "\u215D", - /* 83 */ EMPTY, + /* 83 */ "\u215D", + /* 84 */ EMPTY, // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS - /* 84 */ "\u215E", - /* 85 */ EMPTY, + /* 85 */ "\u215E", /* 86 */ EMPTY, + /* 87 */ EMPTY, // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N // U+2205: "∅" EMPTY SET - /* 87 */ "\u207F,\u2205", - /* 88 */ ",", - /* 89 */ EMPTY, - /* 90 */ "?", - /* 91 */ ";", - /* 92 */ "%", + /* 88 */ "\u207F,\u2205", + /* 89 */ ",", + /* 90 */ EMPTY, + /* 91 */ "?", + /* 92 */ ";", + /* 93 */ "%", // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 93 */ "\u00A1", + /* 94 */ "\u00A1", // U+00BF: "¿" INVERTED QUESTION MARK - /* 94 */ "\u00BF", - /* 95 */ EMPTY, + /* 95 */ "\u00BF", + /* 96 */ EMPTY, // U+2030: "‰" PER MILLE SIGN - /* 96 */ "\u2030", - /* 97 */ ",", - /* 98 */ "!", + /* 97 */ "\u2030", + /* 98 */ ",", /* 99 */ "!", - /* 100 */ "?", + /* 100 */ "!", /* 101 */ "?", - /* 102 */ "\'", - /* 103 */ "\"", + /* 102 */ "?", + /* 103 */ "\'", /* 104 */ "\"", - /* 105 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", - /* 106 */ "!icon/settings_key|!code/key_settings", - /* 107 */ "!icon/shortcut_key|!code/key_shortcut", - /* 108 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", - /* 109 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + /* 105 */ "\"", + /* 106 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", + /* 107 */ "!icon/settings_key|!code/key_settings", + /* 108 */ "!icon/shortcut_key|!code/key_shortcut", + /* 109 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", + /* 110 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", // Label for "switch to more symbol" modifier key. Must be short to fit on key! - /* 110 */ "= \\ <", + /* 111 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 111 */ "~ \\ {", + /* 112 */ "~ \\ {", // Label for "Tab" key. Must be short to fit on key! - /* 112 */ "Tab", + /* 113 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 113 */ "123", + /* 114 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 114 */ "\uFF0A\uFF03", + /* 115 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 115 */ "AM", + /* 116 */ "AM", // Key label for "post meridiem" - /* 116 */ "PM", + /* 117 */ "PM", // Label for "switch to symbols" key on PC QWERTY layout - /* 117 */ "Sym", - /* 118 */ ".com", + /* 118 */ "Sym", + /* 119 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 119 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 120 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 120 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 121 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + }; + + /* Language af: Afrikaans */ + private static final String[] LANGUAGE_af = { + // This is the same as Dutch except more keys of y and demoting vowels with diaeresis. + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + /* 0 */ "\u00E1,\u00E2,\u00E4,\u00E0,\u00E6,\u00E3,\u00E5,\u0101", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK + // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + /* 1 */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + // U+0133: "ij" LATIN SMALL LIGATURE IJ + /* 2 */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133", + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + /* 3 */ "\u00F3,\u00F4,\u00F6,\u00F2,\u00F5,\u0153,\u00F8,\u014D", + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + /* 4 */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", + /* 5 */ null, + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE + /* 6 */ "\u00F1,\u0144", + /* 7 */ null, + // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE + // U+0133: "ij" LATIN SMALL LIGATURE IJ + /* 8 */ "\u00FD,\u0133", }; /* Language ar: Arabic */ @@ -382,33 +436,33 @@ 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, + /* ~42 */ // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_double_quote">“,”,„,‟,«|»,»|«</string> - /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB", + /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 44~ */ + /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", + /* 45~ */ null, null, null, null, - /* ~47 */ + /* ~48 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 49 */ "\u2605,\u066D", + /* 50 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 50 */ "\u266A", - /* 51 */ null, + /* 51 */ "\u266A", + /* 52 */ 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 - /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 54 */ "!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 @@ -424,8 +478,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0654: "ٔ" ARABIC HAMZA ABOVE // U+0652: "ْ" ARABIC SUKUN @@ -441,64 +495,64 @@ public final class KeyboardTextsSet { // U+064E: "َ" ARABIC FATHA // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. - /* 56 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640", - /* 57 */ "\u0651", + /* 57 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640", + /* 58 */ "\u0651", // U+0661: "١" ARABIC-INDIC DIGIT ONE - /* 58 */ "\u0661", + /* 59 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO - /* 59 */ "\u0662", + /* 60 */ "\u0662", // U+0663: "٣" ARABIC-INDIC DIGIT THREE - /* 60 */ "\u0663", + /* 61 */ "\u0663", // U+0664: "٤" ARABIC-INDIC DIGIT FOUR - /* 61 */ "\u0664", + /* 62 */ "\u0664", // U+0665: "٥" ARABIC-INDIC DIGIT FIVE - /* 62 */ "\u0665", + /* 63 */ "\u0665", // U+0666: "٦" ARABIC-INDIC DIGIT SIX - /* 63 */ "\u0666", + /* 64 */ "\u0666", // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN - /* 64 */ "\u0667", + /* 65 */ "\u0667", // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT - /* 65 */ "\u0668", + /* 66 */ "\u0668", // U+0669: "٩" ARABIC-INDIC DIGIT NINE - /* 66 */ "\u0669", + /* 67 */ "\u0669", // U+0660: "٠" ARABIC-INDIC DIGIT ZERO - /* 67 */ "\u0660", - /* 68 */ "1", - /* 69 */ "2", - /* 70 */ "3", - /* 71 */ "4", - /* 72 */ "5", - /* 73 */ "6", - /* 74 */ "7", - /* 75 */ "8", - /* 76 */ "9", + /* 68 */ "\u0660", + /* 69 */ "1", + /* 70 */ "2", + /* 71 */ "3", + /* 72 */ "4", + /* 73 */ "5", + /* 74 */ "6", + /* 75 */ "7", + /* 76 */ "8", + /* 77 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 77 */ "0,\u066B,\u066C", - /* 78~ */ + /* 78 */ "0,\u066B,\u066C", + /* 79~ */ null, null, null, null, null, null, null, null, null, null, - /* ~87 */ + /* ~88 */ // U+060C: "،" ARABIC COMMA - /* 88 */ "\u060C", - /* 89 */ "\\,", - /* 90 */ "\u061F", - /* 91 */ "\u061B", + /* 89 */ "\u060C", + /* 90 */ "\\,", + /* 91 */ "\u061F", + /* 92 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 92 */ "\u066A", - /* 93 */ null, - /* 94 */ "?", - /* 95 */ ";", + /* 93 */ "\u066A", + /* 94 */ null, + /* 95 */ "?", + /* 96 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 96 */ "\\%,\u2030", - /* 97~ */ + /* 97 */ "\\%,\u2030", + /* 98~ */ null, null, null, null, null, - /* ~101 */ + /* ~102 */ // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK - /* 102 */ "\u060C", - /* 103 */ "\u061F", - /* 104 */ "\u061F,\u061B,!,:,-,/,\',\"", + /* 103 */ "\u060C", + /* 104 */ "\u061F", + /* 105 */ "\u061F,\u061B,!,:,-,/,\',\"", }; /* Language be: Belarusian */ @@ -509,19 +563,24 @@ public final class KeyboardTextsSet { /* ~24 */ // U+045E: "ў" CYRILLIC SMALL LETTER SHORT U /* 25 */ "\u045E", + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 26 */ "\u0451", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* 26 */ "\u044B", + /* 27 */ "\u044B", + // U+044D: "э" CYRILLIC SMALL LETTER E + /* 28 */ "\u044D", // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I - /* 27 */ "\u0456", - /* 28~ */ - null, null, null, - /* ~30 */ - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 31 */ "\u044A", - /* 32 */ null, - /* 33 */ null, + /* 29 */ "\u0456", + /* 30~ */ + null, null, null, null, null, + /* ~34 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 34 */ "\u044A", + /* 35 */ "\u044A", + /* 36~ */ + null, null, null, null, + /* ~39 */ + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 40 */ "\u0451", }; /* Language ca: Catalan */ @@ -854,22 +913,22 @@ 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, - /* ~47 */ + null, null, null, null, null, null, null, null, null, null, null, + /* ~48 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 48 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", - /* 49~ */ + /* 49 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", + /* 50~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~98 */ + /* ~99 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 99 */ "!,\u00A1", - /* 100 */ null, + /* 100 */ "!,\u00A1", + /* 101 */ null, // U+00BF: "¿" INVERTED QUESTION MARK - /* 101 */ "?,\u00BF", + /* 102 */ "?,\u00BF", }; /* Language et: Estonian */ @@ -977,33 +1036,33 @@ 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, + /* ~42 */ // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_double_quote">“,”,„,‟,«|»,»|«</string> - /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'", + /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 44~ */ + /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", + /* 45~ */ null, null, null, null, - /* ~47 */ + /* ~48 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 49 */ "\u2605,\u066D", + /* 50 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 50 */ "\u266A", - /* 51 */ null, + /* 51 */ "\u266A", + /* 52 */ 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 - /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 54 */ "!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 @@ -1019,8 +1078,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", - /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", + /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", + /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0652: "ْ" ARABIC SUKUN // U+0651: "ّ" ARABIC SHADDA @@ -1036,68 +1095,68 @@ public final class KeyboardTextsSet { // U+064E: "َ" ARABIC FATHA // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. - /* 56 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640", - /* 57 */ "\u064B", + /* 57 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640", + /* 58 */ "\u064B", // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE - /* 58 */ "\u06F1", + /* 59 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO - /* 59 */ "\u06F2", + /* 60 */ "\u06F2", // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE - /* 60 */ "\u06F3", + /* 61 */ "\u06F3", // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR - /* 61 */ "\u06F4", + /* 62 */ "\u06F4", // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE - /* 62 */ "\u06F5", + /* 63 */ "\u06F5", // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX - /* 63 */ "\u06F6", + /* 64 */ "\u06F6", // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN - /* 64 */ "\u06F7", + /* 65 */ "\u06F7", // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT - /* 65 */ "\u06F8", + /* 66 */ "\u06F8", // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE - /* 66 */ "\u06F9", + /* 67 */ "\u06F9", // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO - /* 67 */ "\u06F0", - /* 68 */ "1", - /* 69 */ "2", - /* 70 */ "3", - /* 71 */ "4", - /* 72 */ "5", - /* 73 */ "6", - /* 74 */ "7", - /* 75 */ "8", - /* 76 */ "9", + /* 68 */ "\u06F0", + /* 69 */ "1", + /* 70 */ "2", + /* 71 */ "3", + /* 72 */ "4", + /* 73 */ "5", + /* 74 */ "6", + /* 75 */ "7", + /* 76 */ "8", + /* 77 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 77 */ "0,\u066B,\u066C", - /* 78~ */ + /* 78 */ "0,\u066B,\u066C", + /* 79~ */ null, null, null, null, null, null, null, null, null, null, - /* ~87 */ + /* ~88 */ // U+060C: "،" ARABIC COMMA - /* 88 */ "\u060C", - /* 89 */ "\\,", - /* 90 */ "\u061F", - /* 91 */ "\u061B", + /* 89 */ "\u060C", + /* 90 */ "\\,", + /* 91 */ "\u061F", + /* 92 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 92 */ "\u066A", - /* 93 */ null, - /* 94 */ "?", - /* 95 */ ";", + /* 93 */ "\u066A", + /* 94 */ null, + /* 95 */ "?", + /* 96 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 96 */ "\\%,\u2030", + /* 97 */ "\\%,\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 - /* 97 */ "\u060C", - /* 98 */ "!", - /* 99 */ "!,\\,", - /* 100 */ "\u061F", - /* 101 */ "\u061F,?", - /* 102 */ "\u060C", - /* 103 */ "\u061F", - /* 104 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 98 */ "\u060C", + /* 99 */ "!", + /* 100 */ "!,\\,", + /* 101 */ "\u061F", + /* 102 */ "\u061F,?", + /* 103 */ "\u060C", + /* 104 */ "\u061F", + /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", }; /* Language fi: Finnish */ @@ -1206,38 +1265,38 @@ 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, - /* ~57 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~58 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 58 */ "\u0967", + /* 59 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 59 */ "\u0968", + /* 60 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 60 */ "\u0969", + /* 61 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 61 */ "\u096A", + /* 62 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 62 */ "\u096B", + /* 63 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 63 */ "\u096C", + /* 64 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 64 */ "\u096D", + /* 65 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 65 */ "\u096E", + /* 66 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 66 */ "\u096F", + /* 67 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 67 */ "\u0966", - /* 68 */ "1", - /* 69 */ "2", - /* 70 */ "3", - /* 71 */ "4", - /* 72 */ "5", - /* 73 */ "6", - /* 74 */ "7", - /* 75 */ "8", - /* 76 */ "9", - /* 77 */ "0", + /* 68 */ "\u0966", + /* 69 */ "1", + /* 70 */ "2", + /* 71 */ "3", + /* 72 */ "4", + /* 73 */ "5", + /* 74 */ "6", + /* 75 */ "7", + /* 76 */ "8", + /* 77 */ "9", + /* 78 */ "0", }; /* Language hr: Croatian */ @@ -1425,27 +1484,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, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~42 */ // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_double_quote">“,”,„,‟,«|»,»|«</string> - /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB", + /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 44~ */ + /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", + /* 45~ */ null, null, null, null, null, - /* ~48 */ + /* ~49 */ // U+2605: "★" BLACK STAR - /* 49 */ "\u2605", - /* 50 */ null, + /* 50 */ "\u2605", + /* 51 */ null, // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 51 */ "\u00B1,\uFB29", + /* 52 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 52 */ "!fixedColumnOrder!3,<|>,{|},[|]", - /* 53 */ "!fixedColumnOrder!3,>|<,}|{,]|[", + /* 53 */ "!fixedColumnOrder!3,<|>,{|},[|]", + /* 54 */ "!fixedColumnOrder!3,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -1461,8 +1520,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", }; /* Language ky: Kirghiz */ @@ -1473,22 +1532,29 @@ public final class KeyboardTextsSet { /* ~24 */ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* 25 */ "\u0449", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 26 */ "\u044A", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* 26 */ "\u044B", + /* 27 */ "\u044B", + // U+044D: "э" CYRILLIC SMALL LETTER E + /* 28 */ "\u044D", // U+0438: "и" CYRILLIC SMALL LETTER I - /* 27 */ "\u0438", + /* 29 */ "\u0438", // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U - /* 28 */ "\u04AF", - /* 29 */ null, + /* 30 */ "\u04AF", // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER - /* 30 */ "\u04A3", - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 31 */ "\u044A", + /* 31 */ "\u04A3", /* 32 */ null, + /* 33 */ null, // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O - /* 33 */ "\u04E9", + /* 34 */ "\u04E9", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 34 */ "\u044A", + /* 35 */ "\u044A", + /* 36~ */ + null, null, null, null, + /* ~39 */ + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 40 */ "\u0451", }; /* Language lt: Lithuanian */ @@ -1675,21 +1741,21 @@ 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, - /* ~34 */ + null, null, null, null, null, null, + /* ~35 */ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 35 */ "\u0455", + /* 36 */ "\u0455", // U+045C: "ќ" CYRILLIC SMALL LETTER KJE - /* 36 */ "\u045C", + /* 37 */ "\u045C", // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 37 */ "\u0437", + /* 38 */ "\u0437", // U+0453: "ѓ" CYRILLIC SMALL LETTER GJE - /* 38 */ "\u0453", + /* 39 */ "\u0453", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 39 */ "\u0450", + /* 40 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 40 */ "\u045D", - /* 41 */ null, + /* 41 */ "\u045D", + /* 42 */ null, // U+2018: "‘" LEFT SINGLE QUOTATION MARK // U+2019: "’" RIGHT SINGLE QUOTATION MARK // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK @@ -1700,10 +1766,10 @@ public final class KeyboardTextsSet { // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_double_quote">!fixedColumnOrder!6,„,“,”,‟,«,»</string> - /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB", + /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«,»,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", + /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", }; /* Language nb: Norwegian Bokmål */ @@ -1965,20 +2031,24 @@ public final class KeyboardTextsSet { /* ~24 */ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* 25 */ "\u0449", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 26 */ "\u044A", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* 26 */ "\u044B", + /* 27 */ "\u044B", + // U+044D: "э" CYRILLIC SMALL LETTER E + /* 28 */ "\u044D", // U+0438: "и" CYRILLIC SMALL LETTER I - /* 27 */ "\u0438", - /* 28 */ null, - // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 29 */ "\u0451", - /* 30 */ null, - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 31 */ "\u044A", - /* 32 */ null, - /* 33 */ null, + /* 29 */ "\u0438", + /* 30~ */ + null, null, null, null, null, + /* ~34 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 34 */ "\u044A", + /* 35 */ "\u044A", + /* 36~ */ + null, null, null, null, + /* ~39 */ + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 40 */ "\u0451", }; /* Language sk: Slovak */ @@ -2096,21 +2166,40 @@ 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, - /* ~34 */ + null, null, null, null, null, null, + /* ~35 */ + // 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 + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE + // <string name="more_keys_for_s">š,ß,ś</string> + // U+010D: "č" LATIN SMALL LETTER C WITH CARON + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE + // <string name="more_keys_for_c">č,ç,ć</string> + // U+010F: "ď" LATIN SMALL LETTER D WITH CARON + // <string name="more_keys_for_d">ď</string> + // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON + // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE + // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE + // <string name="more_keys_for_z">ž,ź,ż</string> + // END: More keys definitions for Serbian (Latin) + // BEGIN: More keys definitions for Serbian (Cyrillic) // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 35 */ "\u0437", + /* 36 */ "\u0437", // U+045B: "ћ" CYRILLIC SMALL LETTER TSHE - /* 36 */ "\u045B", + /* 37 */ "\u045B", // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 37 */ "\u0455", + /* 38 */ "\u0455", // U+0452: "ђ" CYRILLIC SMALL LETTER DJE - /* 38 */ "\u0452", + /* 39 */ "\u0452", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 39 */ "\u0450", + /* 40 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 40 */ "\u045D", - /* 41 */ null, + /* 41 */ "\u045D", + /* 42 */ null, + // END: More keys definitions for Serbian (Cyrillic) // U+2018: "‘" LEFT SINGLE QUOTATION MARK // U+2019: "’" RIGHT SINGLE QUOTATION MARK // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK @@ -2121,10 +2210,10 @@ public final class KeyboardTextsSet { // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_double_quote">!fixedColumnOrder!6,„,“,”,‟,«,»</string> - /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB", + /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB", // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK. // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«,»,‘,’,‚,‛</string> - /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", + /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", }; /* Language sv: Swedish */ @@ -2169,6 +2258,111 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E6", }; + /* Language sw: Swahili */ + private static final String[] LANGUAGE_sw = { + // This is the same as English except more_keys_for_g. + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101", + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113", + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5", + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + /* 5 */ "\u00DF", + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + /* 6 */ "\u00F1", + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + /* 7 */ "\u00E7", + /* 8~ */ + null, null, null, null, null, null, null, + /* ~14 */ + /* 15 */ "g\'", + }; + + /* Language tl: Tagalog */ + private static final String[] LANGUAGE_tl = { + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + // U+00AA: "ª" FEMININE ORDINAL INDICATOR + /* 0 */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK + // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + /* 1 */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113", + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + /* 2 */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + // U+00BA: "º" MASCULINE ORDINAL INDICATOR + /* 3 */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA", + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + /* 4 */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* 5 */ null, + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE + /* 6 */ "\u00F1,\u0144", + // 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", + }; + /* Language tr: Turkish */ private static final String[] LANGUAGE_tr = { // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX @@ -2222,20 +2416,23 @@ public final class KeyboardTextsSet { /* ~24 */ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* 25 */ "\u0449", + // U+0457: "ї" CYRILLIC SMALL LETTER YI + /* 26 */ "\u0457", // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I - /* 26 */ "\u0456", + /* 27 */ "\u0456", + // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE + /* 28 */ "\u0454", // U+0438: "и" CYRILLIC SMALL LETTER I - /* 27 */ "\u0438", - /* 28~ */ - null, null, null, - /* ~30 */ - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 31 */ "\u044A", + /* 29 */ "\u0438", + /* 30 */ null, + /* 31 */ null, + // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN + /* 32 */ "\u0491", // U+0457: "ї" CYRILLIC SMALL LETTER YI - /* 32 */ "\u0457", - /* 33 */ null, + /* 33 */ "\u0457", + /* 34 */ null, // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 34 */ "\u044A", + /* 35 */ "\u044A", }; /* Language vi: Vietnamese */ @@ -2319,6 +2516,53 @@ public final class KeyboardTextsSet { /* 9 */ "\u0111", }; + /* Language zu: Zulu */ + private static final String[] LANGUAGE_zu = { + // This is the same as English + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101", + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113", + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5", + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + /* 5 */ "\u00DF", + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + /* 6 */ "\u00F1", + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + /* 7 */ "\u00E7", + }; + /* Language zz: No language */ private static final String[] LANGUAGE_zz = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -2444,6 +2688,7 @@ public final class KeyboardTextsSet { private static final Object[] LANGUAGES_AND_TEXTS = { "DEFAULT", LANGUAGE_DEFAULT, /* default */ + "af", LANGUAGE_af, /* Afrikaans */ "ar", LANGUAGE_ar, /* Arabic */ "be", LANGUAGE_be, /* Belarusian */ "ca", LANGUAGE_ca, /* Catalan */ @@ -2477,9 +2722,12 @@ public final class KeyboardTextsSet { "sl", LANGUAGE_sl, /* Slovenian */ "sr", LANGUAGE_sr, /* Serbian */ "sv", LANGUAGE_sv, /* Swedish */ + "sw", LANGUAGE_sw, /* Swahili */ + "tl", LANGUAGE_tl, /* Tagalog */ "tr", LANGUAGE_tr, /* Turkish */ "uk", LANGUAGE_uk, /* Ukrainian */ "vi", LANGUAGE_vi, /* Vietnamese */ + "zu", LANGUAGE_zu, /* Zulu */ "zz", LANGUAGE_zz, /* No language */ }; diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java new file mode 100644 index 000000000..c38febf49 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.text.TextUtils; +import android.util.SparseArray; +import android.widget.RelativeLayout; + +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.R; + +public class PreviewPlacerView extends RelativeLayout { + private final Paint mGesturePaint; + private final Paint mTextPaint; + private final int mGestureFloatingPreviewTextColor; + private final int mGestureFloatingPreviewTextOffset; + private final int mGestureFloatingPreviewTextShadowColor; + private final int mGestureFloatingPreviewTextShadowBorder; + private final int mGestureFloatingPreviewTextShadingColor; + private final int mGestureFloatingPreviewTextShadingBorder; + private final int mGestureFloatingPreviewTextConnectorColor; + private final int mGestureFloatingPreviewTextConnectorWidth; + + private int mXOrigin; + private int mYOrigin; + + private final SparseArray<PointerTracker> mPointers = new SparseArray<PointerTracker>(); + + private String mGestureFloatingPreviewText; + private boolean mDrawsGesturePreviewTrail; + private boolean mDrawsGestureFloatingPreviewText; + + public PreviewPlacerView(Context context, TypedArray keyboardViewAttr) { + super(context); + setWillNotDraw(false); + + final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0); + mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0); + mGestureFloatingPreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset( + R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0); + mGestureFloatingPreviewTextShadowColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewTextShadowColor, 0); + mGestureFloatingPreviewTextShadowBorder = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gestureFloatingPreviewTextShadowBorder, 0); + mGestureFloatingPreviewTextShadingColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewTextShadingColor, 0); + mGestureFloatingPreviewTextShadingBorder = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gestureFloatingPreviewTextShadingBorder, 0); + mGestureFloatingPreviewTextConnectorColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorColor, 0); + mGestureFloatingPreviewTextConnectorWidth = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorWidth, 0); + final int gesturePreviewTrailColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gesturePreviewTrailColor, 0); + final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gesturePreviewTrailWidth, 0); + + mGesturePaint = new Paint(); + mGesturePaint.setAntiAlias(true); + mGesturePaint.setStyle(Paint.Style.STROKE); + mGesturePaint.setStrokeJoin(Paint.Join.ROUND); + mGesturePaint.setColor(gesturePreviewTrailColor); + mGesturePaint.setStrokeWidth(gesturePreviewTrailWidth); + + mTextPaint = new Paint(); + mTextPaint.setAntiAlias(true); + mTextPaint.setStrokeJoin(Paint.Join.ROUND); + mTextPaint.setTextAlign(Align.CENTER); + mTextPaint.setTextSize(gestureFloatingPreviewTextSize); + } + + public void setOrigin(int x, int y) { + mXOrigin = x; + mYOrigin = y; + } + + public void setGesturePreviewMode(boolean drawsGesturePreviewTrail, + boolean drawsGestureFloatingPreviewText) { + mDrawsGesturePreviewTrail = drawsGesturePreviewTrail; + mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText; + } + + public void invalidatePointer(PointerTracker tracker) { + synchronized (mPointers) { + mPointers.put(tracker.mPointerId, tracker); + // TODO: Should narrow the invalidate region. + invalidate(); + } + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + synchronized (mPointers) { + canvas.translate(mXOrigin, mYOrigin); + final int trackerCount = mPointers.size(); + boolean hasDrawnFloatingPreviewText = false; + for (int index = 0; index < trackerCount; index++) { + final PointerTracker tracker = mPointers.valueAt(index); + if (mDrawsGesturePreviewTrail) { + tracker.drawGestureTrail(canvas, mGesturePaint); + } + // TODO: Figure out more cleaner way to draw gesture preview text. + if (mDrawsGestureFloatingPreviewText && !hasDrawnFloatingPreviewText) { + drawGestureFloatingPreviewText(canvas, tracker, mGestureFloatingPreviewText); + hasDrawnFloatingPreviewText = true; + } + } + canvas.translate(-mXOrigin, -mYOrigin); + } + } + + public void setGestureFloatingPreviewText(String gestureFloatingPreviewText) { + mGestureFloatingPreviewText = gestureFloatingPreviewText; + invalidate(); + } + + private void drawGestureFloatingPreviewText(Canvas canvas, PointerTracker tracker, + String gestureFloatingPreviewText) { + if (TextUtils.isEmpty(gestureFloatingPreviewText)) { + return; + } + + final Paint paint = mTextPaint; + final int lastX = tracker.getLastX(); + final int lastY = tracker.getLastY(); + final int textSize = (int)paint.getTextSize(); + final int canvasWidth = canvas.getWidth(); + + final int halfTextWidth = (int)paint.measureText(gestureFloatingPreviewText) / 2 + textSize; + final int textX = Math.min(Math.max(lastX, halfTextWidth), canvasWidth - halfTextWidth); + + int textY = Math.max(-textSize, lastY - mGestureFloatingPreviewTextOffset); + if (textY < 0) { + // Paint black text shadow if preview extends above keyboard region. + paint.setStyle(Paint.Style.FILL_AND_STROKE); + paint.setColor(mGestureFloatingPreviewTextShadowColor); + paint.setStrokeWidth(mGestureFloatingPreviewTextShadowBorder); + canvas.drawText(gestureFloatingPreviewText, textX, textY, paint); + } + + // Paint the vertical line connecting the touch point to the preview text. + paint.setStyle(Paint.Style.STROKE); + paint.setColor(mGestureFloatingPreviewTextConnectorColor); + paint.setStrokeWidth(mGestureFloatingPreviewTextConnectorWidth); + final int lineTopY = textY - textSize / 4; + canvas.drawLine(lastX, lastY, lastX, lineTopY, paint); + if (lastX != textX) { + // Paint the horizontal line connection the touch point to the preview text. + canvas.drawLine(lastX, lineTopY, textX, lineTopY, paint); + } + + // Paint the shading for the text preview + paint.setStyle(Paint.Style.FILL_AND_STROKE); + paint.setColor(mGestureFloatingPreviewTextShadingColor); + paint.setStrokeWidth(mGestureFloatingPreviewTextShadingBorder); + canvas.drawText(gestureFloatingPreviewText, textX, textY, paint); + + // Paint the text preview + paint.setColor(mGestureFloatingPreviewTextColor); + paint.setStyle(Paint.Style.FILL); + canvas.drawText(gestureFloatingPreviewText, textX, textY, paint); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java index 2398c0850..9e2cbec52 100644 --- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java @@ -14,12 +14,14 @@ * the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.util.Log; import android.view.MotionEvent; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Utils; diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 5d7995dc2..d101aaf15 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -654,9 +654,12 @@ public class ExpandableDictionary extends Dictionary { --index; mLookedUpString[index] = node.mCode; node = node.mParent; - } while (node != null); + } while (node != null && index > 0); - if (freq >= 0) { + // If node is null, we have a word longer than MAX_WORD_LENGTH in the dictionary. + // It's a little unclear how this can happen, but just in case it does it's safer + // to ignore the word in this case. + if (freq >= 0 && node == null) { suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index), freq, SuggestedWordInfo.KIND_CORRECTION, mDictType)); diff --git a/java/src/com/android/inputmethod/latin/JniUtils.java b/java/src/com/android/inputmethod/latin/JniUtils.java index 4808b867a..86a3826d8 100644 --- a/java/src/com/android/inputmethod/latin/JniUtils.java +++ b/java/src/com/android/inputmethod/latin/JniUtils.java @@ -31,11 +31,7 @@ public class JniUtils { try { System.loadLibrary(JniLibName.JNI_LIB_NAME); } catch (UnsatisfiedLinkError ule) { - Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME); - if (LatinImeLogger.sDBG) { - throw new RuntimeException( - "Could not load native library " + JniLibName.JNI_LIB_NAME); - } + Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule); } } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d4b59c4cd..a7446695e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -60,6 +60,7 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.compat.CompatUtils; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; @@ -82,7 +83,8 @@ import java.util.Locale; * Input method implementation for Qwerty'ish keyboard. */ public class LatinIME extends InputMethodService implements KeyboardActionListener, - SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener { + SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener, + Suggest.SuggestInitializationListener { private static final String TAG = LatinIME.class.getSimpleName(); private static final boolean TRACE = false; private static boolean DEBUG; @@ -174,6 +176,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private AlertDialog mOptionsDialog; + private final boolean mIsHardwareAcceleratedDrawingEnabled; + public final UIHandler mHandler = new UIHandler(this); public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { @@ -346,6 +350,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); + mIsHardwareAcceleratedDrawingEnabled = + InputMethodServiceCompatUtils.enableHardwareAcceleration(this); + Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); } @Override @@ -426,6 +433,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } + @Override + public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable) { + mIsMainDictionaryAvailable = isMainDictionaryAvailable; + updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); + } + private void initSuggest() { final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final String localeStr = subtypeLocale.toString(); @@ -437,7 +450,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { oldContactsDictionary = null; } - mSuggest = new Suggest(this, subtypeLocale); + mSuggest = new Suggest(this /* Context */, subtypeLocale, + this /* SuggestInitializationListener */); if (mCurrentSettings.mCorrectionEnabled) { mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); } @@ -531,15 +545,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen commitTyped(LastComposedWord.NOT_A_SEPARATOR); mConnection.finishComposingText(); mConnection.endBatchEdit(); - if (isShowingOptionDialog()) + if (isShowingOptionDialog()) { mOptionsDialog.dismiss(); + } } super.onConfigurationChanged(conf); } @Override public View onCreateInputView() { - return mKeyboardSwitcher.onCreateInputView(); + return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled); } @Override @@ -651,40 +666,61 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen accessUtils.onStartInputViewInternal(editorInfo, restarting); } - mSubtypeSwitcher.updateParametersOnStartInputView(); + if (!restarting) { + mSubtypeSwitcher.updateParametersOnStartInputView(); + } // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? updateFullscreenMode(); - mLastSelectionStart = editorInfo.initialSelStart; - mLastSelectionEnd = editorInfo.initialSelEnd; mApplicationSpecifiedCompletions = null; - inputView.closing(); - mEnteredText = null; - resetComposingState(true /* alsoResetLastComposedWord */); - mDeleteCount = 0; - mSpaceState = SPACE_STATE_NONE; - - loadSettings(); + final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart + || mLastSelectionEnd != editorInfo.initialSelEnd; + if (!restarting || selectionChanged) { + // If the selection changed, we reset the input state. Essentially, we come here with + // restarting == true when the app called setText() or similar. We should reset the + // state if the app set the text to something else, but keep it if it set a suggestion + // or something. + mEnteredText = null; + resetComposingState(true /* alsoResetLastComposedWord */); + mDeleteCount = 0; + mSpaceState = SPACE_STATE_NONE; - if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) { - mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); + if (mSuggestionStripView != null) { + mSuggestionStripView.clear(); + } } - switcher.loadKeyboard(editorInfo, mCurrentSettings); + if (!restarting) { + inputView.closing(); + loadSettings(); + + if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) { + mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); + } - if (mSuggestionStripView != null) - mSuggestionStripView.clear(); + switcher.loadKeyboard(editorInfo, mCurrentSettings); + updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); + } setSuggestionStripShownInternal( isSuggestionsStripVisible(), /* needsInputViewShown */ false); + mLastSelectionStart = editorInfo.initialSelStart; + mLastSelectionEnd = editorInfo.initialSelEnd; + // If we come here something in the text state is very likely to have changed. + // We should update the shift state regardless of whether we are restarting or not, because + // this is not perceived as a layout change that may be disruptive like we may have with + // switcher.loadKeyboard; in apps like Talk, we come here when the text is sent and the + // field gets emptied and we need to re-evaluate the shift state, but not the whole layout + // which would be disruptive. + mKeyboardSwitcher.updateShiftState(); + mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacesTimer(); inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn, mCurrentSettings.mKeyPreviewPopupDismissDelay); - inputView.setProximityCorrectionEnabled(true); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } @@ -856,13 +892,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(null); + } return; } @@ -884,6 +920,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1003,15 +1042,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), separatorCode, prevWord); } - updateSuggestionStrip(); } // Called from the KeyboardSwitcher which needs to know auto caps state to display @@ -1048,12 +1083,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); + ResearchLogger.latinIME_swapSwapperAndSpace(); } mKeyboardSwitcher.updateShiftState(); } @@ -1070,9 +1102,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_doubleSpaceAutoPeriod(); - } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1136,9 +1165,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } } private void handleLanguageSwitchKey() { @@ -1175,6 +1201,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } return; } @@ -1191,9 +1220,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } } // Implementation of {@link KeyboardActionListener}. @@ -1205,11 +1231,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mLastKeyTime = when; mConnection.beginBatchEdit(); - - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); - } - final KeyboardSwitcher switcher = mKeyboardSwitcher; // The space state depends only on the last character pressed and its own previous // state. Here, we revert the space state to neutral if the key is actually modifying @@ -1291,21 +1312,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord.deactivate(); mEnteredText = null; mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); + } } // Called from PointerTracker through the KeyboardActionListener interface @Override - public void onTextInput(CharSequence text) { + public void onTextInput(CharSequence rawText) { mConnection.beginBatchEdit(); commitTyped(LastComposedWord.NOT_A_SEPARATOR); - text = specificTldProcessingOnTextInput(text); + mHandler.postUpdateSuggestionStrip(); + final CharSequence text = specificTldProcessingOnTextInput(rawText); if (SPACE_STATE_PHANTOM == mSpaceState) { sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(text); - } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1334,6 +1356,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setBatchInputPointers(batchPointers); final SuggestedWords suggestedWords = getSuggestedWords(); showSuggestionStrip(suggestedWords, null); + final String gestureFloatingPreviewText = (suggestedWords.size() > 0) + ? suggestedWords.getWord(0) : null; + mKeyboardSwitcher.getKeyboardView() + .showGestureFloatingPreviewText(gestureFloatingPreviewText); } @Override @@ -1341,6 +1367,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setBatchInputPointers(batchPointers); final SuggestedWords suggestedWords = getSuggestedWords(); showSuggestionStrip(suggestedWords, null); + mKeyboardSwitcher.getKeyboardView().showGestureFloatingPreviewText(null); if (suggestedWords == null || suggestedWords.size() == 0) { return; } @@ -1395,9 +1422,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1417,9 +1441,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1448,9 +1469,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); - } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1469,14 +1487,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1579,11 +1591,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen boolean didAutoCorrect = false; // Handle separator if (mWordComposer.isComposingWord()) { - // In certain languages where single quote is a separator, it's better - // not to auto correct, but accept the typed word. For instance, - // in Italian dov' should not be expanded to dove' because the elision - // requires the last vowel to be removed. - if (mCurrentSettings.mCorrectionEnabled && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { + if (mCurrentSettings.mCorrectionEnabled) { commitCurrentAutoCorrection(primaryCode); didAutoCorrect = true; } else { @@ -1617,13 +1625,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (swapWeakSpace) { swapSwapperAndSpace(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; - } else if (SPACE_STATE_PHANTOM == spaceState) { + } else if (SPACE_STATE_PHANTOM == spaceState + && !mCurrentSettings.isWeakSpaceStripper(primaryCode)) { // If we are in phantom space state, and the user presses a separator, we want to // stay in phantom space state so that the next keypress has a chance to add the // space. For example, if I type "Good dat", pick "day" from the suggestion strip // then insert a comma and go on to typing the next word, I want the space to be // inserted automatically before the next word, the same way it is when I don't // input the comma. + // The case is a little different if the separator is a space stripper. Such a + // separator does not normally need a space on the right (that's the difference + // between swappers and strippers), so we should not stay in phantom space state if + // the separator is a stripper. Hence the additional test above. mSpaceState = SPACE_STATE_PHANTOM; } @@ -1647,8 +1660,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen commitTyped(LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); MainKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); - if (inputView != null) + if (inputView != null) { inputView.closing(); + } } // TODO: make this private @@ -1799,10 +1813,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, - autoCorrection.toString()); - } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1828,13 +1838,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); - } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); + } return; } @@ -1861,10 +1871,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, - completionInfo.getText(), x, y); - } return; } @@ -1873,12 +1879,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); - } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); + } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1913,9 +1919,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -1982,9 +1985,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2012,9 +2012,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(deleteLength); - } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); @@ -2044,18 +2041,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onRefreshKeyboard() { // When the device locale is changed in SetupWizard etc., this method may get called via // onConfigurationChanged before SoftInputWindow is shown. + initSuggest(); + loadSettings(); if (mKeyboardSwitcher.getKeyboardView() != null) { // Reload keyboard because the current language has been changed. mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings); + updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); } - initSuggest(); - loadSettings(); // Since we just changed languages, we should re-evaluate suggestions with whatever word // we are currently composing. If we are not composing anything, we may want to display // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway). mHandler.postUpdateSuggestionStrip(); } + private void updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability() { + final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView(); + if (keyboardView != null) { + final boolean shouldHandleGesture = mCurrentSettings.mGestureInputEnabled + && mIsMainDictionaryAvailable; + keyboardView.setGestureHandlingMode(shouldHandleGesture, + mCurrentSettings.mGesturePreviewTrailEnabled, + mCurrentSettings.mGestureFloatingPreviewTextEnabled); + } + } + // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to // {@link KeyboardSwitcher}. Called from KeyboardSwitcher public void hapticAndAudioFeedback(final int primaryCode) { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8b4c17322..41e59e92d 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,7 +55,9 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.beginBatchEdit(); + if (null != mIC) { + mIC.beginBatchEdit(); + } } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -66,7 +68,9 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + if (--mNestLevel == 0 && null != mIC) { + mIC.endBatchEdit(); + } } private void checkBatchEdit() { @@ -79,12 +83,22 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) mIC.finishComposingText(); + if (null != mIC) { + mIC.finishComposingText(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.commitText(text, i); + if (null != mIC) { + mIC.commitText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitText(text, i); + } + } } public int getCursorCapsMode(final int inputType) { @@ -107,37 +121,72 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) mIC.deleteSurroundingText(i, j); + if (null != mIC) { + mIC.deleteSurroundingText(i, j); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_deleteSurroundingText(i, j); + } + } } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.performEditorAction(actionId); + if (null != mIC) { + mIC.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_performEditorAction(actionId); + } + } } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) mIC.sendKeyEvent(keyEvent); + if (null != mIC) { + mIC.sendKeyEvent(keyEvent); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); + } + } } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.setComposingText(text, i); + if (null != mIC) { + mIC.setComposingText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setComposingText(text, i); + } + } } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) mIC.setSelection(from, to); + if (null != mIC) { + mIC.setSelection(from, to); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setSelection(from, to); + } + } } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCorrection(correctionInfo); + if (null != mIC) { + mIC.commitCorrection(correctionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCorrection(correctionInfo); + } + } } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCompletion(completionInfo); + if (null != mIC) { + mIC.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCompletion(completionInfo); + } + } } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -315,9 +364,6 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } @@ -382,13 +428,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } return true; } @@ -409,13 +449,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); - } return true; } } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 45608f439..6251c9acd 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -62,6 +62,7 @@ public class Settings extends InputMethodSettingsFragment public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = "last_user_dictionary_write_time"; public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; + public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = "pref_suppress_language_switch_key"; public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = @@ -69,13 +70,15 @@ public class Settings extends InputMethodSettingsFragment public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles"; public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = "pref_key_preview_popup_dismiss_delay"; - public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume"; + public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; + public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = + "pref_gesture_floating_preview_text"; public static final String PREF_INPUT_LANGUAGE = "input_language"; public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; @@ -94,13 +97,17 @@ public class Settings extends InputMethodSettingsFragment private TextView mKeypressVibrationDurationSettingsTextView; private TextView mKeypressSoundVolumeSettingsTextView; + private static void setPreferenceEnabled(Preference preference, boolean enabled) { + if (preference != null) { + preference.setEnabled(enabled); + } + } + private void ensureConsistencyOfAutoCorrectionSettings() { final String autoCorrectionOff = getResources().getString( R.string.auto_correction_threshold_mode_index_off); final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); - if (null != mBigramPrediction) { - mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); - } + setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff)); } @Override @@ -180,13 +187,11 @@ public class Settings extends InputMethodSettingsFragment if (null == mKeyPreviewPopupDismissDelay.getValue()) { mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); } - mKeyPreviewPopupDismissDelay.setEnabled( + setPreferenceEnabled(mKeyPreviewPopupDismissDelay, SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); } - final CheckBoxPreference includeOtherImesInLanguageSwitchList = - (CheckBoxPreference)findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); - includeOtherImesInLanguageSwitchList.setEnabled( + setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), !SettingsValues.isLanguageSwitchKeySupressed(prefs)); final PreferenceScreen dictionaryLink = @@ -200,10 +205,19 @@ public class Settings extends InputMethodSettingsFragment final boolean gestureInputEnabledByBuildConfig = res.getBoolean( R.bool.config_gesture_input_enabled_by_build_config); + final Preference gesturePreviewTrail = findPreference(PREF_GESTURE_PREVIEW_TRAIL); + final Preference gestureFloatingPreviewText = findPreference( + PREF_GESTURE_FLOATING_PREVIEW_TEXT); if (!gestureInputEnabledByBuildConfig) { - final Preference gestureInputPref = findPreference(PREF_GESTURE_INPUT); - miscSettings.removePreference(gestureInputPref); + miscSettings.removePreference(findPreference(PREF_GESTURE_INPUT)); + miscSettings.removePreference(gesturePreviewTrail); + miscSettings.removePreference(gestureFloatingPreviewText); + } else { + final boolean gestureInputEnabledByUser = prefs.getBoolean(PREF_GESTURE_INPUT, true); + setPreferenceEnabled(gesturePreviewTrail, gestureInputEnabledByUser); + setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser); } + final boolean showUsabilityStudyModeOption = res.getBoolean(R.bool.config_enable_usability_study_mode_option) || ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS; @@ -277,17 +291,22 @@ public class Settings extends InputMethodSettingsFragment public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { (new BackupManager(getActivity())).dataChanged(); if (key.equals(PREF_POPUP_ON)) { - final ListPreference popupDismissDelay = - (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - if (null != popupDismissDelay) { - popupDismissDelay.setEnabled(prefs.getBoolean(PREF_POPUP_ON, true)); - } + setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY), + prefs.getBoolean(PREF_POPUP_ON, true)); } else if (key.equals(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) { - final CheckBoxPreference includeOtherImesInLanguageSwicthList = - (CheckBoxPreference)findPreference( - PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); - includeOtherImesInLanguageSwicthList.setEnabled( + setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), !SettingsValues.isLanguageSwitchKeySupressed(prefs)); + } else if (key.equals(PREF_GESTURE_INPUT)) { + final boolean gestureInputEnabledByConfig = getResources().getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + if (gestureInputEnabledByConfig) { + final boolean gestureInputEnabledByUser = prefs.getBoolean( + PREF_GESTURE_INPUT, true); + setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL), + gestureInputEnabledByUser); + setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT), + gestureInputEnabledByUser); + } } ensureConsistencyOfAutoCorrectionSettings(); updateVoiceModeSummary(); @@ -335,16 +354,18 @@ public class Settings extends InputMethodSettingsFragment private void refreshEnablingsOfKeypressSoundAndVibrationSettings( SharedPreferences sp, Resources res) { if (mKeypressVibrationDurationSettingsPref != null) { - final boolean hasVibrator = VibratorUtils.getInstance(getActivity()).hasVibrator(); - final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON, + final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity()) + .hasVibrator(); + final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON, res.getBoolean(R.bool.config_default_vibration_enabled)); - mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn); + setPreferenceEnabled(mKeypressVibrationDurationSettingsPref, + hasVibratorHardware && vibrateOnByUser); } if (mKeypressSoundVolumeSettingsPref != null) { final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, res.getBoolean(R.bool.config_default_sound_enabled)); - mKeypressSoundVolumeSettingsPref.setEnabled(soundOn); + setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn); } } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 3ed981375..0843bdbbc 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -84,6 +84,8 @@ public class SettingsValues { private final float mKeypressSoundVolumeRawValue; private final InputMethodSubtype[] mAdditionalSubtypes; public final boolean mGestureInputEnabled; + public final boolean mGesturePreviewTrailEnabled; + public final boolean mGestureFloatingPreviewTextEnabled; // From the input box private final InputAttributes mInputAttributes; @@ -174,6 +176,9 @@ public class SettingsValues { R.bool.config_gesture_input_enabled_by_build_config); mGestureInputEnabled = gestureInputEnabledByBuildConfig && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true); + mGesturePreviewTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); + mGestureFloatingPreviewTextEnabled = prefs.getBoolean( + Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; mSuggestionVisibility = createSuggestionVisibility(res); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 598ef1de7..5e2a04124 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -42,6 +42,10 @@ public class Suggest { // TODO: rename this to CORRECTION_ON public static final int CORRECTION_FULL = 1; + public interface SuggestInitializationListener { + public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); + } + private static final boolean DBG = LatinImeLogger.sDBG; private Dictionary mMainDictionary; @@ -55,11 +59,14 @@ public class Suggest { private float mAutoCorrectionThreshold; // Locale used for upper- and title-casing words - final private Locale mLocale; + private final Locale mLocale; + private final SuggestInitializationListener mListener; - public Suggest(final Context context, final Locale locale) { + public Suggest(final Context context, final Locale locale, + final SuggestInitializationListener listener) { initAsynchronously(context, locale); mLocale = locale; + mListener = listener; } /* package for test */ Suggest(final Context context, final File dictionary, @@ -67,6 +74,7 @@ public class Suggest { final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length /* useFullEditDistance */, false, locale); mLocale = locale; + mListener = null; mMainDictionary = mainDict; addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); initWhitelistAndAutocorrectAndPool(context, locale); @@ -98,6 +106,9 @@ public class Suggest { public void resetMainDict(final Context context, final Locale locale) { mMainDictionary = null; + if (mListener != null) { + mListener.onUpdateMainDictionaryAvailability(hasMainDictionary()); + } new Thread("InitializeBinaryDictionary") { @Override public void run() { @@ -105,6 +116,9 @@ public class Suggest { DictionaryFactory.createMainDictionaryFromManager(context, locale); addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict); mMainDictionary = newMainDict; + if (mListener != null) { + mListener.onUpdateMainDictionaryAvailability(hasMainDictionary()); + } } }.start(); } @@ -219,7 +233,7 @@ public class Suggest { // the current settings. It may also be useful to know, when the setting is off, whether // the word *would* have been auto-corrected. if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord() - || suggestionsSet.isEmpty() + || suggestionsSet.isEmpty() || wordComposer.hasDigits() || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !hasMainDictionary()) { // If we don't have a main dictionary, we never want to auto-correct. The reason for diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 6d346d179..5606a58e4 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -41,6 +41,7 @@ public class WordComposer { // Cache these values for performance private int mCapsCount; + private int mDigitsCount; private boolean mAutoCapitalized; private int mTrailingSingleQuotesCount; private int mCodePointSize; @@ -65,6 +66,7 @@ public class WordComposer { mTypedWord = new StringBuilder(source.mTypedWord); mInputPointers.copy(source.mInputPointers); mCapsCount = source.mCapsCount; + mDigitsCount = source.mDigitsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; @@ -80,6 +82,7 @@ public class WordComposer { mTypedWord.setLength(0); mAutoCorrection = null; mCapsCount = 0; + mDigitsCount = 0; mIsFirstCharCapitalized = false; mTrailingSingleQuotesCount = 0; mIsResumed = false; @@ -141,6 +144,7 @@ public class WordComposer { mIsFirstCharCapitalized = isFirstCharCapitalized( newIndex, primaryCode, mIsFirstCharCapitalized); if (Character.isUpperCase(primaryCode)) mCapsCount++; + if (Character.isDigit(primaryCode)) mDigitsCount++; if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) { ++mTrailingSingleQuotesCount; } else { @@ -213,6 +217,7 @@ public class WordComposer { mTypedWord.deleteCharAt(stringBuilderLength - 1); } if (Character.isUpperCase(lastChar)) mCapsCount--; + if (Character.isDigit(lastChar)) mDigitsCount--; refreshSize(); } // We may have deleted the last one. @@ -268,6 +273,13 @@ public class WordComposer { } /** + * Returns true if we have digits in the composing word. + */ + public boolean hasDigits() { + return mDigitsCount > 0; + } + + /** * Saves the reason why the word is capitalized - whether it was automatic or * due to the user hitting shift in the middle of a sentence. * @param auto whether it was an automatic capitalization due to start of sentence @@ -322,6 +334,7 @@ public class WordComposer { lastComposedWord.deactivate(); } mCapsCount = 0; + mDigitsCount = 0; mIsBatchMode = false; mTypedWord.setLength(0); mTrailingSingleQuotesCount = 0; diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 8b53c9427..5864db28e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -61,8 +61,8 @@ public class FusionDictionary implements Iterable<Word> { * This represents an "attribute", that is either a bigram or a shortcut. */ public static class WeightedString { - final String mWord; - int mFrequency; + public final String mWord; + public int mFrequency; public WeightedString(String word, int frequency) { mWord = word; mFrequency = frequency; diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java index d07826757..65fc72c40 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Word.java +++ b/java/src/com/android/inputmethod/latin/makedict/Word.java @@ -27,10 +27,10 @@ import java.util.Arrays; * This is chiefly used to iterate a dictionary. */ public class Word implements Comparable<Word> { - final String mWord; - final int mFrequency; - final ArrayList<WeightedString> mShortcutTargets; - final ArrayList<WeightedString> mBigrams; + public final String mWord; + public final int mFrequency; + public final ArrayList<WeightedString> mShortcutTargets; + public final ArrayList<WeightedString> mBigrams; private int mHashCode = 0; diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java index c9f3b476a..11eae8813 100644 --- a/java/src/com/android/inputmethod/research/FeedbackActivity.java +++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java @@ -18,10 +18,7 @@ package com.android.inputmethod.research; import android.app.Activity; import android.os.Bundle; -import android.text.Editable; -import android.view.View; import android.widget.CheckBox; -import android.widget.EditText; import com.android.inputmethod.latin.R; @@ -31,6 +28,11 @@ public class FeedbackActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.research_feedback_activity); final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout); + final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history); + final CharSequence cs = checkbox.getText(); + final String actualString = String.format(cs.toString(), + ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE); + checkbox.setText(actualString); layout.setActivity(this); } diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java new file mode 100644 index 000000000..65f5f83ae --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogBuffer.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.research; + +import java.util.LinkedList; + +/** + * A buffer that holds a fixed number of LogUnits. + * + * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are + * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches + * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to + * stay under the capacity limit. + */ +public class LogBuffer { + protected final LinkedList<LogUnit> mLogUnits; + /* package for test */ int mWordCapacity; + // The number of members of mLogUnits that are actual words. + protected int mNumActualWords; + + /** + * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and + * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. + * + * @param wordCapacity maximum number of words + */ + LogBuffer(final int wordCapacity) { + if (wordCapacity <= 0) { + throw new IllegalArgumentException("wordCapacity must be 1 or greater."); + } + mLogUnits = new LinkedList<LogUnit>(); + mWordCapacity = wordCapacity; + mNumActualWords = 0; + } + + /** + * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's + * (oldest first) if word capacity is reached. + */ + public void shiftIn(LogUnit newLogUnit) { + if (newLogUnit.getWord() == null) { + // This LogUnit isn't a word, so it doesn't count toward the word-limit. + mLogUnits.add(newLogUnit); + return; + } + if (mNumActualWords == mWordCapacity) { + shiftOutThroughFirstWord(); + } + mLogUnits.add(newLogUnit); + mNumActualWords++; // Must be a word, or we wouldn't be here. + } + + private void shiftOutThroughFirstWord() { + while (!mLogUnits.isEmpty()) { + final LogUnit logUnit = mLogUnits.removeFirst(); + onShiftOut(logUnit); + if (logUnit.hasWord()) { + // Successfully shifted out a word-containing LogUnit and made space for the new + // LogUnit. + mNumActualWords--; + break; + } + } + } + + /** + * Removes all LogUnits from the buffer without calling onShiftOut(). + */ + public void clear() { + mLogUnits.clear(); + mNumActualWords = 0; + } + + /** + * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are + * removed in the order entered. This method is not called when shiftOut is called directly. + * + * Base class does nothing; subclasses may override. + */ + protected void onShiftOut(LogUnit logUnit) { + } + + /** + * Called to deliberately remove the oldest LogUnit. Usually called when draining the + * LogBuffer. + */ + public LogUnit shiftOut() { + if (mLogUnits.isEmpty()) { + return null; + } + final LogUnit logUnit = mLogUnits.removeFirst(); + if (logUnit.hasWord()) { + mNumActualWords--; + } + return logUnit; + } +} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java new file mode 100644 index 000000000..8a80664f5 --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.research; + +import java.util.ArrayList; + +/** + * A group of log statements related to each other. + * + * A LogUnit is collection of LogStatements, each of which is generated by at a particular point + * in the code. (There is no LogStatement class; the data is stored across the instance variables + * here.) A single LogUnit's statements can correspond to all the calls made while in the same + * composing region, or all the calls between committing the last composing region, and the first + * character of the next composing region. + * + * Individual statements in a log may be marked as potentially private. If so, then they are only + * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit + * will not violate the user's privacy. Checks for this may include whether other LogUnits have + * been published recently, or whether the LogUnit contains numbers, etc. + */ +/* package */ class LogUnit { + private final ArrayList<String[]> mKeysList = new ArrayList<String[]>(); + private final ArrayList<Object[]> mValuesList = new ArrayList<Object[]>(); + private final ArrayList<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>(); + private String mWord; + private boolean mContainsDigit; + + public void addLogStatement(final String[] keys, final Object[] values, + final Boolean isPotentiallyPrivate) { + mKeysList.add(keys); + mValuesList.add(values); + mIsPotentiallyPrivate.add(isPotentiallyPrivate); + } + + public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) { + final int size = mKeysList.size(); + for (int i = 0; i < size; i++) { + if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) { + researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + } + } + } + + public void setWord(String word) { + mWord = word; + } + + public String getWord() { + return mWord; + } + + public boolean hasWord() { + return mWord != null; + } + + public void setContainsDigit() { + mContainsDigit = true; + } + + public boolean hasDigit() { + return mContainsDigit; + } + + public boolean isEmpty() { + return mKeysList.isEmpty(); + } +} diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java new file mode 100644 index 000000000..745768d35 --- /dev/null +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.research; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Suggest; + +import java.util.Random; + +public class MainLogBuffer extends LogBuffer { + // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. + private static final int N_GRAM_SIZE = 2; + // The number of words between n-grams to omit from the log. + private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18; + + private final ResearchLog mResearchLog; + private Suggest mSuggest; + + // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if + // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc. + // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a + // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe + // n-gram does appear. + /* package for test */ int mMinWordPeriod; + + // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod + // after a sample is taken. + /* package for test */ int mWordsUntilSafeToSample; + + public MainLogBuffer(final ResearchLog researchLog) { + super(N_GRAM_SIZE); + mResearchLog = researchLog; + mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE; + final Random random = new Random(); + mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); + } + + public void setSuggest(Suggest suggest) { + mSuggest = suggest; + } + + @Override + public void shiftIn(final LogUnit newLogUnit) { + super.shiftIn(newLogUnit); + if (newLogUnit.hasWord()) { + if (mWordsUntilSafeToSample > 0) { + mWordsUntilSafeToSample--; + } + } + } + + public void resetWordCounter() { + mWordsUntilSafeToSample = mMinWordPeriod; + } + + /** + * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete + * form and still protect the user's privacy. + * + * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any + * non-character data that is typed between words. The decision about privacy is made based on + * the buffer's entire content. If it is decided that the privacy risks are too great to upload + * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g., + * the screen orientation and other characteristics about the device can be uploaded without + * revealing much about the user. + */ + public boolean isSafeToLog() { + // Check that we are not sampling too frequently. Having sampled recently might disclose + // too much of the user's intended meaning. + if (mWordsUntilSafeToSample > 0) { + return false; + } + if (mSuggest == null || !mSuggest.hasMainDictionary()) { + // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word + // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to + // potentially pose a privacy risk. + return false; + } + // Reload the dictionary in case it has changed (e.g., because the user has changed + // languages). + final Dictionary dictionary = mSuggest.getMainDictionary(); + if (dictionary == null) { + return false; + } + // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the + // complete buffer contents in detail. + final int length = mLogUnits.size(); + for (int i = 0; i < length; i++) { + final LogUnit logUnit = mLogUnits.get(i); + final String word = logUnit.getWord(); + if (word == null) { + // Digits outside words are a privacy threat. + if (logUnit.hasDigit()) { + return false; + } + } else { + // Words not in the dictionary are a privacy threat. + if (!(dictionary.isValidWord(word))) { + return false; + } + } + } + // All checks have passed; this buffer's content can be safely uploaded. + return true; + } + + @Override + protected void onShiftOut(LogUnit logUnit) { + if (mResearchLog != null) { + mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 18bf3c07f..71a6d6a78 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -26,7 +26,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.research.ResearchLogger.LogUnit; import java.io.BufferedWriter; import java.io.File; @@ -37,6 +36,7 @@ import java.io.OutputStreamWriter; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -51,21 +51,22 @@ import java.util.concurrent.TimeUnit; */ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); - private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( - new OutputStreamWriter(new NullOutputStream())); + private static final boolean DEBUG = false; + private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; - final ScheduledExecutorService mExecutor; + /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; private JsonWriter mJsonWriter = NULL_JSON_WRITER; + // true if at least one byte of data has been written out to the log file. This must be + // remembered because JsonWriter requires that calls matching calls to beginObject and + // endObject, as well as beginArray and endArray, and the file is opened lazily, only when + // it is certain that data will be written. Alternatively, the matching call exceptions + // could be caught, but this might suppress other errors. + private boolean mHasWrittenData = false; - private int mLoggingState; - private static final int LOGGING_STATE_UNSTARTED = 0; - private static final int LOGGING_STATE_READY = 1; // don't create file until necessary - private static final int LOGGING_STATE_RUNNING = 2; - private static final int LOGGING_STATE_STOPPING = 3; - private static final int LOGGING_STATE_STOPPED = 4; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; - + private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( + new OutputStreamWriter(new NullOutputStream())); private static class NullOutputStream extends OutputStream { /** {@inheritDoc} */ @Override @@ -84,128 +85,81 @@ public class ResearchLog { } } - public ResearchLog(File outputFile) { - mExecutor = Executors.newSingleThreadScheduledExecutor(); + public ResearchLog(final File outputFile) { if (outputFile == null) { throw new IllegalArgumentException(); } + mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; - mLoggingState = LOGGING_STATE_UNSTARTED; - } - - public synchronized void start() throws IOException { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_READY; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - break; - } } - public synchronized void stop() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - } finally { - boolean success = mFile.setWritable(false, false); - mLoggingState = LOGGING_STATE_STOPPED; - } - return null; + public synchronized void close() { + mExecutor.submit(new Callable<Object>() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.flush(); + mJsonWriter.close(); + mHasWrittenData = false; } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + } catch (Exception e) { + Log.d(TAG, "error when closing ResearchLog:"); + e.printStackTrace(); + } finally { + if (mFile.exists()) { + mFile.setWritable(false, false); + } + } + return null; + } + }); + removeAnyScheduledFlush(); + mExecutor.shutdown(); } - public boolean isAlive() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - return true; - } - return false; - } + private boolean mIsAbortSuccessful; - public void waitUntilStopped(final int timeoutInMs) throws InterruptedException { + public synchronized void abort() { + mExecutor.submit(new Callable<Object>() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.close(); + mHasWrittenData = false; + } + } finally { + mIsAbortSuccessful = mFile.delete(); + } + return null; + } + }); removeAnyScheduledFlush(); mExecutor.shutdown(); - mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); } - public synchronized void abort() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - isAbortSuccessful = true; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.close(); - } finally { - isAbortSuccessful = mFile.delete(); - } - return null; - } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + public boolean blockingAbort() throws InterruptedException { + abort(); + mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + return mIsAbortSuccessful; } - private boolean isAbortSuccessful; - public boolean isAbortSuccessful() { - return isAbortSuccessful; + public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException { + mExecutor.awaitTermination(delay, timeUnit); } /* package */ synchronized void flush() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + removeAnyScheduledFlush(); + mExecutor.submit(mFlushCallable); } - private Callable<Object> mFlushCallable = new Callable<Object>() { + private final Callable<Object> mFlushCallable = new Callable<Object>() { @Override public Object call() throws Exception { - if (mLoggingState == LOGGING_STATE_RUNNING) { - mJsonWriter.flush(); - } + mJsonWriter.flush(); return null; } }; @@ -224,56 +178,40 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } - public synchronized void publishPublicEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - logUnit.publishPublicEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public synchronized void publishAllEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - logUnit.publishAllEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: + public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) { + try { + mExecutor.submit(new Callable<Object>() { + @Override + public Object call() throws Exception { + logUnit.publishTo(ResearchLog.this, isIncludingPrivateData); + scheduleFlush(); + return null; + } + }); + } catch (RejectedExecutionException e) { + // TODO: Add code to record loss of data, and report. } } private static final String CURRENT_TIME_KEY = "_ct"; private static final String UPTIME_KEY = "_ut"; private static final String EVENT_TYPE_KEY = "_ty"; + void outputEvent(final String[] keys, final Object[] values) { - // not thread safe. + // Not thread safe. + if (keys.length == 0) { + return; + } + if (DEBUG) { + if (keys.length != values.length + 1) { + Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); + } + } try { if (mJsonWriter == NULL_JSON_WRITER) { mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.setLenient(true); mJsonWriter.beginArray(); + mHasWrittenData = true; } mJsonWriter.beginObject(); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); @@ -283,8 +221,8 @@ public class ResearchLog { for (int i = 0; i < length; i++) { mJsonWriter.name(keys[i + 1]); Object value = values[i]; - if (value instanceof String) { - mJsonWriter.value((String) value); + if (value instanceof CharSequence) { + mJsonWriter.value(value.toString()); } else if (value instanceof Number) { mJsonWriter.value((Number) value); } else if (value instanceof Boolean) { @@ -331,14 +269,11 @@ public class ResearchLog { SuggestedWords words = (SuggestedWords) value; mJsonWriter.beginObject(); mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect") - .value(words.mWillAutoCorrect); + mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions") - .value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction") - .value(words.mIsPrediction); + .value(words.mIsPunctuationSuggestions); + mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); + mJsonWriter.name("isPrediction").value(words.mIsPrediction); mJsonWriter.name("words"); mJsonWriter.beginArray(); final int size = words.size(); @@ -363,8 +298,8 @@ public class ResearchLog { try { mJsonWriter.close(); } catch (IllegalStateException e1) { - // assume that this is just the json not being terminated properly. - // ignore + // Assume that this is just the json not being terminated properly. + // Ignore } catch (IOException e1) { e1.printStackTrace(); } finally { diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java index 3b1213009..9904a1de2 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -27,7 +27,6 @@ import android.os.BatteryManager; import android.util.Log; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.R.string; import java.io.BufferedReader; import java.io.File; @@ -48,6 +47,7 @@ public final class ResearchLogUploader { private static final String TAG = ResearchLogUploader.class.getSimpleName(); private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; private final boolean mCanUpload; private final Context mContext; @@ -55,8 +55,6 @@ public final class ResearchLogUploader { private final URL mUrl; private final ScheduledExecutorService mExecutor; - private Runnable doUploadRunnable = new UploadRunnable(null, false); - public ResearchLogUploader(final Context context, final File filesDir) { mContext = context; mFilesDir = filesDir; @@ -93,11 +91,15 @@ public final class ResearchLogUploader { public void start() { if (mCanUpload) { - Log.d(TAG, "scheduling regular uploading"); - mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS, - UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } else { - Log.d(TAG, "no permission to upload"); + mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, + null /* callback */, false /* forceUpload */), + UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } + } + + public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); } } @@ -106,7 +108,8 @@ public final class ResearchLogUploader { // another upload happening right now, as it may have missed the latest changes. // TODO: Reschedule regular upload tests starting from now. if (mCanUpload) { - mExecutor.submit(new UploadRunnable(callback, true)); + mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, + true /* forceUpload */)); } } @@ -130,19 +133,33 @@ public final class ResearchLogUploader { } class UploadRunnable implements Runnable { + private final ResearchLog mLogToWaitFor; private final Callback mCallback; private final boolean mForceUpload; - public UploadRunnable(final Callback callback, final boolean forceUpload) { + public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, + final boolean forceUpload) { + mLogToWaitFor = logToWaitFor; mCallback = callback; mForceUpload = forceUpload; } @Override public void run() { + if (mLogToWaitFor != null) { + waitFor(mLogToWaitFor); + } doUpload(); } + private void waitFor(final ResearchLog researchLog) { + try { + researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private void doUpload() { if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { return; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 68bd98a23..77c498648 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -34,15 +34,18 @@ import android.graphics.Paint.Style; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.IBinder; +import android.os.SystemClock; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -64,11 +67,8 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.Locale; import java.util.UUID; @@ -94,24 +94,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); private static final boolean IS_SHOWING_INDICATOR = true; private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false; + public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; - private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user private static final ResearchLogger sInstance = new ResearchLogger(); // to write to a different filename, e.g., for testing, set mFile before calling start() /* package */ File mFilesDir; /* package */ String mUUIDString; /* package */ ResearchLog mMainResearchLog; - // The mIntentionalResearchLog records all events for the session, private or not (excepting - // passwords). It is written to permanent storage only if the user explicitly commands - // the system to do so. - /* package */ ResearchLog mIntentionalResearchLog; - // LogUnits are queued here and released only when the user requests the intentional log. - private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>(); + /* package */ ResearchLog mFeedbackLog; + /* package */ MainLogBuffer mMainLogBuffer; + /* package */ LogBuffer mFeedbackLogBuffer; private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -135,10 +132,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private Dictionary mDictionary; private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; - + private final Statistics mStatistics; private ResearchLogUploader mResearchLogUploader; + private LogUnit mCurrentLogUnit = new LogUnit(); + private ResearchLogger() { + mStatistics = Statistics.getInstance(); } public static ResearchLogger getInstance() { @@ -259,6 +259,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } + private void setLoggingAllowed(boolean enableLogging) { + if (mPrefs == null) { + return; + } + Editor e = mPrefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); + e.apply(); + sIsLogging = enableLogging; + } + private File createLogFile(File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(FILENAME_PREFIX).append('-'); @@ -268,10 +278,35 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } + private void checkForEmptyEditor() { + if (mInputMethodService == null) { + return; + } + final InputConnection ic = mInputMethodService.getCurrentInputConnection(); + if (ic == null) { + return; + } + final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(textBefore)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + final CharSequence textAfter = ic.getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(textAfter)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + if (textBefore != null && textAfter != null) { + mStatistics.setIsEmptyUponStarting(true); + } + } + private void start() { maybeShowSplashScreen(); updateSuspendedState(); requestIndicatorRedraw(); + mStatistics.reset(); + checkForEmptyEditor(); if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; @@ -280,73 +315,58 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); return; } - try { - if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mMainResearchLog.start(); - if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mIntentionalResearchLog.start(); - } catch (IOException e) { - Log.w(TAG, "Could not start ResearchLogger."); + if (mMainLogBuffer == null) { + mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); + mMainLogBuffer = new MainLogBuffer(mMainResearchLog); + mMainLogBuffer.setSuggest(mSuggest); + } + if (mFeedbackLogBuffer == null) { + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); + // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold + // the feedback LogUnit itself. + mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); } } /* package */ void stop() { - if (mMainResearchLog != null) { - mMainResearchLog.stop(); - } - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.stop(); - } - } + logStatistics(); + commitCurrentLogUnit(); - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; + if (mMainLogBuffer != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */); + mMainResearchLog.close(); + mMainLogBuffer = null; + } + if (mFeedbackLogBuffer != null) { + mFeedbackLog.close(); + mFeedbackLogBuffer = null; } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); - sIsLogging = enableLogging; } public boolean abort() { boolean didAbortMainLog = false; - if (mMainResearchLog != null) { - mMainResearchLog.abort(); + if (mMainLogBuffer != null) { + mMainLogBuffer.clear(); try { - mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortMainLog = mMainResearchLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - if (mMainResearchLog.isAbortSuccessful()) { - didAbortMainLog = true; - } - mMainResearchLog = null; + mMainLogBuffer = null; } - boolean didAbortIntentionalLog = false; - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.abort(); + boolean didAbortFeedbackLog = false; + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.clear(); try { - mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortFeedbackLog = mFeedbackLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. - } - if (mIntentionalResearchLog.isAbortSuccessful()) { - didAbortIntentionalLog = true; + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - mIntentionalResearchLog = null; - } - return didAbortMainLog && didAbortIntentionalLog; - } - - /* package */ void flush() { - if (mMainResearchLog != null) { - mMainResearchLog.flush(); + mFeedbackLogBuffer = null; } + return didAbortMainLog && didAbortFeedbackLog; } private void restart() { @@ -450,79 +470,39 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } - private ResearchLog mFeedbackLog; - private List<LogUnit> mFeedbackQueue; - private ResearchLog mSavedMainResearchLog; - private ResearchLog mSavedIntentionalResearchLog; - private List<LogUnit> mSavedIntentionalResearchLogQueue; - - private void saveLogsForFeedback() { - mFeedbackLog = mIntentionalResearchLog; - if (mIntentionalResearchLogQueue != null) { - mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue); - } else { - mFeedbackQueue = null; - } - mSavedMainResearchLog = mMainResearchLog; - mSavedIntentionalResearchLog = mIntentionalResearchLog; - mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue; - - mMainResearchLog = null; - mIntentionalResearchLog = null; - mIntentionalResearchLogQueue = new ArrayList<LogUnit>(); - } - - private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; + private static final String[] EVENTKEYS_FEEDBACK = { + "UserTimestamp", "contents" + }; public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (includeHistory && mFeedbackLog != null) { - try { - LogUnit headerLogUnit = new LogUnit(); - headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false); - mFeedbackLog.publishAllEvents(headerLogUnit); - for (LogUnit logUnit : mFeedbackQueue) { - mFeedbackLog.publishAllEvents(logUnit); - } - userFeedback(mFeedbackLog, feedbackContents); - mFeedbackLog.stop(); - try { - mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - mIntentionalResearchLog.start(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - mIntentionalResearchLogQueue.clear(); - } - mResearchLogUploader.uploadNow(null); - } else { - // create a separate ResearchLog just for feedback - final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir)); - try { - feedbackLog.start(); - userFeedback(feedbackLog, feedbackContents); - feedbackLog.stop(); - feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - mResearchLogUploader.uploadNow(null); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if (mFeedbackLogBuffer == null) { + return; + } + if (!includeHistory) { + mFeedbackLogBuffer.clear(); } + commitCurrentLogUnit(); + final LogUnit feedbackLogUnit = new LogUnit(); + final Object[] values = { + feedbackContents + }; + feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values, + false /* isPotentiallyPrivate */); + mFeedbackLogBuffer.shiftIn(feedbackLogUnit); + publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); + mFeedbackLog.close(); + mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; - mMainResearchLog = mSavedMainResearchLog; - mIntentionalResearchLog = mSavedIntentionalResearchLog; - mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue; } public void initSuggest(Suggest suggest) { mSuggest = suggest; + if (mMainLogBuffer != null) { + mMainLogBuffer.setSuggest(mSuggest); + } } private void setIsPasswordView(boolean isPasswordView) { @@ -530,7 +510,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging; + return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; } public void requestIndicatorRedraw() { @@ -577,13 +557,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; private static final Object[] EVENTKEYS_NULLVALUES = {}; - private LogUnit mCurrentLogUnit = new LogUnit(); - /** * Buffer a research log event, flagging it as privacy-sensitive. * @@ -599,10 +574,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, true); + mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */); } } + private void setCurrentLogUnitContainsDigitFlag() { + mCurrentLogUnit.setContainsDigit(); + } + /** * Buffer a research log event, flaggint it as not privacy-sensitive. * @@ -618,139 +597,54 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void enqueueEvent(final String[] keys, final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, false); + mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */); } } - // Used to track how often words are logged. Too-frequent logging can leak - // semantics, disclosing private data. - /* package for test */ static class LoggingFrequencyState { - private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; - private int mWordsRemainingToSkip; - private final int mFrequency; - - /** - * Tracks how often words may be uploaded. - * - * @param frequency 1=Every word, 2=Every other word, etc. - */ - public LoggingFrequencyState(int frequency) { - mFrequency = frequency; - mWordsRemainingToSkip = mFrequency; - } - - public void onWordLogged() { - mWordsRemainingToSkip = mFrequency; - } - - public void onWordNotLogged() { - if (mWordsRemainingToSkip > 1) { - mWordsRemainingToSkip--; + /* package for test */ void commitCurrentLogUnit() { + if (!mCurrentLogUnit.isEmpty()) { + if (mMainLogBuffer != null) { + mMainLogBuffer.shiftIn(mCurrentLogUnit); + if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, + true /* isIncludingPrivateData */); + mMainLogBuffer.resetWordCounter(); + } } + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); + } + mCurrentLogUnit = new LogUnit(); + Log.d(TAG, "commitCurrentLogUnit"); } + } - public boolean isSafeToLog() { - return mWordsRemainingToSkip <= 1; + /* package for test */ void publishLogBuffer(final LogBuffer logBuffer, + final ResearchLog researchLog, final boolean isIncludingPrivateData) { + LogUnit logUnit; + while ((logUnit = logBuffer.shiftOut()) != null) { + researchLog.publish(logUnit, isIncludingPrivateData); } } - /* package for test */ LoggingFrequencyState mLoggingFrequencyState = - new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); - - /* package for test */ boolean isPrivacyThreat(String word) { - // Current checks: - // - Word not in dictionary - // - Word contains numbers - // - Privacy-safe word not logged recently - if (TextUtils.isEmpty(word)) { - return false; - } - if (!mLoggingFrequencyState.isSafeToLog()) { - return true; - } + private boolean hasOnlyLetters(final String word) { final int length = word.length(); - boolean hasLetter = false; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(word, i); - if (Character.isDigit(codePoint)) { - return true; + final int codePoint = word.codePointAt(i); + if (!Character.isLetter(codePoint)) { + return false; } - if (Character.isLetter(codePoint)) { - hasLetter = true; - break; // Word may contain digits, but will only be allowed if in the dictionary. - } - } - if (hasLetter) { - if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { - mDictionary = mSuggest.getMainDictionary(); - } - if (mDictionary == null) { - // Can't access dictionary. Assume privacy threat. - return true; - } - return !(mDictionary.isValidWord(word)); } - // No letters, no numbers. Punctuation, space, or something else. - return false; + return true; } - private void onWordComplete(String word) { - if (isPrivacyThreat(word)) { - publishLogUnit(mCurrentLogUnit, true); - mLoggingFrequencyState.onWordNotLogged(); - } else { - publishLogUnit(mCurrentLogUnit, false); - mLoggingFrequencyState.onWordLogged(); - } - mCurrentLogUnit = new LogUnit(); - } - - private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { - if (!isAllowedToLog()) { - return; - } - if (mMainResearchLog == null) { - return; - } - if (isPrivacySensitive) { - mMainResearchLog.publishPublicEvents(logUnit); - } else { - mMainResearchLog.publishAllEvents(logUnit); - } - mIntentionalResearchLogQueue.add(logUnit); - } - - /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) { - publishLogUnit(mCurrentLogUnit, isPrivacySensitive); - } - - static class LogUnit { - private final List<String[]> mKeysList = new ArrayList<String[]>(); - private final List<Object[]> mValuesList = new ArrayList<Object[]>(); - private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>(); - - private void addLogAtom(final String[] keys, final Object[] values, - final Boolean isPotentiallyPrivate) { - mKeysList.add(keys); - mValuesList.add(values); - mIsPotentiallyPrivate.add(isPotentiallyPrivate); - } - - public void publishPublicEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - if (!mIsPotentiallyPrivate.get(i)) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); - } - } - } - - public void publishAllEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); - } + private void onWordComplete(final String word) { + Log.d(TAG, "onWordComplete: " + word); + if (word != null && word.length() > 0 && hasOnlyLetters(word)) { + mCurrentLogUnit.setWord(word); + mStatistics.recordWordEntered(); } + commitCurrentLogUnit(); } private static int scrubDigitFromCodePoint(int codePoint) { @@ -803,12 +697,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return WORD_REPLACEMENT_STRING; } - // Special methods related to startup, shutdown, logging itself - - private static final String[] EVENTKEYS_INTENTIONAL_LOG = { - "IntentionalLog" - }; - private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" @@ -816,9 +704,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs) { final ResearchLogger researchLogger = getInstance(); - if (researchLogger.mInFeedbackDialog) { - researchLogger.saveLogsForFeedback(); - } researchLogger.start(); if (editorInfo != null) { final Context context = researchLogger.mInputMethodService; @@ -846,34 +731,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; - private void userFeedback(ResearchLog researchLog, String feedbackContents) { - // this method is special; it directs the feedbackContents to a particular researchLog - final LogUnit logUnit = new LogUnit(); - final Object[] values = { - feedbackContents - }; - logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false); - researchLog.publishAllEvents(logUnit); - } - // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { @@ -908,51 +769,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEOnCodeInput", "code", "x", "y" }; public static void latinIME_onCodeInput(final int code, final int x, final int y) { - final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - } - - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; + final long time = SystemClock.uptimeMillis(); final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { final Object[] values = { - length + Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } + researchLogger.mStatistics.recordChar(code, time); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { @@ -979,6 +805,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { + // Capture the TextView contents. This will trigger onUpdateSelection(), so we + // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, + // it can tell that it was generated by the logging code, and not by the user, and + // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1013,9 +843,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); - // Play it safe. Remove privacy-sensitive events. - researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); - researchLogger.mCurrentLogUnit = new LogUnit(); + researchLogger.commitCurrentLogUnit(); getInstance().stop(); } } @@ -1048,29 +876,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs, int x, int y) { - final Object[] values = { - index, cs, x, y - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1096,21 +901,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1118,15 +908,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)) }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { + "LatinIMESwapSwapperAndSpace" }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpace() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1245,6 +1038,128 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { + "RichInputConnectionCommitCompletion", "completionInfo" + }; + public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { + final Object[] values = { + completionInfo + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); + } + + // Disabled for privacy-protection reasons. Because this event comes after + // richInputConnection_commitText, which is the event used to separate LogUnits, the + // data in this event can be associated with the next LogUnit, revealing information + // about the current word even if it was supposed to be suppressed. The occurrance of + // autocorrection can be determined by examining the difference between the text strings in + // the last call to richInputConnection_setComposingText before + // richInputConnection_commitText, so it's not a data loss. + // TODO: Figure out how to log this event without loss of privacy. + /* + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { + "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection" + }; + */ + public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + /* + final String typedWord = correctionInfo.getOldText().toString(); + final String autoCorrection = correctionInfo.getNewText().toString(); + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + */ + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { + "RichInputConnectionCommitText", "typedWord", "newCursorPosition" + }; + public static void richInputConnection_commitText(final CharSequence typedWord, + final int newCursorPosition) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord, newCursorPosition + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, + values); + researchLogger.onWordComplete(scrubbedWord); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { + "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" + }; + public static void richInputConnection_deleteSurroundingText(final int beforeLength, + final int afterLength) { + final Object[] values = { + beforeLength, afterLength + }; + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { + "RichInputConnectionFinishComposingText" + }; + public static void richInputConnection_finishComposingText() { + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { + "RichInputConnectionPerformEditorAction", "imeActionNext" + }; + public static void richInputConnection_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { + "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" + }; + public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { + final Object[] values = { + keyEvent.getEventTime(), + keyEvent.getAction(), + keyEvent.getKeyCode() + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, + values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { + "RichInputConnectionSetComposingText", "text", "newCursorPosition" + }; + public static void richInputConnection_setComposingText(final CharSequence text, + final int newCursorPosition) { + if (text == null) { + throw new RuntimeException("setComposingText is null"); + } + final Object[] values = { + text, newCursorPosition + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, + values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { + "RichInputConnectionSetSelection", "from", "to" + }; + public static void richInputConnection_setSelection(final int from, final int to) { + final Object[] values = { + from, to + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, + values); + } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; @@ -1277,4 +1192,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void userTimestamp() { getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); } + + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java new file mode 100644 index 000000000..eab465aa2 --- /dev/null +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.research; + +import com.android.inputmethod.keyboard.Keyboard; + +public class Statistics { + // Number of characters entered during a typing session + int mCharCount; + // Number of letter characters entered during a typing session + int mLetterCount; + // Number of number characters entered + int mNumberCount; + // Number of space characters entered + int mSpaceCount; + // Number of delete operations entered (taps on the backspace key) + int mDeleteKeyCount; + // Number of words entered during a session. + int mWordCount; + // Whether the text field was empty upon editing + boolean mIsEmptyUponStarting; + boolean mIsEmptinessStateKnown; + + // Timers to count average time to enter a key, first press a delete key, + // between delete keys, and then to return typing after a delete key. + final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); + final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); + + static class AverageTimeCounter { + int mCount; + int mTotalTime; + + public void reset() { + mCount = 0; + mTotalTime = 0; + } + + public void add(long deltaTime) { + mCount++; + mTotalTime += deltaTime; + } + + public int getAverageTime() { + if (mCount == 0) { + return 0; + } + return mTotalTime / mCount; + } + } + + // 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 + + // The last time that a tap was performed + private long mLastTapTime; + // The type of the last keypress (delete key or not) + boolean mIsLastKeyDeleteKey; + + private static final Statistics sInstance = new Statistics(); + + public static Statistics getInstance() { + return sInstance; + } + + private Statistics() { + reset(); + } + + public void reset() { + mCharCount = 0; + mLetterCount = 0; + mNumberCount = 0; + mSpaceCount = 0; + mDeleteKeyCount = 0; + mWordCount = 0; + mIsEmptyUponStarting = true; + mIsEmptinessStateKnown = false; + mKeyCounter.reset(); + mBeforeDeleteKeyCounter.reset(); + mDuringRepeatedDeleteKeysCounter.reset(); + mAfterDeleteKeyCounter.reset(); + + mLastTapTime = 0; + mIsLastKeyDeleteKey = false; + } + + public void recordChar(int codePoint, long time) { + final long delta = time - mLastTapTime; + if (codePoint == Keyboard.CODE_DELETE) { + mDeleteKeyCount++; + if (delta < MIN_DELETION_INTERMISSION) { + if (mIsLastKeyDeleteKey) { + mDuringRepeatedDeleteKeysCounter.add(delta); + } else { + mBeforeDeleteKeyCounter.add(delta); + } + } + mIsLastKeyDeleteKey = true; + } else { + mCharCount++; + if (Character.isDigit(codePoint)) { + mNumberCount++; + } + if (Character.isLetter(codePoint)) { + mLetterCount++; + } + if (Character.isSpaceChar(codePoint)) { + mSpaceCount++; + } + if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { + mAfterDeleteKeyCounter.add(delta); + } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { + mKeyCounter.add(delta); + } + mIsLastKeyDeleteKey = false; + } + mLastTapTime = time; + } + + public void recordWordEntered() { + mWordCount++; + } + + public void setIsEmptyUponStarting(final boolean isEmpty) { + mIsEmptyUponStarting = isEmpty; + mIsEmptinessStateKnown = true; + } +} |