diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index cc4c74441..e0eecfc7d 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -1,8 +1,6 @@ - - diff --git a/java/proguard.flags b/java/proguard.flags index 9096855e8..914bd7595 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -14,3 +14,11 @@ void waitUntilUpdateDBDone(); void waitForDictionaryLoading(); } + +-keep class com.android.inputmethod.latin.AutoCorrection { + java.lang.CharSequence getAutoCorrectionWord(); +} + +-keep class com.android.inputmethod.latin.Utils { + boolean equalsIgnoreCase(...); +} diff --git a/java/res/drawable-hdpi/key_hint_comma_holo.9.png b/java/res/drawable-hdpi/key_hint_comma_holo.9.png index 47ae5efaf..da0d6fdd6 100644 Binary files a/java/res/drawable-hdpi/key_hint_comma_holo.9.png and b/java/res/drawable-hdpi/key_hint_comma_holo.9.png differ diff --git a/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png new file mode 100644 index 000000000..1f2f70762 Binary files /dev/null and b/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png differ diff --git a/java/res/drawable-hdpi/key_hint_minus_holo.9.png b/java/res/drawable-hdpi/key_hint_minus_holo.9.png new file mode 100644 index 000000000..2c34ef90b Binary files /dev/null and b/java/res/drawable-hdpi/key_hint_minus_holo.9.png differ diff --git a/java/res/drawable-hdpi/key_hint_minus_large_holo.9.png b/java/res/drawable-hdpi/key_hint_minus_large_holo.9.png new file mode 100644 index 000000000..0df056ee1 Binary files /dev/null and b/java/res/drawable-hdpi/key_hint_minus_large_holo.9.png differ diff --git a/java/res/drawable-hdpi/key_hint_underscore_holo.9.png b/java/res/drawable-hdpi/key_hint_underscore_holo.9.png new file mode 100644 index 000000000..e4f271918 Binary files /dev/null and b/java/res/drawable-hdpi/key_hint_underscore_holo.9.png differ diff --git a/java/res/drawable-hdpi/key_hint_underscore_large_holo.9.png b/java/res/drawable-hdpi/key_hint_underscore_large_holo.9.png new file mode 100644 index 000000000..dad34fc72 Binary files /dev/null and b/java/res/drawable-hdpi/key_hint_underscore_large_holo.9.png differ diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png index b3af0c638..471bd0b86 100644 Binary files a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png and b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png b/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png index 9ab5dadac..da0d6fdd6 100644 Binary files a/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png and b/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png new file mode 100644 index 000000000..1f2f70762 Binary files /dev/null and b/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_minus_holo.9.png b/java/res/drawable-land-hdpi/key_hint_minus_holo.9.png new file mode 100644 index 000000000..2c34ef90b Binary files /dev/null and b/java/res/drawable-land-hdpi/key_hint_minus_holo.9.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_minus_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_minus_large_holo.9.png new file mode 100644 index 000000000..0df056ee1 Binary files /dev/null and b/java/res/drawable-land-hdpi/key_hint_minus_large_holo.9.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_underscore_holo.9.png b/java/res/drawable-land-hdpi/key_hint_underscore_holo.9.png new file mode 100644 index 000000000..e4f271918 Binary files /dev/null and b/java/res/drawable-land-hdpi/key_hint_underscore_holo.9.png differ diff --git a/java/res/drawable-land-hdpi/key_hint_underscore_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_underscore_large_holo.9.png new file mode 100644 index 000000000..dad34fc72 Binary files /dev/null and b/java/res/drawable-land-hdpi/key_hint_underscore_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_at_holo.9.png b/java/res/drawable-land-mdpi/key_hint_at_holo.9.png index d1ea313c0..5b946ff9b 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_at_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_at_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png index 786bbc5de..852f899ed 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png index 12ce26758..1d9346e6f 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png index a51bada57..17e9091b4 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png index f9391623b..c2a913c04 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png new file mode 100644 index 000000000..846f213f1 Binary files /dev/null and b/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png index a14623dc0..ce8e8de43 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png index ce52d3a02..035dcf85d 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_minus_holo.9.png b/java/res/drawable-land-mdpi/key_hint_minus_holo.9.png new file mode 100644 index 000000000..e59a31587 Binary files /dev/null and b/java/res/drawable-land-mdpi/key_hint_minus_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_minus_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_minus_large_holo.9.png new file mode 100644 index 000000000..52c28dd87 Binary files /dev/null and b/java/res/drawable-land-mdpi/key_hint_minus_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png index a80c03169..931390b45 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png index e8daaf085..e6f9f8a9c 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_question_holo.9.png b/java/res/drawable-land-mdpi/key_hint_question_holo.9.png index 2b71d744f..6cbeb5993 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_question_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_question_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png index 041336832..bfd58de09 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png index 486e5e19d..3b361b71c 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png index 49770314f..2a08aa12e 100644 Binary files a/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png and b/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_underscore_holo.9.png b/java/res/drawable-land-mdpi/key_hint_underscore_holo.9.png new file mode 100644 index 000000000..52e871e0a Binary files /dev/null and b/java/res/drawable-land-mdpi/key_hint_underscore_holo.9.png differ diff --git a/java/res/drawable-land-mdpi/key_hint_underscore_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_underscore_large_holo.9.png new file mode 100644 index 000000000..ee0e83578 Binary files /dev/null and b/java/res/drawable-land-mdpi/key_hint_underscore_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_at_holo.9.png b/java/res/drawable-mdpi/key_hint_at_holo.9.png index e596144f9..5b946ff9b 100644 Binary files a/java/res/drawable-mdpi/key_hint_at_holo.9.png and b/java/res/drawable-mdpi/key_hint_at_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-mdpi/key_hint_at_large_holo.9.png index 63d071405..852f899ed 100644 Binary files a/java/res/drawable-mdpi/key_hint_at_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_at_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-mdpi/key_hint_colon_holo.9.png index 12ce26758..1d9346e6f 100644 Binary files a/java/res/drawable-mdpi/key_hint_colon_holo.9.png and b/java/res/drawable-mdpi/key_hint_colon_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png index a51bada57..17e9091b4 100644 Binary files a/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-mdpi/key_hint_comma_holo.9.png index 82e4a93b7..c2a913c04 100644 Binary files a/java/res/drawable-mdpi/key_hint_comma_holo.9.png and b/java/res/drawable-mdpi/key_hint_comma_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png new file mode 100644 index 000000000..846f213f1 Binary files /dev/null and b/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png index b57351b57..ce8e8de43 100644 Binary files a/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png and b/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png index a8a17eb44..035dcf85d 100644 Binary files a/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_minus_holo.9.png b/java/res/drawable-mdpi/key_hint_minus_holo.9.png new file mode 100644 index 000000000..e59a31587 Binary files /dev/null and b/java/res/drawable-mdpi/key_hint_minus_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_minus_large_holo.9.png b/java/res/drawable-mdpi/key_hint_minus_large_holo.9.png new file mode 100644 index 000000000..52c28dd87 Binary files /dev/null and b/java/res/drawable-mdpi/key_hint_minus_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-mdpi/key_hint_plus_holo.9.png index a80c03169..931390b45 100644 Binary files a/java/res/drawable-mdpi/key_hint_plus_holo.9.png and b/java/res/drawable-mdpi/key_hint_plus_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png index e8daaf085..e6f9f8a9c 100644 Binary files a/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_question_holo.9.png b/java/res/drawable-mdpi/key_hint_question_holo.9.png index 9491d878f..6cbeb5993 100644 Binary files a/java/res/drawable-mdpi/key_hint_question_holo.9.png and b/java/res/drawable-mdpi/key_hint_question_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-mdpi/key_hint_question_large_holo.9.png index c9902ffa8..bfd58de09 100644 Binary files a/java/res/drawable-mdpi/key_hint_question_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_question_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-mdpi/key_hint_quote_holo.9.png index a036421d8..3b361b71c 100644 Binary files a/java/res/drawable-mdpi/key_hint_quote_holo.9.png and b/java/res/drawable-mdpi/key_hint_quote_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png index 5381b1337..2a08aa12e 100644 Binary files a/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png and b/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_underscore_holo.9.png b/java/res/drawable-mdpi/key_hint_underscore_holo.9.png new file mode 100644 index 000000000..52e871e0a Binary files /dev/null and b/java/res/drawable-mdpi/key_hint_underscore_holo.9.png differ diff --git a/java/res/drawable-mdpi/key_hint_underscore_large_holo.9.png b/java/res/drawable-mdpi/key_hint_underscore_large_holo.9.png new file mode 100644 index 000000000..ee0e83578 Binary files /dev/null and b/java/res/drawable-mdpi/key_hint_underscore_large_holo.9.png differ diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png index 8233623e3..784a45054 100644 Binary files a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png and b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png differ diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_holo.png b/java/res/drawable-mdpi/sym_keyboard_smiley_holo.png new file mode 100644 index 000000000..594fe211c Binary files /dev/null and b/java/res/drawable-mdpi/sym_keyboard_smiley_holo.png differ diff --git a/java/res/layout/recognition_status.xml b/java/res/layout/recognition_status.xml index 9474d6f58..45f68974a 100644 --- a/java/res/layout/recognition_status.xml +++ b/java/res/layout/recognition_status.xml @@ -45,7 +45,7 @@ android:layout_height="0dip" android:layout_width="match_parent" android:layout_weight="1.0"> - "اهتزاز عند الضغط على مفتاح" "صوت عند الضغط على مفتاح" "انبثاق عند الضغط على المفاتيح" - "إعدادات اقتراحات الكلمات" + "عام" + "تصحيح النص" "استخدام الأحرف الكبيرة تلقائيًا" "إصلاحات سريعة" "تصحيح الأخطاء المكتوبة الشائعة" - - - - - - - - - - + "عرض اقتراحات التصحيح" + "عرض الكلمات المقترحة أثناء الكتابة" + "عرض دومًا" + "عرض في وضع رأسي" + "إخفاء دومًا" "عرض مفتاح الإعدادات" "تلقائي" "إظهار بشكل دائم" "إخفاء دومًا" - "اقتراحات الكلمات" - "تصحيح الكلمة السابقة تلقائيًا" - - - - - - + "التصحيح التلقائي" + "تؤدي المسافة والترقيم إلى تصحيح الكلمات المكتوبة بشكل غير صحيح" + "إيقاف" + "معتدل" + "حاد" "اقتراحات ثنائية" "استخدام الكلمة السابقة لتحسين الاقتراح" "%s : تم الحفظ" @@ -59,16 +52,27 @@ "التالي" "تم" "إرسال" - - + "أ ب ج د" "المزيد" "توقف مؤقت" "انتظار" + "حذف" + "رجوع" + "الإعدادات" + "العالي" + "مسافة" + "الرموز" + "Tab" + "الإدخال الصوتي" + "تشغيل الرموز" + "إيقاف الرموز" + "تشغيل العالي" + "إيقاف العالي" "الإدخال الصوتي" "الإدخال الصوتي غير معتمد حاليًا للغتك، ولكنه يعمل باللغة الإنجليزية." - "الإدخال الصوتي هو ميزة تجريبية تستخدم التعرف على الكلام المتصل في Google." - "لتشغيل الإدخال الصوتي، انتقل إلى إعدادات لوحة المفاتيح." - "لاستخدام الإدخال الصوتي، اضغط على زر الميكروفون أو مرر إصبعك عبر لوحة المفاتيح على الشاشة." + "يستخدم الإدخال الصوتي خاصية التعرف على الكلام من Google. تنطبق ""سياسة خصوصية الجوال""." + "لإيقاف الإدخال الصوتي، انتقل إلى إعدادات طريقة الإدخال." + "لاستخدام الإدخال الصوتي، اضغط على زر الميكروفون." "تحدث الآن" "العمل" @@ -85,18 +89,12 @@ "إلغاء" "موافق" "الإدخال الصوتي" - - - - - - - - - - - - + "لوحة مفاتيح رئيسية" + "لوحة مفاتيح الرموز" + "إيقاف" + "ميكروفون على لوحة مفاتيح رئيسية" + "ميكروفون على لوحة مفاتيح الرموز" + "الإدخال الصوتي مُعطل" "تحديد طريقة الإرسال" "لغات الإدخال" "مرر إصبعك على مفتاح المسافة لتغيير اللغة" @@ -105,44 +103,40 @@ "تمكين ملاحظات المستخدم" "المساعدة في تحسين محرر طريقة الإرسال هذا من خلال إرسال إحصاءات الاستخدام وتقارير الأعطال تلقائيًا إلى Google." "المس لتصحيح الكلمات" - "المس الكلمات المدخلة لتصحيحها" + "المس الكلمات التي تم إدخالها لتصحيحها، وذلك فقط عندما تكون الاقتراحات مرئية." "مظهر لوحة المفاتيح" "لوحة مفاتيح تشيكية" "لوحة مفاتيح دانماركية" - "لوحة مفاتيح ألمانية" - "لوحة مفاتيح إنجليزية (بريطانيا العظمى)" - "لوحة مفاتيح إنجليزية (الولايات المتحدة)" + "لوحة مفاتيح ألمانية" + "لوحة مفاتيح إنجليزية (بريطانيا)" + "لوحة مفاتيح إنجليزية (الولايات المتحدة)" "لوحة مفاتيح إسبانية" - - + "لوحة مفاتيح إسبانية (الولايات المتحدة)" "لوحة مفاتيح فرنسية" - - - - - "لوحة مفاتيح إيطالية" + "لوحة مفاتيح فرنسية (كندا)" + "لوحة مفاتيح فرنسية (سويسرا)" + "لوحة مفاتيح إيطالية" "لوحة مفاتيح نرويجية" "لوحة مفاتيح بولندية" "لوحة مفاتيح روسية" "لوحة مفاتيح صربية" "لوحة مفاتيح سويدية" - "صوت تشيكي" - "صوت ألماني" - "صوت إنجليزي (أستراليا)" - "صوت إنجليزي (بريطانيا العظمى)" - "صوت إنجليزي (الهند)" - "صوت إنجليزي (نيوزلندا)" - "صوت إنجليزي (الولايات المتحدة)" + "صوت أفريقاني" + "صوت تشيكي" + "صوت ألماني" + "صوت إنجليزي" "صوت إسباني" "صوت فرنسي" + "صوت إيطالي" "صوت ياباني" "صوت كوري" + "صوت هولندي" "صوت بولندي" "صوت برتغالي" "صوت روسي" "صوت تركي" - "صوت صيني (الصين)" - "صوت صيني (تايوان)" - - + "صوت صيني، يوي" + "صوت صيني، الماندارين" + "صوت زولوي" + "وضع دراسة الاستخدام" diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 8940adc6d..418bb18a1 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -26,32 +26,25 @@ "Да вибрира при натискане на клавиш" "Звук при натискане на клавиш" "Изскачащ прозорец при натискане на клавиш" - "Настройки за предложения на думи" + "Общи" + "Корекция на текста" "Автоматично поставяне на главни букви" "Бързи корекции" "Коригира най-честите грешки при въвеждане" - - - - - - - - - - + "Показване на предложения за поправка" + "Показване на предложения, докато пишете" + "Винаги да се показва" + "Показване с вертикална ориентация" + "Винаги да се скрива" "Показване на клавиша за настройки" "Автоматично" "Да се показва винаги" "Да се скрива винаги" - "Предложения на думи" - "Автоматично коригиране на предишната дума" - - - - - - + "Автоко" + "Клавишът за интервал и пунктуация авт. поправя сгрешени думи" + "Изкл." + "Умерено" + "Агресивно" "Предложения за биграми" "Използване на предишната дума за подобряване на предложението" "%s : Запазено" @@ -59,16 +52,27 @@ "Напред" "Готово" "Изпращане" - - + "АБВ" "Още" "Пауза" "Чака" + "Изтриване" + "Return" + "Настройки" + "Shift" + "Интервал" + "Символи" + "Tab" + "Гласово въвеждане" + "Символите са включени" + "Символите са изключени" + "Shift е включен" + "Shift е изключен" "Гласово въвеждане" "За вашия език понастоящем не се поддържа гласово въвеждане, но можете да го използвате на английски." - "Гласовото въвеждане е експериментална функция, използваща разпознаването на реч в мрежата на Google." - "За да изключите гласовото въвеждане, отворете настройките на клавиатурата." - "За да използвате гласово въвеждане, натиснете бутона на микрофона или плъзнете пръст през екранната клавиатура." + "Гласовото въвеждане използва функцията на Google за разпознаване на говор. В сила е ""Декларацията за поверителност за мобилни устройства""." + "За да изключите гласовото въвеждане, отворете настройките за метода за въвеждане." + "За да използвате гласово въвеждане, натиснете бутона на микрофона." "Говорете сега" "Обработва се" @@ -85,18 +89,12 @@ "Отказ" "OK" "Гласово въвеждане" - - - - - - - - - - - - + "На осн. клавиатура" + "На клав. на симв." + "Изкл." + "Микр. на осн. клав." + "Микр. на клав. на симв." + "Глас. въвежд. е деакт." "Избор на метод на въвеждане" "Входни езици" "Плъзнете пръст по клавиша за интервал за промяна на езика" @@ -105,44 +103,40 @@ "Активиране на отзивите от потребителите" "Помогнете за подобряването на този редактор за въвеждане чрез автоматично изпращане до Google на статистически данни за употребата и сигнали за сривове." "Докоснете, за да поправите думите" - "Докоснете въведените думи, за да ги поправите" + "Докоснете въведените думи, за да ги поправите – само когато предложенията са видими" "Тема на клавиатурата" "чешка клавиатура" "датска клавиатура" - "немска клавиатура" - "английска (Великобритания) клавиатура" - "английска (САЩ) клавиатура" + "немска клавиатура" + "английска (Великобрит.) клавиатура" + "английска (САЩ) клавиатура" "испанска клавиатура" - - + "испанска (САЩ) клавиатура" "френска клавиатура" - - - - - "италианска клавиатура" + "френска (Канада) клавиатура" + "френска (Швейцария) клавиатура" + "италианска клавиатура" "норвежка клавиатура" "нидерландска клавиатура" "руска клавиатура" "сръбска клавиатура" "шведска клавиатура" - "чешки, гласово" - "немски, гласово" - "английски (Австралия), гласово" - "английски (Великобритания), гласово" - "английски (Индия), гласово" - "английски (Нова Зеландия), гласово" - "английски (САЩ), гласово" + "африкаанс, гласово" + "чешки, гласово" + "немски, гласово" + "английски, гласово" "испански, гласово" "френски, гласово" + "италиански, гласово" "японски, гласово" "корейски, гласово" + "нидерландски, гласово" "полски, гласово" "португалски, гласово" "руски, гласово" "турски, гласово" - "китайски (Китай), гласово" - "китайски (Тайван), гласово" - - + "китайски, кантонски, гласово" + "китайски, мандарин, гласово" + "зулуски, гласово" + "Режим за изследване на използваемостта" diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index cd9191e06..1bfa3a9ab 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -26,32 +26,25 @@ "Vibra en prémer tecles" "So en prémer una tecla" "Finestra emergent en prémer un botó" - "Configuració de suggeriment de paraules" + "General" + "Correcció de text" "Majúscules automàtiques" "Correccions ràpides" "Corregeix els errors d\'ortografia habituals" - - - - - - - - - - + "Mostra els suggeriments de correcció" + "Mostra paraules suggerides mentre s\'escriu" + "Mostra sempre" + "Mostra en mode vertical" + "Amaga sempre" "Mostra la tecla de configuració" "Automàtic" "Mostra sempre" "Amaga sempre" - "Suggeriments de paraules" - "Corregeix automàticament la paraula anterior" - - - - - - + "Correcció automàtica" + "Barra espaiadora i punt. correg. autom. paraules mal escrites" + "Desactiva" + "Moderada" + "Total" "Suggeriments Bigram" "Utilitza la paraula anterior per millorar el suggeriment" "%s: desada" @@ -59,16 +52,27 @@ "Següent" "Fet" "Envia" - - + "ABC" "Més" "Pausa" "Espera" + "Suprimeix" + "Retorn" + "Configuració" + "Majúscules" + "Espai" + "Símbols" + "Tabulador" + "Entrada de veu" + "Símbols activats" + "Símbols desactivats" + "Majúscules activades" + "Majúscules desactivades" "Entrada de veu" "Actualment, l\'entrada de veu no és compatible amb el vostre idioma, però funciona en anglès." - "L\'entrada de veu és una funció experimental que utilitza el reconeixement de la parla en xarxa de Google." - "Per desactivar l\'entada de veu, aneu a la configuració del teclat." - "Per utilitzar l\'entrada de veu, premeu el botó del micròfon o feu lliscar el dit pel teclat en pantalla." + "L\'entrada de veu utilitza el reconeixement de veu de Google. S\'hi aplica la ""Política de privadesa de Google Mobile""." + "Per desactivar l\'entada de veu, vés a la configuració del mètode d\'entrada." + "Per utilitzar l\'entrada de veu, prem el botó del micròfon." "Parleu ara" "S\'està treballant" @@ -85,18 +89,12 @@ "Cancel·la" "D\'acord" "Entrada de veu" - - - - - - - - - - - - + "Al teclat principal" + "Al tecl. de símb." + "Desactivada" + "Micro en tecl. princ." + "Micro en tecl. símb." + "Entr. veu desactiv." "Selecciona el mètode d\'entrada" "Idiomes d\'entrada" "Feu lliscar el dit a la barra espaiadora per canviar l\'idioma" @@ -105,44 +103,40 @@ "Activa els comentaris de l\'usuari" "Ajuda a millorar aquest editor de mètodes d\'entrada enviant automàticament estadístiques d\'ús i informes de bloqueigs a Google." "Toca per corregir paraules" - "Toca les paraules introduïdes per corregir-les" + "Toca les paraules introduïdes per corregir-les, només quan els suggeriments siguin visibles" "Tema del teclat" "Teclat txec" "Teclat danès" - "Teclat alemany " - "Teclat anglès (Gran Bretanya)" - "Teclat anglès (Estats Units)" + "Teclat alemany" + "Teclat anglès (Regne Unit)" + "Teclat anglès (Estats Units)" "Teclat espanyol" - - + "Teclat espanyol (Estats Units)" "Teclat francès" - - - - - "Teclat italià " + "Teclat francès (Canadà)" + "Teclat francès (Suïssa)" + "Teclat italià" "Teclat noruec" "Teclat holandès" "Teclat rus" "Teclat serbi" "Teclat suec" - "Veu txeca" - "Veu alemanya" - "Veu anglesa (Austràlia)" - "Veu anglesa (Gran Bretanya)" - "Veu anglesa (Índia)" - "Veu anglesa (Nova Zelanda)" - "Veu anglesa (Estats Units)" + "Veu en afrikaans" + "Veu txeca" + "Veu alemanya" + "Veu anglesa" "Veu espanyola" "Veu francesa" + "Veu italiana" "Veu japonesa" "Veu coreana" + "Veu holandesa" "Veu polonesa" "Veu portuguesa" "Veu russa" "Veu turca" - "Veu xinesa (Xina)" - "Veu xinesa (Taiwan)" - - + "Veu xinesa (Yue)" + "Veu xinesa (mandarí)" + "Veu en zulu" + "Mode d\'estudi d\'usabilitat" diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 70f66ecd7..b0cb7daf7 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -26,32 +26,25 @@ "Při stisku klávesy vibrovat" "Zvuk při stisku klávesy" "Zobrazit znaky při stisku klávesy" - "Nastavení návrhů slov" + "Obecné" + "Oprava textu" "Velká písmena automaticky" "Rychlé opravy" "Opravuje nejčastější chyby při psaní" - - - - - - - - - - + "Zobrazit návrhy oprav" + "Zobrazovat navržená slova během psaní" + "Vždy zobrazovat" + "Zobrazit v režimu na výšku" + "Vždy skrývat" "Zobrazit klávesu Nastavení" "Automaticky" "Vždy zobrazovat" "Vždy skrývat" - "Návrhy slov" - "Automaticky opravit předchozí slovo" - - - - - - + "Automatické opravy" + "Stisknutím mezerníku a interpunkce se automaticky opravují chybně napsaná slova" + "Vypnuto" + "Mírné" + "Agresivní" "Návrh Bigram" "Použít předchozí slovo ke zlepšení návrhu" "%s: Uloženo" @@ -59,16 +52,27 @@ "Další" "Hotovo" "Odeslat" - - + "ABC" "Další" "Pauza" "Čekat" + "Smazat" + "Enter" + "Nastavení" + "Shift" + "mezera" + "Symboly" + "Karta" + "Hlasový vstup" + "Symboly jsou zapnuty" + "Symboly jsou vypnuty" + "Režim Shift je zapnutý" + "Režim Shift je vypnutý" "Hlasový vstup" "Pro váš jazyk aktuálně není hlasový vstup podporován, ale funguje v angličtině." - "Hlasový vstup je experimentální funkce, která využívá síťové rozpoznávání řeči společnosti Google." - "Chcete-li vypnout hlasový vstup, přejděte do nastavení klávesnice." - "Chcete-li použít hlasový vstup, stiskněte tlačítko mikrofonu nebo přejeďte prstem přes klávesnici na obrazovce." + "Hlasový vstup používá rozpoznávání hlasu Google a vztahují se na něj ""Zásady ochrany osobních údajů pro mobilní služby""." + "Chcete-li vypnout hlasový vstup, přejděte do nastavení metod vstupu." + "Chcete-li použít hlasový vstup, stiskněte tlačítko mikrofonu." "Mluvte" "Probíhá zpracování" @@ -85,18 +89,12 @@ "Zrušit" "OK" "Hlasový vstup" - - - - - - - - - - - - + "Na hlavní klávesnici" + "Na klávesnici se symb." + "Vypnuto" + "Mikr. na hlav. kláv." + "Mikr. na kláv. se symb." + "Hlasový vstup vypnut" "Výběr metody zadávání dat" "Vstupní jazyky" "Jazyk můžete změnit posunutím prstu po mezerníku." @@ -104,75 +102,41 @@ "K dispozici je slovník" "Aktivovat zasílání statistik užívání a zpráv o selhání" "Automatickým zasíláním statistik o užívání editoru zadávání dat a zpráv o jeho selhání do Googlu můžete přispět k vylepšení tohoto nástroje." - "Dotykem aktivujete opravy" - "Dotknete-li se slov, která jste napsali, můžete je opravit." + "Dotykem aktivovat opravy" + "Klepnutím na zadaná slova tato slova opravíte, musí však být viditelné návrhy." "Motiv klávesnice" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Klávesnice – čeština" + "Klávesnice – dánština" + "Klávesnice – němčina" + "Klávesnice – angličtina (VB)" + "Klávesnice – angličtina (USA)" + "Klávesnice – španělština" + "Klávesnice – španělština (USA)" + "Klávesnice – francouzština" + "Klávesnice – francouzština (Kanada)" + "Klávesnice – francouzština (Švýc.)" + "Klávesnice – italština" + "Klávesnice – norština" + "Klávesnice – holandština" + "Klávesnice – ruština" + "Klávesnice – srbština" + "Klávesnice – švédština" + "Voice – afrikánština" + "Voice – čeština" + "Voice – němčina" + "Voice – angličtina" + "Voice – španělština" + "Voice – francouzština" + "Hlasový vstup – italština" + "Voice – japonština" + "Voice – korejština" + "Hlasový vstup – holandština" + "Voice – polština" + "Voice – portugalština" + "Voice – ruština" + "Voice – turečtina" + "Voice – čínština, kantonština" + "Voice – čínština, mandarínština" + "Voice – zulu" + "Režim studie použitelnosti" diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index d748d10fb..eb79888d6 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -26,32 +26,25 @@ "Vibration ved tastetryk" "Lyd ved tastetryk" "Popup ved tastetryk" - "Indstillinger for ordforslag" + "Generelt" + "Tekstkorrigering" "Skriv aut. med stort" "Hurtige løsninger" "Retter almindelige stavefejl" - - - - - - - - - - + "Vis rettelsesforslag" + "Vis ordforslag under indtastning" + "Vis altid" + "Vis i portrættilstand" + "Skjul altid" "Vis indstillingsnøgle" "Automatisk" "Vis altid" "Skjul altid" - "Ordforslag" - "Ret automatisk det forrige ord" - - - - - - + "Automatisk retning" + "Mellemrumstast og tegnsætning retter automatisk forkerte ord" + "Fra" + "Beskeden" + "Aggressiv" "Bigram-forslag" "Brug forrige ord for at forbedre forslag" "%s: Gemt" @@ -59,16 +52,27 @@ "Næste" "Udfør" "Send" - - + "ABC" "Mere" "Pause" "Vent" + "Slet" + "Tilbage" + "Indstillinger" + "Shift" + "Mellemrum" + "Symboler" + "Fane" + "Stemmeinput" + "Symboler: Til" + "Symboler: Fra" + "Shift: Til" + "Shift: Fra" "Stemmeinput" "Stemmeinput understøttes i øjeblikket ikke for dit sprog, men fungerer på engelsk." - "Stemme-input er en funktion på forsøgsbasis, som bruger Googles netværksstemmegenkendelse." - "Slå stemmeinput fra i indstillingerne for tastaturet." - "For at bruge stemme-input skal du trykke på knappen mikrofon eller lade glide fingeren hen over skærmtastaturet." + "Stemmeinput anvender Googles stemmegenkendelse. ""Fortrolighedspolitikken for mobilenheder"" gælder." + "Slå stemmeinput fra i indstillingerne for inputmetode." + "Brug stemmeinput ved at trykke på mikrofonknappen." "Tal nu" "Arbejder" @@ -85,18 +89,12 @@ "Annuller" "OK" "Stemmeinput" - - - - - - - - - - - - + "På hovedtastatur" + "På symboltastatur" + "Fra" + "Mik. på hovedtastatur" + "Mik. på symboltastatur" + "Stemmeinput deaktiveret" "Vælg inputmetode" "Inputsprog" "Træk fingeren på mellemrumstasten for at skifte sprog" @@ -104,75 +102,41 @@ "Ordbog er tilgængelig" "Aktiver brugerfeedback" "Vær med til at forbedre denne inputmetode ved at sende anvendelsesstatistikker og rapporter om nedbrud til Google." - "Tryk for at rette ord igen" - "Du kan rette ordene igen ved at trykke på de ord, du har indtastet" + "Tryk for at rette ord" + "Tryk på de indtastede ord for at rette dem. Kun når der er synlige forslag." "Tastaturtema" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tjekkisk tastatur" + "Dansk tastatur" + "Tysk tastatur" + "Engelsk tastatur (Storbritannien)" + "Engelsk tastatur (USA)" + "Spansk tastatur" + "Spansk tastatur (USA)" + "Fransk tastatur" + "Fransk tastatur (Canada)" + "Fransk tastatur (Schweiz)" + "Italiensk tastatur" + "Norsk tastatur" + "Hollandsk tastatur" + "Russisk tastatur" + "Serbisk tastatur" + "Svensk tastatur" + "Afrikaans stemme" + "Tjekkisk stemme" + "Tysk stemme" + "Engelsk stemme" + "Spansk stemme" + "Fransk stemme" + "Italiensk stemme" + "Japansk stemme" + "Koreansk stemme" + "Hollandsk stemme" + "Polsk stemme" + "Portugisisk stemme" + "Russisk stemme" + "Tyrkisk stemme" + "Kinesisk, Yue stemme" + "Kinesisk, mandarin stemme" + "isiZulu stemme" + "Tilstand for brugsstudie" diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 330946e5c..396d74dd8 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -26,32 +26,25 @@ "Vibrieren b. Tastendruck" "Ton bei Tastendruck" "Pop-up bei Tastendruck" - "Einstellungen für Wortvorschläge" + "Allgemein" + "Textkorrektur" "Autom. Groß-/Kleinschr." "Quick Fixes" "Korrigiert gängige Tippfehler" - - - - - - - - - - + "Änderungsvorschläge anzeigen" + "Vorgeschlagene Wörter während des Tippens anzeigen" + "Immer anzeigen" + "Im Hochformat anzeigen" + "Immer ausblenden" "Einstellungstaste anz." "Automatisch" "Immer anzeigen" "Immer ausblenden" - "Wortvorschläge" - "Vorheriges Wort automatisch korrigieren" - - - - - - + "Auto-Korrektur" + "Korrektur fehlerhafter Wörter durch Leertaste und Satzzeichen" + "Aus" + "Mäßig" + "Aggressiv" "Bigramm-Vorschläge" "Zur Verbesserung des Vorschlags vorheriges Wort verwenden" "%s: gespeichert" @@ -59,16 +52,27 @@ "Weiter" "Fertig" "Senden" - - + "ABC" "Mehr" "Pause" "Warten" + "Löschen" + "Eingabe" + "Einstellungen" + "Umschalt" + "Leerzeichen" + "Symbole" + "Tab" + "Spracheingabe" + "Symbole an" + "Symbole aus" + "Umschalt an" + "Umschalt aus" "Spracheingabe" "Spracheingaben werden derzeit nicht für Ihre Sprache unterstützt, funktionieren jedoch in Englisch." - "Die Spracheingabe ist eine Funktion im Versuchsstadium, die die vernetzte Spracherkennung von Google verwendet." - "Wenn Sie die Spracheingabe deaktivieren möchten, rufen Sie die Tastatureinstellungen auf." - "Um die Spracheingabe zu verwenden, drücken Sie die Mikrofontaste oder ziehen Sie Ihren Finger über die Bildschirmtastatur." + "Die Spracheingabe verwendet die Spracherkennung von Google. Es gelten die ""Google Mobile-Datenschutzbestimmungen""." + "Rufen Sie zum Deaktivieren der Spracheingabe die Einstellungen für die Eingabemethode auf." + "Drücken Sie zur Verwendung der Spracheingabe die Mikrofonschaltfläche." "Jetzt sprechen" "Vorgang läuft" @@ -85,18 +89,12 @@ "Abbrechen" "OK" "Spracheingabe" - - - - - - - - - - - - + "Auf Haupttastatur" + "Auf Symboltastatur" + "Aus" + "Mikro auf Haupttastatur" + "Mikro auf Symboltastatur" + "Spracheing. deaktiviert" "Eingabemethode auswählen" "Eingabesprachen" "Finger über die Leertaste bewegen, um die Eingabesprache zu wechseln" @@ -105,74 +103,40 @@ "Nutzer-Feedback aktivieren" "Tragen Sie zur Verbesserung dieses Eingabemethodeneditors bei, indem Sie automatisch Nutzungsstatistiken und Absturzberichte an Google senden." "Wortkorrektur" - "Sie können Wörter korrigieren, indem Sie die eingegebenen Wörter berühren." + "Tippen Sie zum Korrigieren auf eingegebene Wörter (nur, wenn Vorschläge angezeigt werden)." "Tastaturdesign" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tschechische Tastatur" + "Dänische Tastatur" + "Deutsche Tastatur" + "Englische Tastatur (GB)" + "Englische Tastatur (USA)" + "Spanische Tastatur" + "Spanische Tastatur (USA)" + "Französische Tastatur" + "Französische Tastatur (Kanada)" + "Französische Tastatur (Schweiz)" + "Italienische Tastatur" + "Norwegische Tastatur" + "Niederländische Tastatur" + "Russische Tastatur" + "Serbische Tastatur" + "Schwedische Tastatur" + "Stimme in Afrikaans" + "Tschechische Sprache" + "Deutsche Sprache" + "Englische Stimme" + "Spanische Sprache" + "Französische Sprache" + "Italienisch" + "Japanische Sprache" + "Koreanische Sprache" + "Niederländisch" + "Polnische Sprache" + "Portugiesische Sprache" + "Russische Sprache" + "Türkische Sprache" + "Chinesische Stimme (Yue)" + "Chinesische Stimme (Mandarin)" + "Stimme in isiZulu" + "Modus der Studie zur Benutzerfreundlichkeit" diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index ff6a73ad4..6d0869bf1 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -26,32 +26,25 @@ "Δόνηση κατά το πάτημα πλήκτρων" "Ήχος κατά το πάτημα πλήκτρων" "Εμφάνιση με το πάτημα πλήκτρου" - "Ρυθμίσεις υποδείξεων λέξεων" + "Γενικά" + "Διόρθωση κειμένου" "Αυτόματη χρήση κεφαλαίων" "Γρήγορες διορθώσεις" "Διορθώνει συνηθισμένα λάθη πληκτρολόγησης" - - - - - - - - - - + "Εμφάνιση προτάσεων διόρθωσης" + "Προβολή προτεινόμενων λέξεων κατά την πληκτρολόγηση" + "Να εμφανίζεται πάντα" + "Εμφάνιση σε λειτουργία κατακόρυφης προβολής" + "Πάντα απόκρυψη" "Εμφάνιση πλήκτρου ρυθμίσεων" "Αυτόματο" "Να εμφανίζεται πάντα" "Πάντα απόκρυψη" - "Υποδείξεις λέξεων" - "Αυτόματη διόρθωση της προηγούμενης λέξης" - - - - - - + "Αυτόματη διόρθωση" + "Τα πλήκτρα διαστήματος και στίξης διορθ. αυτόμ. λάθος λέξεις" + "Απενεργοποίηση" + "Μέτρια" + "Υψηλή" "Προτάσεις bigram" "Χρήση προηγούμενης λέξης για τη βελτίωση πρότασης" "%s : Αποθηκεύτηκε" @@ -59,16 +52,27 @@ "Επόμενο" "Τέλος" "Αποστολή" - - + "ΑΒΓ" "Περισσότερα" "Παύση" "Αναμ." + "Delete" + "Return" + "Ρυθμίσεις" + "Shift" + "Κενό" + "Σύμβολα" + "Tab" + "Φωνητική εντολή" + "Σύμβολα ενεργά" + "Σύμβολα ανενεργά" + "Shift ενεργό" + "Shift ανενεργό" "Φωνητική είσοδος" "Η φωνητική είσοδος δεν υποστηρίζεται αυτή τη στιγμή για τη γλώσσα σας, ωστόσο λειτουργεί στα Αγγλικά." - "Οι φωνητικές εντολές είναι μια πειραματική λειτουργία, η οποία χρησιμοποιεί τη δικτυακή αναγνώριση ομιλίας της Google." - "Για να απενεργοποιήσετε τη φωνητική είσοδο, μεταβείτε στις ρυθμίσεις πληκτρολογίου." - "Για να χρησιμοποιήσετε τις φωνητικές εντολές, πιέστε το κουμπί μικροφώνου ή σύρετε το δάχτυλό σας κατά μήκος του πληκτρολογίου της οθόνης." + "Οι φωνητικές εντολές χρησιμοποιούν την τεχνολογία αναγνώρισης φωνής της Google. Ισχύει ""η Πολιτική Απορρήτου για κινητά""." + "Για να απενεργοποιήσετε τις φωνητικές εντολές, μεταβείτε στις ρυθμίσεις της μεθόδου εισαγωγής." + "Για τη χρήση φωνητικών εντολών, πατήστε το κουμπί του μικροφώνου." "Μιλήστε τώρα" "Σε λειτουργία" @@ -85,18 +89,12 @@ "Ακύρωση" "ΟΚ" "Φωνητική είσοδος" - - - - - - - - - - - - + "Στο κύριο πληκτρολ." + "Πληκτρ. συμβ. ενερ." + "Απενεργοποίηση" + "Μικ. στο κύριο πληκ." + "Μικ. στο πληκ. συμβ." + "Απεν. φωνητ. είσοδος" "Επιλογή μεθόδου εισόδου" "Γλώσσες εισόδου" "Σύρετε το δάχτυλο στο πλήκτρο διαστήματος για να αλλάξετε γλώσσα" @@ -104,75 +102,41 @@ "Λεξικό διαθέσιμο" "Ενεργοποίηση σχολίων χρηστών" "Βοηθήστε μας να βελτιώσουμε αυτό το πρόγραμμα επεξεργασίας μεθόδου εισόδου στέλνοντας αυτόματα στατιστικά στοιχεία και αναφορές σφαλμάτων στην Google." - "Αγγίξτε για να διορθώσετε ξανά τις λέξεις" - "Μπορείτε να διορθώσετε ξανά τις λέξεις αγγίζοντας τις λέξεις που έχετε πληκτρολογήσει" + "Αγγίξτε για διόρθωση λέξεων" + "Αγγίξτε τις λέξες για να τις διορθώσετε, μόνο όταν οι προτάσεις είναι ορατές" "Θέμα πληκτρολογίου" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Τσεχικό πληκτρολόγιο" + "Δανικό πληκτρολόγιο" + "Γερμανικό πληκτρολόγιο" + "Αγγλικό (ΗΒ) πληκτρολόγιο" + "Αγγλικό (ΗΠΑ) πληκτρολόγιο" + "Ισπανικό πληκτρολόγιο" + "Ισπανικό (ΗΠΑ) πληκτρολόγιο" + "Γαλλικό πληκτρολόγιο" + "Γαλλικό (Καναδάς) πληκτρολόγιο" + "Γαλλικό (Ελβετία) πληκτρολόγιο" + "Ιταλικό πληκτρολόγιο" + "Νορβηγικό πληκτρολόγιο" + "Ολλανδικό πληκτρολόγιο" + "Ρωσικό πληκτρολόγιο" + "Σερβικό πληκτρολόγιο" + "Σουηδικό πληκτρολόγιο" + "Αφρικάανς" + "Τσεχικά" + "Γερμανικά" + "Αγγλικά" + "Ισπανικά" + "Γαλλικά" + "Φωνητικές εντολές στα Ιταλικά" + "Ιαπωνικά" + "Κορεατικά" + "Φωνητικές εντολές στα Ολλανδικά" + "Πολωνικά" + "Πορτογαλικά" + "Ρωσικά" + "Τουρκικά" + "Κινεζικά, Γιούε" + "Κινεζικά, Μανδαρινικά" + "Ζουλού" + "Λειτουργία μελέτης χρηστικότητας" diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 8f1815f5b..b2e62b0cd 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -26,32 +26,25 @@ "Vibrate on key-press" "Sound on key-press" "Pop-up on key press" - "Word suggestion settings" + "General" + "Text correction" "Auto-capitalisation" "Quick fixes" "Corrects commonly typed mistakes" - - - - - - - - - - + "Show correction suggestions" + "Display suggested words while typing" + "Always show" + "Show on portrait mode" + "Always hide" "Show settings key" "Automatic" "Always show" "Always hide" - "Word suggestions" - "Automatically correct the previous word" - - - - - - + "Auto-correction" + "Spacebar and punctuation correct mistyped words automatically" + "Off" + "Modest" + "Aggressive" "Bigram Suggestions" "Use previous word to improve suggestion" "%s : Saved" @@ -59,16 +52,27 @@ "Next" "Done" "Send" - - + "ABC" "More" "Pause" "Wait" + "Delete" + "Return" + "Settings" + "Shift" + "Space" + "Symbols" + "Tab" + "Voice Input" + "Symbols on" + "Symbols off" + "Shift on" + "Shift off" "Voice input" "Voice input is not currently supported for your language, but does work in English." - "Voice input is an experimental feature using Google\'s networked speech recognition." - "To turn off voice input, go to keyboard settings." - "To use voice input, press the microphone button or slide your finger across the on-screen keyboard." + "Voice input uses Google\'s speech recognition. ""The Mobile Privacy Policy"" applies." + "To turn off voice input, go to input method settings." + "To use voice input, press the microphone button." "Speak now" "Working" @@ -85,18 +89,12 @@ "Cancel" "OK" "Voice input" - - - - - - - - - - - - + "On main keyboard" + "On symbols keyboard" + "Off" + "Mic on main keyboard" + "Mic on symbols keyboard" + "Voice input is disabled" "Select input method" "Input languages" "Slide finger on spacebar to change language" @@ -105,44 +103,40 @@ "Enable user feedback" "Help improve this input method editor by sending usage statistics and crash reports automatically to Google." "Touch to correct words" - "Touch words entered to correct them" + "Touch words entered to correct them, only when suggestions are visible" "Keyboard Theme" "Czech Keyboard" "Danish Keyboard" - "German Keyboard" - "English (Great Britain) Keyboard" - "English (United States) Keyboard" + "German Keyboard" + "English (UK) Keyboard" + "English (US) Keyboard" "Spanish Keyboard" - - + "Spanish (US) Keyboard" "French Keyboard" - - - - - "Italian Keyboard" + "French (Canada) Keyboard" + "French (Switzerland) Keyboard" + "Italian Keyboard" "Norwegian Keyboard" "Dutch Keyboard" "Russian Keyboard" "Serbian Keyboard" "Swedish Keyboard" - "Czech Voice" - "German Voice" - "English (Australia) Voice" - "English (Great Britain) Voice" - "English (India) Voice" - "English (New Zealand) Voice" - "English (United States) Voice" + "Afrikaans Voice" + "Czech Voice" + "German Voice" + "English Voice" "Spanish Voice" "French Voice" + "Italian Voice" "Japanese Voice" "Korean Voice" + "Dutch Voice" "Polish Voice" "Portuguese Voice" "Russian Voice" "Turkish Voice" - "Chinese (China) Voice" - "Chinese (Taiwan) Voice" - - + "Chinese, Yue Voice" + "Chinese, Mandarin Voice" + "isiZulu Voice" + "Usability Study Mode" diff --git a/java/res/values-en/donottranslate-altchars.xml b/java/res/values-en/donottranslate-altchars.xml index 3950d7dff..29582c950 100644 --- a/java/res/values-en/donottranslate-altchars.xml +++ b/java/res/values-en/donottranslate-altchars.xml @@ -22,6 +22,7 @@ 3,è,é,ê,ë,ē 8,î,ï,í,ī,ì 9,ô,ö,ò,ó,œ,ø,ō,õ + ß 7,û,ü,ù,ú,ū ñ ç diff --git a/java/res/values-es-rUS-xlarge/strings.xml b/java/res/values-es-rUS-xlarge/strings.xml deleted file mode 100644 index 24d2b4f90..000000000 --- a/java/res/values-es-rUS-xlarge/strings.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - "Sonar al pulsar teclas" - - "Uso de mayúsculas automático" - - "Corrección automática" - - "La barra espaciadora o la puntuación insertan automáticamente la palabra resaltada." - - "Sugerencias de bigramas" - - "Listo" - - "Entrada de voz" - - "La entrada de voz utiliza el reconocimiento de voz de Google. ""Aplica la Política de privacidad de Google para celulares""." - - "Para desactivar la entrada por voz, ve a la configuración de métodos de entrada." - - "Para utilizar entrada de voz, presiona el botón micrófono." - - "Entrada de voz" - - "Toca las palabras ingresadas que desees corregir, solo cuando las sugerencias estén visibles." - - "Mostrar sugerencias" - - "Mostrar palabras sugeridas al escribir" - - "Mostrar siempre" - - "Mostrar en modo retrato" - - "Ocultar siempre" - - "Apagado" - - "Moderado" - - "Intenso" - - "ABC" - - "En el teclado principal" - - "En el teclado de símbolos" - - "Apagado" - - "Micrófono en el teclado principal" - - "Micrófono en el teclado de símbolos" - - "La entrada por voz está inhabilitada." - - "Teclado en checo" - - "Teclado en danés" - - "Teclado en alemán" - - "Teclado en inglés (Reino Unido)" - - "Teclado en inglés (EE.UU.)" - - "Teclado en español" - - "Teclado en español (EE.UU.)" - - "Teclado en francés" - - "Teclado en francés (Canadá)" - - "Teclado en francés (Suiza)" - - "Teclado en italiano" - - "Teclado en noruego" - - "Teclado en holandés" - - "Teclado en ruso" - - "Teclado en serbio" - - "Teclado en sueco" - - "Voz en Afrikáans" - - "Voz en checo" - - "Voz en alemán" - - "Voz en inglés" - - "Voz en español" - - "Voz en francés" - - "Voz en japonés" - - "Voz en coreano" - - "Voz en polaco" - - "Voz en portugués" - - "Voz en ruso" - - "Voz en turco" - - "Voz en chino, yue" - - "Voz en chino, mandarín" - - "Voz en isiZulu" - - "Modo estudio de usabilidad" - diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 44007e2ea..02f36ff79 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -25,33 +25,26 @@ "Opciones de entrada" "Vibrar al pulsar teclas" "Sonar al pulsar las teclas" - "Aviso emergente sobre keypress" - "Configuración de sugerencia de palabra" + "Aviso emergente al pulsar tecla" + "General" + "Corrección de texto" "Mayúsculas automáticas" "Arreglos rápidos" "Corrige errores de escritura comunes" - - - - - - - - - - + "Mostrar sugerencias de correcciones" + "Mostrar palabras sugeridas al escribir" + "Mostrar siempre" + "Mostrar en modo retrato" + "Ocultar siempre" "Mostrar tecla de configuración" "Automático" "Mostrar siempre" "Ocultar siempre" - "Sugerencias de palabras" - "Corregir automáticamente la palabra anterior" - - - - - - + "Corrección automática" + "La barra espaciadora y puntuación insertan automáticamente las palabras corregidas" + "Desactivado" + "Moderado" + "Total" "Sugerencias de Vigoran" "Utiliza la palabra anterior para mejorar la sugerencia" "%s: guardada" @@ -59,16 +52,27 @@ "Siguiente" "Hecho" "Enviar" - - + "ABC" "Más" "Pausa" "Espera" + "Eliminar" + "Volver" + "Configuración" + "Mayús" + "Espacio" + "Símbolos" + "Tab" + "Entrada de voz" + "Símbolos activados" + "Símbolos desactivados" + "Mayús activado" + "Mayús desactivado" "Entrada por voz" "La entrada por voz no está admitida en tu idioma, pero sí funciona en inglés." - "La entrada por voz es una característica experimental que utiliza la red de reconocimiento de voz de Google." - "Para desactivar la entrada por voz, ve a configuración del teclado." - "Para realizar entrada por voz, presiona el botón del micrófono o desliza tus dedos por el teclado en pantalla." + "La entrada de voz usa el reconocimiento de voz de Google. ""Se aplica la política de privacidad para"" celulares." + "Para desactivar la entrada de voz, ve a la configuración de métodos de entrada." + "Para utilizar entrada de voz, presiona el botón micrófono." "Habla ahora" "Procesando" @@ -85,18 +89,12 @@ "Cancelar" "Aceptar" "Entrada por voz" - - - - - - - - - - - - + "En el teclado principal" + "En el teclado de símbolos" + "Desactivado" + "Micrófono en el teclado principal" + "Micrófono en el teclado de símbolos" + "La entrada por voz está inhabilitada" "Seleccionar método de entrada" "Idiomas de entrada" "Deslizarse manualmente por la barra espaciadora para cambiar el idioma" @@ -105,74 +103,40 @@ "Habilitar los comentarios del usuario" "Ayuda a mejorar este editor de método de introducción de texto al enviar las estadísticas de uso y los informes de error a Google." "Tocar para corregir palabras" - "Toca las palabras ingresadas que desees corregir" + "Toca las palabras ingresadas que desees corregir solo cuando las sugerencias estén visibles." "Tema del teclado" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Teclado en checo" + "Teclado en danés" + "Teclado en alemán" + "Teclado en inglés (Reino Unido)" + "Teclado en inglés (EE.UU.)" + "Teclado en español" + "Teclado en español (EE.UU.)" + "Teclado en francés" + "Teclado en francés (Canadá)" + "Teclado en francés (Suiza)" + "Teclado en italiano" + "Teclado en noruego" + "Teclado en holandés" + "Teclado en ruso" + "Teclado en serbio" + "Teclado en sueco" + "Voz en Afrikaans" + "Voz en checo" + "Voz en alemán" + "Voz en inglés" + "Voz en español" + "Voz en francés" + "Voz italiana" + "Voz en japonés" + "Voz en coreano" + "Voz holandesa" + "Voz en polaco" + "Voz en portugués" + "Voz en ruso" + "Voz en turco" + "Voz en Yue, chino" + "Voz en mandarín, chino" + "Voz en isiZulu" + "Modo estudio de usabilidad" diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 511141c04..f9a86ffd6 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -25,33 +25,26 @@ "Opciones introducción texto" "Vibrar al pulsar tecla" "Sonido al pulsar tecla" - "Popup al pulsar" - "Ajustes de sugerencia de palabras" - "Uso de mayúsculas auto." + "Popup al pulsar tecla" + "General" + "Corrección ortográfica" + "Mayúsculas automáticas" "Correcciones rápidas" "Corrige los errores tipográficos que se cometen con más frecuencia." - - - - - - - - - - + "Mostrar sugerencias de correcciones" + "Muestra las palabras sugeridas mientras se escribe." + "Mostrar siempre" + "Mostrar en modo vertical" + "Ocultar siempre" "Mostrar tecla de ajustes" "Automáticamente" "Mostrar siempre" "Ocultar siempre" - "Sugerencias de palabras" - "Corregir automáticamente la palabra anterior" - - - - - - + "Autocorrección" + "Espacio y punt para corregir errores" + "Desactivada" + "Parcial" + "Total" "Sugerencias de bigramas" "Usar palabra anterior para mejorar sugerencias" "%s: guardada" @@ -59,16 +52,27 @@ "Sig." "Listo" "Enviar" - - + "ABC" "Más" "Pausa" "Espera" + "Eliminar" + "Retroceso" + "Ajustes" + "Mayús" + "Espacio" + "Símbolos" + "Tabulador" + "Entrada de voz" + "Símbolos activados" + "Símbolos desactivados" + "Mayús activadas" + "Mayús desactivadas" "Introducción de voz" "Actualmente la introducción de voz no está disponible en tu idioma, pero se puede utilizar en inglés." - "La introducción de voz es una función en fase experimental que utiliza la tecnología de reconocimiento de voz en red de Google." - "Para desactivar la función de introducción de voz, accede a la configuración del teclado." - "Para utilizar la función de introducción de voz, pulsa el botón de micrófono o desliza el dedo por el teclado en pantalla." + "La entrada de voz utiliza el reconocimiento de voz de Google. Se aplica la ""Política de privacidad de Google para móviles""." + "Para desactivar la función de entrada de voz, accede a los ajustes del método de introducción de texto." + "Para utilizar la entrada de voz, pulsa el botón de micrófono." "Habla ahora" "En curso" @@ -85,18 +89,12 @@ "Cancelar" "Aceptar" "Introducción de voz" - - - - - - - - - - - - + "En teclado principal" + "En teclado símbolos" + "Desactivada" + "Micro en tecl princ" + "Micro en tecl símb" + "Entrada de voz inhab" "Seleccionar método de introducción de texto" "Idiomas" "Deslizar el dedo por la barra espaciadora para cambiar el idioma" @@ -105,74 +103,40 @@ "Habilitar comentarios de usuarios" "Ayuda a mejorar este editor de método de introducción de texto enviando estadísticas de uso e informes de error a Google." "Tocar para corregir palabras" - "Tocar palabras introducidas para corregirlas" + "Toca las palabras introducidas para corregirlas, solo cuando las sugerencias sean visibles." "Tema de teclado" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Teclado checo" + "Teclado danés" + "Teclado alemán" + "Teclado inglés (Reino Unido)" + "Teclado de inglés (EE.UU.)" + "Teclado español" + "Teclado en español (EE.UU.)" + "Teclado francés" + "Teclado francés (Canadá)" + "Teclado francés (Suiza)" + "Teclado italiano" + "Teclado noruego" + "Teclado holandés" + "Teclado ruso" + "Teclado serbio" + "Teclado sueco" + "Voz afrikáans" + "Google Voice en checo" + "Google Voice en alemán" + "Voz inglesa" + "Google Voice en español" + "Google Voice en francés" + "Voz italiana" + "Google Voice en japonés" + "Google Voice en coreano" + "Voz holandesa" + "Google Voice en polaco" + "Google Voice en portugués" + "Google Voice en ruso" + "Google Voice en turco" + "Voz china (cantonés)" + "Voz china (mandarín)" + "Voz zulú" + "Modo de estudio de uso" diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index db370757c..b594411ac 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -26,32 +26,25 @@ "لرزش با فشار کلید" "صدا با فشار کلید" "بازشو با فشار کلید" - "تنظیمات پیشنهاد کلمه" + "کلی" + "تصحیح متن" "نوشتن با حروف بزرگ خودکار" "راه حل های سریع" "تصحیح خطاهای تایپی رایج" - - - - - - - - - - + "نمایش پیشنهادات تصحیح" + "نمایش واژه های پیشنهادی در حین تایپ" + "همیشه نمایش داده شود" + "نمایش در حالت عمودی" + "همیشه پنهان شود" "نمایش کلید تنظیمات" "خودکار" "همیشه نمایش" "همیشه پنهان" - "پیشنهادات کلمه" - "تصحیح خودکار کلمه قبلی" - - - - - - + "تصحیح خودکار" + "کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده اند تصحیح می کنند" + "خاموش" + "متوسط" + "فعال" "توضیحات بیگرام" "برای بهبود پیشنهاد از کلمه قبلی استفاده شود" "%s : ذخیره شد" @@ -59,16 +52,27 @@ "بعدی" "انجام شد" "ارسال" - - + "ABC" "بیشتر" "توقف موقت" "منتظر بمانید" + "Delete" + "Return" + "تنظیمات" + "Shift" + "فاصله" + "نمادها" + "Tab" + "ورودی صوتی" + "نمادها روشن" + "نمادها خاموش" + "Shift روشن" + "Shift خاموش" "ورودی صوتی" "ورودی صوتی در حال حاضر برای زبان شما پشتیبانی نمی شود اما برای زبان انگلیسی فعال است." - "ورودی صوتی یک ویژگی آزمایشی با استفاده از تشخیص گفتار شبکه Google است." - "برای خاموش کردن ورودی صدا، به تنظیمات صفحه کلید بروید." - "برای استفاده از ورودی صوتی، دکمه میکروفن را فشار دهید یا انگشت خود را روی صفحه کلید روی صفحه حرکت دهید." + "ورودی صوتی از تشخیص صدای Google استفاده می کند. ""خط مشی رازداری Mobile "" اعمال می شود." + "برای خاموش کردن ورودی صدا، به تنظیمات روش ورودی بروید." + "برای استفاده از ورودی صوتی، دکمه میکروفن را فشار دهید." "اکنون صحبت کنید" "در حال کار" @@ -85,18 +89,12 @@ "لغو" "تأیید" "ورودی صوتی" - - - - - - - - - - - - + "در صفحه کلید اصلی" + "در صفحه کلید نمادها" + "خاموش" + "میکروفن در صفحه کلید اصلی" + "میکروفن در صفحه کلید نمادها" + "ورودی صدا غیرفعال است" "انتخاب روش ورودی" "زبان های ورودی" "برای تغییر زبان انگشت را روی کلید فاصله بلغزانید" @@ -105,44 +103,40 @@ "فعال کردن بازخورد کاربر" "با ارسال خودکار آمارهای کاربرد و گزارش های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید." "برای تصحیح کلمات لمس کنید" - "برای تصحیح کلمات وارد شده آنها را لمس کنید" + "فقط هنگامی که پیشنهادات قابل مشاهده هستند، برای تصحیح کلمات وارد شده آنها را لمس کنید" "طرح زمینه صفحه کلید" "صفحه کلید چک" "صفحه کلید دانمارکی" - "صفحه کلید آلمانی" - "صفحه کلید انگلیسی (بریتانیای کبیر)" - "صفحه کلید انگلیسی (ایالات متحده)" + "صفحه کلید آلمانی" + "صفحه کلید انگلیسی (بریتانیایی)" + "صفحه کلید انگلیسی (آمریکایی)" "صفحه کلید اسپانیایی" - - + "صفحه کلید اسپانیایی (آمریکایی)" "صفحه کلید فرانسوی" - - - - - "صفحه کلید ایتالیایی" + "صفحه کلید فرانسوی (کانادایی)" + "صفحه کلید فرانسوی (سوئیس)" + "صفحه کلید ایتالیایی" "صفحه کلید نروژی" "صفحه کلید هلندی" "صفحه کلید روسی" "صفحه کلید صربی" "صفحه کلید سوئدی" - "صدای چک" - "صدای آلمانی" - "صدای انگلیسی (استرالیا)" - "صدای انگلیسی (بریتانیای کبیر)" - "صدای انگلیسی (هندوستان)" - "صدای انگلیسی (نیوزیلند)" - "صدای انگلیسی (ایالات متحده)" + "صدای آفریکانس" + "صدای چک" + "صدای آلمانی" + "صدای انگلیسی" "صدای اسپانیایی" "صدای فرانسوی" + "صدای ایتالیایی" "صدای ژاپنی" "صدای کره ای" + "صدای هلندی" "صدای لهستانی" "صدای پرتغالی" "صدای روسی" "صدای ترکی" - "صدای چینی (چین)" - "صدای چینی (تایوان)" - - + "صدای چینی، یوئه" + "صدای چینی، ماندارین" + "صدای ایزی زولو" + "حالت تست قابلیت استفاده" diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 71c0a9b86..0bf385552 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -26,32 +26,25 @@ "Käytä värinää näppäimiä painettaessa" "Toista ääni näppäimiä painettaessa" "Ponnahdusikkuna painalluksella" - "Sanaehdotusasetukset" + "Yleinen" + "Tekstin korjaus" "Automaattiset isot kirjaimet" "Pikakorjaukset" "Korjaa yleiset kirjoitusvirheet" - - - - - - - - - - + "Näytä korjausehdotukset" + "Näytä sanaehdotukset kirjoitettaessa" + "Näytä aina" + "Näytä pystysuunnassa" + "Piilota aina" "Näytä asetukset-näppäin" "Automaattinen" "Näytä aina" "Piilota aina" - "Sanaehdotukset" - "Korjaa edellinen sana automaattisesti" - - - - - - + "Autom. korjaus" + "Välilyönnit ja välimerkit korjaavat väärinkirjoitetut sanat automaattisesti" + "Älä käytä" + "Osittainen" + "Täysi" "Bigram-ehdotukset" "Paranna ehdotusta aiemman sanan avulla" "%s : Tallennettu" @@ -59,16 +52,27 @@ "Seuraava" "Valmis" "Lähetä" - - + "ABC" "Lisää" "Tauko" "Odota" + "Poista" + "Rivinvaihto" + "Asetukset" + "Shift" + "Välilyönti" + "Symbolit" + "Sarkain" + "Äänisyöte" + "Symbolit käytössä" + "Symbolit pois käytöstä" + "Shift käytössä" + "Shift pois käytöstä" "Äänisyöte" "Äänisyötettä ei vielä tueta kielelläsi, mutta voit käyttää sitä englanniksi." - "Äänisyöte on kokeellinen Googlen puheentunnistusta käyttävä ominaisuus." - "Siirry näppäimistön asetuksiin poistaaksesi äänisyötteen käytöstä." - "Käytä äänisyötettä painamalla mikrofonipainiketta tai liu\'uttamalla sormeasi näytön näppäimistön poikki." + "Äänisyöte käyttää Googlen puheentunnistusta. ""Mobile-tietosuojakäytäntö"" on voimassa." + "Siirry syöttöasetuksiin poistaaksesi äänisyötteen käytöstä." + "Ota äänisyöte käyttöön painamalla mikrofonikuvaketta." "Puhu nyt" "Työstetään" @@ -85,18 +89,12 @@ "Peruuta" "OK" "Äänisyöte" - - - - - - - - - - - - + "Päänäppäimistössä" + "Symbolinäppäimistössä" + "Älä näytä" + "Mikr. päänäppäim." + "Mikr. symbolinäppäim." + "Ääniohjaus on pois käytöstä" "Valitse syöttötapa" "Syöttökielet" "Vaihda kieltä liu\'uttamalla sormea välilyöntinäppäimellä" @@ -105,44 +103,40 @@ "Ota käyttäjäpalaute käyttöön" "Auta parantamaan tätä syöttötavan muokkausohjelmaa lähettämällä automaattisesti käyttötietoja ja kaatumisraportteja Googlelle." "Korjaa sanoja koskettamalla" - "Korjaa sanoja koskettamalla niitä" + "Korjaa annetut sanat napauttamalla. (Vain, kun ehdotuksia on näkyvillä.)" "Näppäimistön teema" "Näppäimistö: tšekki" "Näppäimistö: tanska" - "Ääni: saksa" - "Ääni: englanti (Iso-Britannia)" - "Näppäimistö: englanti (Yhdysvallat)" + "Näppäimistö: saksa" + "Näppäimistö: englanti (UK)" + "Näppäimistö: englanti (US)" "Näppäimistö: espanja" - - + "Näppäimistö: espanja (US)" "Näppäimistö: ranska" - - - - - "Näppäimistö: italia" + "Näppäimistö: ranska (Kanada)" + "Näppäimistö: ranska (Sveitsi)" + "Näppäimistö: italia" "Näppäimistö: norja" "Näppäimistö: hollanti" "Näppäimistö: venäjä" "Näppäimistö: serbia" "Näppäimistö: ruotsi" - "Ääni: tšekki" - "Ääni: saksa" - "Ääni: englanti (Australia)" - "Ääni: englanti (Iso-Britannia)" - "Ääni: englanti (Intia)" - "Ääni: englanti (Uusi-Seelanti)" - "Ääni: englanti (Yhdysvallat)" + "Ääni: afrikaans" + "Ääni: tšekki" + "Ääni: saksa" + "Ääni: englanti" "Ääni: espanja" "Ääni: ranska" + "italia (ääni)" "Ääni: japani" "Ääni: korea" + "hollanti (ääni)" "Ääni: puola" "Ääni: portugali" "Ääni: venäjä" "Ääni: turkki" - "Ääni: kiina (Kiina)" - "Ääni: kiina (Taiwan)" - - + "Ääni: kiina, yue" + "Ääni: mandariinikiina" + "Ääni: isiZulu" + "Käytettävyystutkimustila" diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml index b79df7b37..6c3536210 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/donottranslate.xml @@ -19,7 +19,7 @@ --> - .\u0009\u0020,;:!?\'\n()[]*&@{}/<>_+=|\u0022 + .\u0009\u0020,;:!?\n()[]*&@{}/<>_+=|\u0022 ., diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 7148ef532..b7aac9445 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -25,33 +25,26 @@ "Options de saisie" "Vibrer à chaque touche" "Son à chaque touche" - "Agrandir les caractères à chaque touche" - "Paramètres de la saisie prédictive" + "Agrandir les caractères" + "Général" + "Correction du texte" "Majuscules auto" "Corrections rapides" "Corrige les fautes de frappe courantes" - - - - - - - - - - - "Afficher la touche des paramètres" + "Afficher les suggestions de correction" + "Afficher les suggestions de terme lors de la saisie" + "Toujours afficher" + "Afficher en mode Portrait" + "Toujours masquer" + "Afficher touche param." "Automatique" "Toujours afficher" "Toujours masquer" - "Saisie prédictive" - "Corriger automatiquement le mot précédent" - - - - - - + "Correction auto" + "Corriger autom. orthographe (pression sur barre espace/signes ponctuation)" + "Désactiver" + "Simple" + "Proactive" "Suggestions de type bigramme" "Améliorer la suggestion en fonction du mot précédent" "%s : enregistré" @@ -59,16 +52,27 @@ "Suivant" "OK" "Envoyer" - - + "ABC" "Plus" "Pause" "Attente" + "Supprimer" + "Entrée" + "Paramètres" + "Maj" + "Espace" + "Symboles" + "Tabulation" + "Saisie vocale" + "Symboles activés" + "Symboles désactivés" + "Maj activée" + "Maj désactivée" "Saisie vocale" "La saisie vocale n\'est pas encore prise en charge pour votre langue, mais elle fonctionne en anglais." - "La saisie vocale est une fonctionnalité expérimentale qui fait appel à la reconnaissance vocale en réseau de Google." - "Pour désactiver la saisie vocale, accédez aux paramètres du clavier." - "Pour utiliser la saisie vocale, appuyez sur la touche du microphone ou faites glisser votre doigt sur le clavier à l\'écran." + "La saisie vocale fait appel à la reconnaissance vocale de Google. Les ""Règles de confidentialité Google Mobile"" s\'appliquent." + "Pour désactiver la saisie vocale, accédez aux paramètres du mode de saisie." + "Pour utiliser la saisie vocale, appuyez sur la touche du microphone." "Parlez maintenant" "Traitement en cours" @@ -85,18 +89,12 @@ "Annuler" "OK" "Saisie vocale" - - - - - - - - - - - - + "Sur clavier principal" + "Sur clavier symboles" + "Désactiver" + "Micro clavier principal" + "Micro sur clavier symboles" + "Saisie vocale désactivée" "Sélectionner un mode de saisie." "Langues de saisie" "Faites glisser votre doigt sur la barre d\'espacement pour changer la langue." @@ -104,75 +102,41 @@ "Dictionnaire disponible" "Autoriser les commentaires des utilisateurs" "Contribuer à l\'amélioration de cet éditeur du mode de saisie grâce à l\'envoi automatique de statistiques d\'utilisation et de rapports d\'incident à Google." - "Appuyer pour corriger les suggestions" - "Appuyer sur les mots saisis pour les corriger" + "Appuyer pour corriger" + "Appuyer sur les mots saisis pour les corriger, uniquement lorsque des suggestions sont visibles" "Thème du clavier" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Clavier tchèque" + "Clavier danois" + "Clavier allemand" + "Clavier anglais (Royaume-Uni)" + "Clavier anglais (États-Unis)" + "Clavier espagnol" + "Clavier espagnol (États-Unis)" + "Clavier français" + "Clavier français (Canada)" + "Clavier français (Suisse)" + "Clavier italien" + "Clavier norvégien" + "Clavier néerlandais" + "Clavier russe" + "Clavier serbe" + "Clavier suédois" + "Voix parlant afrikaans" + "Voix tchèque" + "Voix allemande" + "Voix parlant anglais" + "Voix espagnole" + "Voix française" + "Voix parlant italien" + "Voix japonaise" + "Voix coréenne" + "Voix parlant néerlandais" + "Voix polonaise" + "Voix portugaise" + "Voix russe" + "Voix turque" + "Voix parlant chinois (cantonais)" + "Voix parlant chinois (mandarin)" + "Voix parlant zoulou" + "Mode d\'étude de l\'utilisation" diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index dcee1b1d2..e6879a6d1 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -26,32 +26,25 @@ "Vibracija pri pritisku na tipku" "Zvuk pri pritisku tipke" "Povećanja na pritisak tipke" - "Postavke prijedloga riječi" + "Općenito" + "Ispravak teksta" "Automatsko pisanje velikih slova" "Brzi popravci" "Ispravlja uobičajene pogreške u pisanju" - - - - - - - - - - + "Pokaži prijedloge ispravka" + "Prikazivanje predloženih riječi prilikom upisivanja" + "Uvijek prikaži" + "Prikaži u portretnom načinu" + "Uvijek sakrij" "Prikaži tipku postavki" "Automatski" "Uvijek prikaži" "Uvijek sakrij" - "Prijedlozi riječi" - "Automatsko ispravljanje prethodne riječi" - - - - - - + "Samoispravak" + "Razm. i intrp. aut. ispr. kr. rči." + "Isključeno" + "Skromno" + "Agresivno" "Bigram prijedlozi" "Upotrijebi prethodnu riječ radi poboljšanja prijedloga" "%s : Spremljeno" @@ -59,16 +52,27 @@ "Dalje" "Gotovo" "Pošalji" - - + "ABC" "Više" "Pauza" "Pričekaj" + "Delete" + "Enter" + "Postavke" + "Shift" + "Razmaknica" + "Simboli" + "Tabulator" + "Glasovni unos" + "Simboli uključeni" + "Simboli isključeni" + "Shift uključen" + "Shift isključen" "Glasovni unos" "Vaš jezik trenutno nije podržan za glasovni unos, ali radi za engleski." - "Glasovni unos je pokusna značajka koja koristi Googleovo umreženo prepoznavanje govora." - "Za isključivanje glasovnog unosa idite na postavke tipkovnice." - "Da biste koristili glasovni unos pritisnite gumb mikrofona ili kliznite prstom preko tipkovnice na zaslonu." + "Glasovni unos upotrebljava Googleovo prepoznavanje govora. Primjenjuju se ""Pravila o privatnosti za uslugu Mobile""." + "Za isključivanje glasovnog unosa idite na postavke načina unosa." + "Za upotrebu glasovnog unosa pritisnite gumb mikrofona." "Govorite sad" "Obrada" @@ -85,18 +89,12 @@ "Odustani" "U redu" "Glasovni unos" - - - - - - - - - - - - + "Na glavnoj tipkovnici" + "Na tipkovnici simb." + "Isključeno" + "Mik. na gl. tipk." + "Mik. na tipk. simb." + "Glas. unos onemog." "Odabir ulazne metode" "Jezici unosa" "Kliznite prstom po razmaknici za promjenu jezika" @@ -105,44 +103,40 @@ "Omogući korisničke povratne informacije" "Pomozite u poboljšanju ovog urednika ulazne metode automatskim slanjem statistike upotrebe i padova Googleu." "Dodirnite za ispravak riječi" - "Dodirnite unesene riječi radi ispravka" + "Dodirnite unesene riječi da biste ih ispravili samo kada su prijedlozi vidljivi" "Tema tipkovnice" "Češka tipkovnica" "Danska tipkovnica" - "Njemačka tipkovnica" - "Engleska (Velika Britanija) tipkovnica" - "Engleska (SAD) tipkovnica" + "Njemačka tipkovnica" + "Engleska (UK) tipkovnica" + "Engleska (SAD) tipkovnica" "Španjolska tipkovnica" - - + "Tipkovnica za španjolski (SAD)" "Francuska tipkovnica" - - - - - "Talijanska tipkovnica" + "Francuska (Kanada) tipkovnica" + "Francuska (Švicarska) tipkovnica" + "Talijanska tipkovnica" "Norveška tipkovnica" "Nizozemska tipkovnica" "Ruska tipkovnica" "Srpska tipkovnica" "Švedska tipkovnica" - "Češki glas" - "Njemački glas" - "Engleski (Australija) glas" - "Engleski (Velika Britanija) glas" - "Engleski (Indija) glas" - "Engleski (Novi Zeland) glas" - "Engleski (SAD) glas" + "afrikaans glasovno" + "Češki glasovni" + "Njemački glasovni" + "engleski glasovno" "Španjolski glas" "Francuski glas" + "Talijanski glas" "Japanski glas" "Korejski glas" + "Nizozemski glas" "Poljski glas" "Portugalski glas" "Ruski glas" "Turski glas" - "Kineski (Kina) glas" - "Kineski (Tajvan) glas" - - + "kineski, Yue glasovno" + "kineski, mandarinski glasovno" + "isiZulu glasovno" + "Način studije upotrebljivosti" diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 8d64d243d..a8bf98367 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -26,32 +26,25 @@ "Rezgés billentyű megnyomása esetén" "Hangjelzés billentyű megnyomása esetén" "Legyen nagyobb billentyű lenyomásakor" - "Szójavaslati beállítások" + "Általános" + "Szövegjavítás" "Automatikusan nagy kezdőbetű" "Gyorsjavítások" "Kijavítja a gyakori gépelési hibákat" - - - - - - - - - - + "Javítási ajánlások megjelenítése" + "A javasolt szavak megjelenítése gépelés közben" + "Mindig látszik" + "Megjelenítés álló tájolásban" + "Mindig rejtve" "Beállítások billentyű megjelenítése" "Automatikus" "Mindig látszik" "Mindig rejtve" - "Szójavaslatok" - "Az előző szó automatikus kijavítása" - - - - - - + "Automatikus javítás" + "Szóköz és központozás automatikusan javítja az elgépelést" + "Ki" + "Mérsékelt" + "Agresszív" "Bigram javaslatok" "Előző szó használata a javaslatok javításához" "%s : mentve" @@ -59,16 +52,27 @@ "Tovább" "Kész" "Küldés" - - + "ABC" "Egyebek" "Szün." "Vár" + "Törlés" + "Vissza" + "Beállítások" + "Shift" + "Szóköz" + "Szimbólumok" + "Tab" + "Hangbevitel" + "Szimbólumok be" + "Szimbólumok ki" + "Shift be" + "Shift ki" "Hangbevitel" "A hangbevitel szolgáltatás jelenleg nem támogatja az Ön nyelvét, ám angolul működik." - "A hangbevitel a Google hálózati beszédfelismerését alkalmazó kísérleti funkció." - "A hangbevitelt a billentyűzet beállításai között lehet kikapcsolni." - "A hangbevitel használatához nyomja meg a mikrofon gombját vagy húzza végig az ujját a képernyő-billentyűzeten." + "A hangbevitel a Google beszédfelismerő technológiáját használja, amelyre a ""Mobil adatvédelmi irányelvek"" érvényesek." + "A hangbevitelt a beviteli mód beállításai között lehet kikapcsolni." + "A hangbevitel használatához nyomja meg a mikrofon gombot." "Most beszéljen" "Feldolgozás" @@ -85,18 +89,12 @@ "Mégse" "OK" "Hangbevitel" - - - - - - - - - - - - + "A fő billentyűzeten" + "Szimbólumoknál" + "Ki" + "Mikr. a billentyűzeten" + "Mikr. a szimbólumoknál" + "Hangbevivel KI" "Beviteli mód kiválasztása" "Beviteli nyelvek" "A nyelv módosításához húzza végig az ujját a szóköz billentyűn" @@ -105,44 +103,40 @@ "Felhasználói visszajelzés engedélyezése" "Segíthet ennek a beviteli módszernek a javításában, ha engedélyezi a használati statisztikák és a hibajelentések elküldését a Google-nak." "Javítás a szavak megérintésével" - "A beírt szavakat megérintve kijavíthatja őket" + "A beírt szavakat csak akkor javíthatja ki megérintve, ha látszanak javaslatok" "Billentyűzettéma" "Cseh billentyűzet" "Dán billentyűzet" - "Német billentyűzet" - "Angol (brit) billentyűzet" - "Angol (amerikai) billentyűzet" + "Német billentyűzet" + "Angol (UK) billentyűzet" + "Angol (US) billentyűzet" "Spanyol billentyűzet" - - + "Spanyol (US) billentyűzet" "Francia billentyűzet" - - - - - "Olasz billentyűzet" + "Francia (kanadai) billentyűzet" + "Francia (svájci) billentyűzet" + "Olasz billentyűzet" "Norvég billentyűzet" "Holland billentyűzet" "Orosz billentyűzet" "Szerb billentyűzet" "Svéd billentyűzet" - "Cseh hang" - "Német hang" - "Angol (ausztrál) hang" - "Angol (brit) hang" - "Angol (indiai) hang" - "Angol (új-zélandi) hang" - "Angol (amerikai) hang" + "Afrikaans hang" + "Cseh hang" + "Német hang" + "Angol hang" "Spanyol hang" "Francia hang" + "Olasz hang" "Japán hang" "Koreai hang" + "Holland hang" "Lengyel hang" "Portugál hang" "Orosz hang" "Török hang" - "Kínai (kínai) hang" - "Kínai (tajvani) hang" - - + "Kínai (jüe) hang" + "Kínai (mandarin) hang" + "isiZulu hang" + "Használhatóság - tanulás mód" diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 60d50b46b..4df7f1e7f 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -26,32 +26,25 @@ "Getar jika tombol ditekan" "Berbunyi jika tombol ditekan" "Muncul saat tombol ditekan" - "Setelan saran kata" + "Umum" + "Koreksi teks" "Kapitalisasi otomatis" "Perbaikan cepat" "Memperbaiki kesalahan ketik umum" - - - - - - - - - - + "Tampilkan saran koreksi" + "Tampilkan kata yang disarankan ketika mengetik" + "Selalu tampilkan" + "Tampilkan pada mode potret" + "Selalu sembunyikan" "Lihat tombol setelan" "Otomatis" "Selalu tampilkan" "Selalu sembunyikan" - "Saran kata" - "Perbaiki kata sebelumnya secara otomatis" - - - - - - + "Koreksi otomatis" + "Bilah spasi dan tanda baca secara otomatis dikoreksi pada kata yang salah ketik" + "Mati" + "Sederhana" + "Agresif" "Saran Bigram" "Gunakan kata sebelumnya untuk meningkatkan sara" "%s : Telah disimpan" @@ -59,16 +52,27 @@ "Berikutnya" "Selesai" "Kirimkan" - - + "ABC" "Lainnya" "Jeda" "Tunggu" + "Hapus" + "Enter" + "Setelan" + "Shift" + "Spasi" + "Simbol" + "Tab" + "Masukan Suara" + "Simbol hidup" + "Simbol mati" + "Shift hidup" + "Shift mati" "Masukan suara" "Masukan suara saat ini tidak didukung untuk bahasa Anda, tetapi bekerja dalam Bahasa Inggris." - "Masukan suara adalah fitur eksperimental yang menggunakan pengenal suara berjaringan Google." - "Untuk mematikan masukan suara, buka setelan keyboard." - "Untuk menggunakan masukan suara, tekan tombol mikrofon atau geser jari Anda di sepanjang keyboard pada layar." + "Masukan suara menggunakan pengenalan ucapan Google. ""Kebijakan Privasi Seluler"" berlaku." + "Untuk mematikan masukan suara, buka setelan metode masukan." + "Untuk menggunakan masukan suara, tekan tombol mikrofon." "Ucapkan sekarang" "Bekerja" @@ -85,18 +89,12 @@ "Batal" "OK" "Masukan suara" - - - - - - - - - - - - + "Pada keyboard utama" + "Pada keyboard simbol" + "Mati" + "Mik pada keyboard utama" + "Mik pada keyboard simbol" + "Masukan suara dinonaktifkan" "Pilih metode masukan" "Bahasa masukan" "Geser jari pada bilah spasi untuk mengubah bahasa" @@ -105,44 +103,40 @@ "Aktifkan umpan balik pengguna" "Bantu tingkatkan metode editor masukan dengan mengirim statistik penggunaan dan laporan kerusakan ke Google secara otomatis." "Sentuh untuk memperbaiki kata" - "Sentuk kata yang dimasukkan untuk memperbaikinya" + "Sentuh kata yang dimasukkan untuk memperbaikinya, hanya saat saran dapat dilihat" "Tema Keyboard" "Keyboard Cheska" "Keyboard Denmark" - "Keyboard Jerman" - "Keyboard Inggris (Inggris Raya)" - "Keyboard Inggris (Amerika Serikat)" + "Keyboard Jerman" + "Keyboard Inggris (Britania Raya)" + "Keyboard Inggris (AS)" "Keyboard Spanyol" - - + "Keyboard Spanyol (AS)" "Keyboard Prancis" - - - - - "Keyboard Italia" + "Keyboard Prancis (Kanada)" + "Keyboard Prancis (Swiss)" + "Keyboard Italia" "Keyboard Norwegia" "Keyboard Belanda" "Keyboard Rusia" "Keyboard Serbia" "Keyboard Swedia" - "Suara Cheska" - "Suara Jerman" - "Suara Bahasa Inggris (Australia)" - "Suara Bahasa Inggris (Inggris Raya)" - "Suara Bahasa Inggris (India)" - "Suara Bahasa Inggris (Selandia Baru)" - "Suara Bahasa Inggris (Amerika Serikat)" + "Suara Bahasa Afrika" + "Suara Bahasa Cheska" + "Suara Bahasa Jerman" + "Suara Bahasa Inggris" "Suara Bahasa Spanyol" - "Suara Prancis" - "Suara Jepang" + "Suara Bahasa Prancis" + "Suara Bahasa Italia" + "Suara Bahasa Jepang" "Suara Bahasa Korea" + "Suara Bahasa Belanda" "Suara Bahasa Polandia" "Suara Bahasa Portugis" "Suara Bahasa Rusia" "Suara Bahasa Turki" - "Suara Bahasa China (China)" - "Suara Bahasa China (Taiwan)" - - + "Suara Bahasa China, Yue" + "Suara Bahasa China, Mandarin" + "Suara Zulu" + "Mode Studi Daya Guna" diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 45bca4db5..9bf4f9e30 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -25,33 +25,26 @@ "Opzioni inserimento" "Vibrazione tasti" "Suono tasti" - "Popup alla pressione di un tasto" - "Impostazioni suggerimento parole" + "Popup sui tasti" + "Generali" + "Correzione testo" "Maiuscole automatiche" "Correzioni veloci" "Corregge gli errori di digitazione più comuni" - - - - - - - - - - - "Mostra tasto impostazioni" + "Mostra suggerimenti correzioni" + "Visualizza le parole suggerite durante la digitazione" + "Mostra sempre" + "Mostra in modalità verticale" + "Nascondi sempre" + "Mostra tasto impostaz." "Automatico" "Mostra sempre" "Nascondi sempre" - "Suggerimenti parola" - "Correggi automaticamente la parola precedente" - - - - - - + "Correzione automatica" + "Barra spaziatrice/punteggiatura correggono parole con errori" + "Off" + "Media" + "Massima" "Suggerimenti sui bigrammi" "Utilizza parola precedente per migliorare il suggerimento" "%s : parola salvata" @@ -59,16 +52,27 @@ "Avanti" "Fine" "Invia" - - + "ABC" "Altro" "Pausa" "Attesa" + "Cancella" + "Invio" + "Impostazioni" + "Maiuscolo" + "Spazio" + "Simboli" + "Tabulazione" + "Input vocale" + "Simboli attivati" + "Simboli disattivati" + "Maiuscole attivate" + "Maiuscole disattivate" "Comandi vocali" "I comandi vocali non sono attualmente supportati per la tua lingua ma funzionano in inglese." - "I comandi vocali sono una funzione sperimentale che utilizza il riconoscimento vocale in rete di Google." - "Per disattivare i comandi vocali, vai alle impostazioni della tastiera." - "Per utilizzare i comandi vocali, premi il pulsante del microfono o fai scorrere il dito sulla tastiera sullo schermo." + "L\'input vocale utilizza il riconoscimento vocale di Google. Sono valide le ""norme sulla privacy di Google Mobile""." + "Per disattivare l\'input vocale, vai alle impostazioni del metodo di input." + "Per utilizzare l\'input vocale, premi il pulsante del microfono." "Parla ora" "Elaborazione in corso" @@ -85,18 +89,12 @@ "Annulla" "OK" "Comandi vocali" - - - - - - - - - - - - + "Su tastiera principale" + "Su tastiera simboli" + "Non attivo" + "Mic su tastiera princ." + "Mic su tastiera simboli" + "Comandi vocali disatt." "Seleziona metodo di inserimento" "Lingue comandi" "Scorri il dito sulla barra spaziatrice per cambiare la lingua" @@ -104,75 +102,41 @@ "Dizionario disponibile" "Attiva commenti degli utenti" "Aiuta a migliorare l\'editor del metodo di inserimento inviando automaticamente a Google statistiche sull\'utilizzo e segnalazioni sugli arresti anomali." - "Tocca per correggere di nuovo le parole" - "Puoi correggere di nuovo le parole toccando quelle che hai digitato" + "Tocca per correggere" + "Tocca le parole inserite per correggerle, solo quando sono visibili i suggerimenti" "Tema della tastiera" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tastiera ceca" + "Tastiera danese" + "Tastiera tedesca" + "Tastiera inglese (Regno Unito)" + "Tastiera inglese (Stati Uniti)" + "Tastiera spagnola" + "Tastiera spagnola (Stati Uniti)" + "Tastiera francese" + "Tastiera francese (Canada)" + "Tastiera francese (Svizzera)" + "Tastiera italiana" + "Tastiera norvegese" + "Tastiera olandese" + "Tastiera russa" + "Tastiera serba" + "Tastiera svedese" + "Voce afrikaans" + "Voce ceca" + "Voce tedesca" + "Voce inglese" + "Voce spagnola" + "Voce francese" + "Voce italiana" + "Voce giapponese" + "Voce coreana" + "Voce olandese" + "Voce polacca" + "Voce portoghese" + "Voce russa" + "Voce turca" + "Voce cinese Yue" + "Voce cinese mandarino" + "Voce isiZulu" + "Modalità studio usabilità" diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 12c031b9e..af0854c97 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -26,32 +26,25 @@ "רטט עם לחיצה על מקשים" "צלילים עם לחיצה על מקשים" "חלון קופץ עם לחיצה על מקשים" - "הגדרות של הצעות מילים" + "כללי" + "תיקון טקסט" "הפיכה אוטומטית של אותיות לרישיות" "תיקונים מהירים" "מתקן שגיאות הקלדה נפוצות" - - - - - - - - - - + "הצג הצעות לתיקונים" + "הצג הצעות למילים בעת הקלדה" + "הצג תמיד" + "הצג בפריסה לאורך" + "הסתר תמיד" "הצג מקש הגדרות" "אוטומטי" "הצג תמיד" "הסתר תמיד" - "הצעות למילים" - "תקן באופן אוטומטי את המילה הקודמת" - - - - - - + "תיקון אוטומטי" + "מקש הרווח ופיסוק מתקנים אוטומטית שגיאות הקלדה" + "כבוי" + "מצומצם" + "מחמיר" "הצעות של צמדי אותיות (Bigram)" "השתמש במילה הקודמת כדי לשפר את ההצעה" "%s : נשמרה" @@ -59,16 +52,27 @@ "הבא" "בוצע" "שלח" - - + "ABC" "עוד" "השהה" "המתן" + "מחק" + "חזור" + "הגדרות" + "Shift" + "רווח" + "סמלים" + "כרטיסייה" + "קלט קולי" + "מצב סמלים פועל" + "מצב סמלים כבוי" + "Shift פועל" + "Shift כבוי" "קלט קולי" "קלט קולי אינו נתמך בשלב זה בשפתך, אך הוא פועל באנגלית." - "קלט קולי הוא תכונה ניסיונית של זיהוי הדיבור ברשת של Google." - "כדי לכבות את הקלט הקולי, עבור להגדרות מקלדת." - "כדי להשתמש בקלט הקולי, לחץ על לחצן המיקרופון או החלק את האצבע על המקלדת שבמסך." + "קלט קולי משתמש בזיהוי דיבור של Google.‏ ""מדיניות הפרטיות של \'Google לנייד\'"" חלה במקרה זה." + "כדי לכבות את הקלט הקולי, עבור להגדרות שיטת קלט." + "כדי להשתמש בקלט קולי, לחץ על לחצן המיקרופון." "דבר כעת" "פועל" @@ -85,18 +89,12 @@ "ביטול" "אישור" "קלט קולי" - - - - - - - - - - - - + "במקלדת הראשית" + "במקלדת הסמלים" + "כבוי" + "מיקרופון במקלדת הראשית" + "מיקרופון במקלדת הסמלים" + "הקלט הקולי מושבת" "בחר שיטת קלט" "שפות קלט" "החלק את האצבע על מקש הרווח כדי לשנות שפה" @@ -105,44 +103,40 @@ "הפוך משוב ממשתמשים לפעיל" "עזור לשפר שיטת קלט זו על ידי שליחה אוטומטית של סטטיסטיקת שימוש ודוחות קריסת מחשב ל-Google." "גע כדי לתקן מילים" - "גע במילים שהוזנו כדי לתקן אותן" + "גע במילים שהוזנו כדי לתקן אותן, רק כאשר הצעות מוצגות" "עיצוב מקלדת" "מקלדת צ\'כית" "מקלדת דנית" - "מקלדת גרמנית " - "מקלדת אנגלית (בריטניה)" - "מקלדת אנגלית (ארצות הברית)" + "מקלדת גרמנית " + "מקלדת אנגלית (בריטניה)" + "מקלדת אנגלית (ארה\"ב)" "מקלדת ספרדית" - - + "מקלדת ספרדית (ארה\"ב)" "מקלדת צרפתית" - - - - - "מקלדת איטלקית " + "מקלדת צרפתית (קנדה)" + "מקלדת צרפתית (שוויץ)" + "מקלדת איטלקית" "מקלדת נורווגית" "מקלדת הולנדית" "מקלדת רוסית" "מקלדת סרבית" "מקלדת שוודית" - "Google Voice צ\'כי" - "Google Voice גרמני" - "Google Voice אנגלי (אוסטרליה)" - "Google Voice אנגלי (בריטניה)" - "Google Voice אנגלי (הודו)" - "Googel Voice אנגלי (ניו זילנד)" - "Google Voice אנגלי (ארצות הברית)" + "Google Voice באפריקאנס" + "Google Voice צ\'כי" + "Google Voice גרמני" + "Google Voice באנגלית" "Google Voice ספרדי" "Google Voice צרפתי" + "קול באיטלקית" "Google Voice יפני" "Google Voice קוריאני" + "קול בהולנדית" "Google Voice פולני" "Google Voice פורטוגזי" "Google Voice רוסי" "Google Voice תורכי" - "Google Voice סיני (סין)" - "Google Voice סיני (טייוואן)" - - + "Google Voice בסינית, יו" + "Google Voice בסינית, מנדרינית" + "Google Voice באיסיזולו" + "מצב מחקר שימושיות" diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 22bd0e448..cd2cf8070 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -26,32 +26,25 @@ "キー操作バイブ" "キー操作音" "キー押下時ポップアップ" - "入力候補の設定" + "全般" + "テキストの修正" "自動大文字変換" "クイックフィックス" "よくある誤字・脱字を修正します" - - - - - - - - - - + "修正候補を表示する" + "入力中に入力候補を表示する" + "常に表示" + "縦向きで表示" + "常に非表示" "設定キーを表示" "自動" "常に表示" "常に非表示" - "入力候補表示" - "前の単語を自動修正する" - - - - - - + "自動修正" + "誤入力をスペースまたは句読点キーで修正する" + "OFF" + "中" + "強" "バイグラム入力候補表示" "直前の単語から入力候補を予測します" "%s:保存しました" @@ -59,16 +52,27 @@ "次へ" "完了" "送信" - - + "ABC" "Shift" "停止" "待機" + "Del" + "Enter" + "設定" + "Shift" + "Space" + "記号" + "Tab" + "音声入力" + "記号ON" + "記号OFF" + "Shift ON" + "Shift OFF" "音声入力" "音声入力は現在英語には対応していますが、日本語には対応していません。" - "音声入力はGoogleのネットワーク音声認識技術を利用した試験段階の機能です。" - "音声入力をOFFにするには、キーボードの設定を開きます。" - "音声入力するには、マイクボタンを押すか画面キーボードをスワイプしてください。" + "音声入力ではGoogleの音声認識技術を利用します。""モバイルプライバシーポリシー""が適用されます。" + "音声入力をOFFにするには、入力方法の設定を開きます。" + "音声入力するには、マイクボタンを押してください。" "お話しください" "処理中" @@ -85,18 +89,12 @@ "キャンセル" "OK" "音声入力" - - - - - - - - - - - - + "メインキーボード上" + "記号キーボード上" + "OFF" + "メインキーボードのマイク" + "記号キーボードのマイク" + "音声入力は無効です" "入力方法の選択" "入力言語" "スペースバーで指をスライドさせて言語を変更する" @@ -104,75 +102,41 @@ "辞書を利用できます" "ユーザーフィードバックを有効にする" "IMEの機能向上のため、使用統計状況やクラッシュレポートをGoogleに自動送信します。" - "タップして語句を再修正" - "入力した語句をタップすると語句を再修正できます" + "タップして語句を修正" + "候補が表示されているときのみ、入力した語句をタップして修正する" "キーボードテーマ" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "チェコ語のキーボード" + "デンマーク語のキーボード" + "ドイツ語のキーボード" + "英語(英国)のキーボード" + "英語(米国)のキーボード" + "スペイン語のキーボード" + "スペイン語(米国)のキーボード" + "フランス語のキーボード" + "フランス語(カナダ)のキーボード" + "フランス語(スイス)のキーボード" + "イタリア語のキーボード" + "ノルウェー語のキーボード" + "オランダ語のキーボード" + "ロシア語のキーボード" + "セルビア語のキーボード" + "スウェーデン語のキーボード" + "アフリカーンス語の音声" + "チェコ語の音声" + "ドイツ語の音声" + "英語の音声" + "スペイン語の音声" + "フランス語の音声" + "イタリア語の音声" + "日本語の音声" + "韓国語の音声" + "オランダ語の音声" + "ポーランド語の音声" + "ポルトガル語の音声" + "ロシア語の音声" + "トルコ語の音声" + "中国語(広東語)の音声" + "中国語(標準語)の音声" + "ズールー語の音声" + "使いやすさの研究モード" diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 13bd604e1..7a09da880 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -26,32 +26,25 @@ "키를 누를 때 진동 발생" "키를 누를 때 소리 발생" "키를 누를 때 팝업" - "단어 추천 설정" + "일반" + "텍스트 수정" "자동 대문자화" "빠른 수정" "자주 발생하는 오타를 수정합니다." - - - - - - - - - - + "수정 제안 표시" + "글자를 입력하는 동안 추천 단어 표시" + "항상 표시" + "세로 모드로 표시" + "항상 숨기기" "설정 키 표시" "자동" "항상 표시" "항상 숨기기" - "단어 추천" - "이전 단어를 자동으로 수정" - - - - - - + "자동 수정" + "스페이스바와 문장부호 키를 사용하면 오타가 자동으로 교정됩니다." + "사용 안함" + "보통" + "적극적" "Bigram 추천" "이전 단어를 사용하여 추천 기능 개선" "%s: 저장됨" @@ -59,16 +52,27 @@ "다음" "완료" "전송" - - + "ABC" "더보기" "일시 중지" "대기" + "삭제" + "리턴" + "설정" + "시프트" + "스페이스" + "기호" + "탭" + "음성 입력" + "기호 사용" + "기호 사용 안함" + "시프트 사용" + "시프트 사용 안함" "음성 입력" "음성 입력은 현재 자국어로 지원되지 않으며 영어로 작동됩니다." - "음성 입력은 Google의 네트워크화된 음성 인식을 사용하는 실험적 기능입니다." - "음성 입력을 사용하지 않으려면 키보드 설정으로 이동하세요." - "음성 입력을 사용하려면 마이크 버튼을 누르거나 터치 키보드 위로 손가락을 미끄러지듯 움직이세요." + "음성 입력에서는 Google의 음성 인식 기능을 사용합니다. ""모바일 개인정보취급방침""이 적용됩니다." + "음성 입력을 사용하지 않으려면 입력 방법 설정으로 이동하세요." + "음성 입력을 사용하려면 마이크 버튼을 누르세요." "지금 말하세요." "인식 중" @@ -85,18 +89,12 @@ "취소" "확인" "음성 입력" - - - - - - - - - - - - + "기본 키보드" + "기호 키보드" + "사용 안함" + "기본 키보드의 마이크" + "기호 키보드의 마이크" + "음성 입력이 사용 중지됨" "입력 방법 선택" "입력 언어" "손가락을 스페이스바에서 미끄러지듯 움직여 언어 변경" @@ -104,75 +102,41 @@ "사전 사용 가능" "사용자 의견 사용" "사용 통계 및 충돌 보고서를 Google에 자동으로 전송하여 입력 방법 편집기의 개선에 도움을 줍니다." - "터치하여 단어 다시 수정" - "입력한 단어를 터치하면 다시 수정할 수 있습니다." + "터치하여 단어 수정" + "입력한 단어를 터치하여 수정(추천 단어가 표시되는 경우에만)" "키보드 테마" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "체코어 키보드" + "덴마크어 키보드" + "독일어 키보드" + "영어(영국) 키보드" + "영어(미국) 키보드" + "스페인어 키보드" + "스페인어(미국) 키보드" + "프랑스어 키보드" + "프랑스어(캐나다) 키보드" + "프랑스어(스위스) 키보드" + "이탈리아어 키보드" + "노르웨이어 키보드" + "네덜란드어 키보드" + "러시아어 키보드" + "세르비아어 키보드" + "스웨덴어 키보드" + "아프리칸스어 음성" + "체코어 음성" + "독일어 음성" + "영어 음성" + "스페인어 음성" + "프랑스어 음성" + "이탈리아어 음성" + "일본어 키보드" + "한국어 음성" + "네덜란드어 음성" + "폴란드어 음성" + "포르투갈어 음성" + "러시아어 음성" + "터키어 음성" + "중국어, 광둥어 음성" + "중국어, 북경어 음성" + "줄루어 음성" + "가용성 연구 모드" diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index cc15bd6fe..c12c62aaa 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -26,32 +26,25 @@ "Vibruoti, kai paspaudžiami klavišai" "Klavišo paspaudimo garsas" "Iššoka paspaudus klavišą" - "Žodžių pasiūlymo nustatymai" + "Bendra" + "Teksto taisymas" "Automatinis didžiųjų raidžių rašymas" "Greiti pataisymai" "Taiso dažnai padarytas rašybos klaidas" - - - - - - - - - - + "Rodyti taisymo pasiūlymus" + "Įvedant tekstą pateikti siūlomų žodžių" + "Visada rodyti" + "Rodyti stačiuoju režimu" + "Visada slėpti" "Rodyti nustatymų raktą" "Automatinis" "Visada rodyti" "Visada slėpti" - "Žodžių pasiūlymai" - "Automatiškai taisyti ankstesnį žodį" - - - - - - + "Automatinis taisymas" + "Tarpo kl. ir skyr. ženkl. aut. išt. neteis. įv. žodž." + "Išjungta" + "Vidutinis" + "Atkaklus" "Digramų pasiūlymai" "Naudoti ankstesnį žodį pasiūlymui patobulinti" "%s: išsaugota" @@ -59,16 +52,27 @@ "Kitas" "Atlikta" "Siųsti" - - + "ABC" "Daugiau" "Prist." "Lauk." + "Ištrinti" + "Grįžti" + "Nustatymai" + "Keitimas" + "Tarpas" + "Simboliai" + "Skirtukas" + "Balso įvestis" + "Simboliai įjungti" + "Simboliai išjungti" + "Keitimas įjungtas" + "Keitimas išjungtas" "Balso įvestis" "Šiuo metu balso įvestis jūsų kompiuteryje nepalaikoma, bet ji veikia anglų k." - "Balso įvestis – tai eksperimentinė funkcija, naudojanti „Google“ tinklo kalbos atpažinimą." - "Jei norite išjungti balso įvestį, eikite į klaviatūros nustatymus." - "Jei norite naudoti balso įvestį, paspauskite mikrofono mygtuką arba pirštu slyskite ekranine klaviatūra." + "Balso įvesčiai naudojamas „Google“ kalbos atpažinimas. Taikoma ""privatumo politika mobiliesiems""." + "Jei norite išjungti balso įvestį, eikite į įvesties metodo nustatymus." + "Jei norite naudoti balso įvestį, paspauskite mikrofono mygtuką." "Kalbėkite dabar" "Veikia" @@ -85,18 +89,12 @@ "Atšaukti" "Gerai" "Balso įvestis" - - - - - - - - - - - - + "Pagr. klaviatūroje" + "Simbolių klaviatūr." + "Išjungta" + "Mikrof. pagr. klav." + "Mikrof. simb. klav." + "Balso įv. neleidž." "Pasirinkti įvesties metodą" "Įvesties kalbos" "Pirštu slyskite tarpo klavišu, kad pakeistumėte kalbą" @@ -104,45 +102,41 @@ "Žodynas galimas" "Įgalinti naudotojų atsiliepimus" "Padėkite patobulinti šią įvesties metodo redagavimo programą automatiškai „Google“ siųsdami naudojimo statistiką ir strigčių ataskaitas." - "Jei norite ištaisyti žodžius, palieskite" - "Jei norite ištaisyti įvestus žodžius, palieskite juos" + "Jei norite ištais. žodž., paliesk." + "Jei norite ištaisyti įvestus žodžius, palieskite juos tik tada, kai matomi pasiūlymai" "Klaviatūros tema" "Čekiška klaviatūra" "Daniška klaviatūra" - "Vokiška klaviatūra" - "Angliška (Didžioji Britanija) klaviatūra" - "Angliška (Jungtinės Amerikos Valstijos) klaviatūra" + "Vokiška klaviatūra" + "Angliška (JK) klaviatūra" + "Angliška (JAV) klaviatūra" "Ispaniška klaviatūra" - - + "Ispaniška (JAV) klaviatūra" "Prancūziška klaviatūra" - - - - - "Itališka klaviatūra" + "Prancūziška (Kanada) klaviatūra" + "Prancūziška (Šveicarija) klaviatūra" + "Itališka klaviatūra" "Norvegiška klaviatūra" "Olandiška klaviatūra" "Rusiška klaviatūra" "Serbiška klaviatūra" "Švediška klaviatūra" - "„Voice“ čekų k." - "„Voice“ vokiečių k." - "„Voice“ anglų k. (Australija)" - "„Voice“ anglų k. (Didžioji Britanija)" - "„Voice“ anglų k. (Indija)" - "„Voice“ anglų k. (Naujoji Zelandija)" - "„Voice“ anglų k. (Jungtinės Amerikos Valstijos)" + "„Voice“ afrikanų k." + "„Voice“ čekų k." + "„Voice“ vokiečių k." + "„Voice“ anglų k." "„Voice“ ispanų k." "„Voice“ prancūzų k." + "„Voice“ italų k." "„Voice“ japonų k." "„Voice“ korėjiečių k." + "„Voice“ olandų k." "„Voice“ lenkų k." "„Voice“ portugalų k." "„Voice“ rusų k." "„Voice“ turkų k." - "„Voice“ kinų k. (Kinija)" - "„Voice“ kinų k. (Taivanas)" - - + "„Voice“ kinų (dziue) k." + "„Voice“ kinų (mandarinų) k." + "„Voice“ zulų k." + "Tinkamumo analizės režimas" diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 550c7b284..8b975b033 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -26,32 +26,25 @@ "Vibrēt, nospiežot taustiņu" "Skaņa, nospiežot taustiņu" "Nospiežot taustiņu, parādīt uznirstošo izvēlni" - "Vārdu ieteikumu iestatījumi" + "Vispārīgi" + "Teksta korekcija" "Automātiska lielo burtu lietošana" "Ātrie labojumi" "Nodrošina izplatītu drukas kļūdu labošanu." - - - - - - - - - - + "Rādīt labojumu ieteikumus" + "Ievades laikā attēlot ieteiktos vārdus" + "Vienmēr rādīt" + "Rādīt portreta režīmā" + "Vienmēr slēpt" "Rādīt iestatījumu taustiņu" "Automātiski" "Vienmēr rādīt" "Vienmēr slēpt" - "Vārdu ieteikumi" - "Automātiski labot iepriekšējo vārdu" - - - - - - + "Automāt. korekcija" + "Atstarpes taustiņš un interpunkcija; automātiska kļūdainu vārdu labošana" + "Izslēgta" + "Mērena" + "Agresīva" "Bigram ieteikumi" "Ieteikuma uzlabošanai izmantot iepriekšējo vārdu" "%s: saglabāts" @@ -59,16 +52,27 @@ "Tālāk" "Gatavs" "Sūtīt" - - + "ABC" "Vairāk" "Pauze" "Gaidīt" + "Dzēšanas taustiņš" + "Atgriešanās taustiņš" + "Iestatījumu taustiņš" + "Pārslēgšanas taustiņš" + "Atstarpes taustiņš" + "Simbolu taustiņš" + "Tabulēšanas taustiņš" + "Runas ievades taustiņš" + "Simbolu režīms ir ieslēgts." + "Simbolu režīms ir izslēgts." + "Pārslēgšanas režīms ir ieslēgts." + "Pārslēgšanas režīms ir izslēgts." "Balss ievade" "Balss ievade jūsu valodā pašlaik netiek atbalstīta, taču tā ir pieejama angļu valodā." - "Balss ievade ir izmēģinājuma funkcija, kuras pamatā ir Google tīkla runas atpazīšanas līdzeklis." - "Lai izslēgtu balss ievadi, atveriet tastatūras iestatījumus." - "Lai izmantotu balss ievadi, nospiediet mikrofona pogu vai slidiniet pirkstus pāri ekrāna tastatūrai." + "Balss ievadei tiek izmantota Google runas atpazīšanas funkcija. Uz šīs funkcijas lietošanu attiecas ""mobilo sakaru ierīču lietošanas konfidencialitātes politika""." + "Lai izslēgtu balss ievadi, atveriet ievades metodes iestatījumus." + "Lai izmantotu balss ievadi, nospiediet mikrofona taustiņu." "Runājiet!" "Notiek apstrāde" @@ -85,18 +89,12 @@ "Atcelt" "Labi" "Balss ievade" - - - - - - - - - - - - + "Uz galv. tastatūras" + "Uz simbolu tastat." + "Izslēgts" + "Mikr.uz galv.tastat." + "Mikr.uz simb.tastat." + "Balss iev. atspējota" "Atlasīt ievades metodi" "Ievades valodas" "Slidiniet pirkstu uz atstarpes taustiņa, lai mainītu valodu" @@ -105,44 +103,40 @@ "Iespējot lietotāju atsauksmes" "Palīdziet uzlabot šo ievades metodes redaktoru, automātiski nosūtot lietojuma statistiku un pārskatus par avārijām uzņēmumam Google." "Pieskarties, lai izlabotu vārdus" - "Pieskarties ievadītajiem vārdiem, lai tos labotu" + "Pieskarties ievadītajiem vārdiem, lai tos labotu (tikai tad, ja tiek rādīti ieteikumi)." "Tastatūras motīvs" "Čehu tastatūra" "Dāņu tastatūra" - "Vācu tastatūra" - "Angļu tastatūra (Lielbritānija)" - "Angļu tastatūra (ASV)" + "Vācu tastatūra" + "Angļu (Lielbritānija) tastatūra" + "Angļu (ASV) tastatūra" "Spāņu tastatūra" - - + "Spāņu (ASV) tastatūra" "Franču tastatūra" - - - - - "Itāliešu tastatūra" + "Franču (Kanāda) tastatūra" + "Franču (Šveices) tastatūra" + "Itāļu tastatūra" "Norvēģu tastatūra" "Holandiešu tastatūra" "Krievu tastatūra" "Serbu tastatūra" "Zviedru tastatūra" - "Voice čehu valodā" - "Voice vācu valodā" - "Voice angļu valodā (Austrālija)" - "Voice angļu valodā (Lielbritānija)" - "Voice angļu valodā (Indija)" - "Voice angļu valodā (Jaunzēlande)" - "Voice angļu valodā (ASV)" + "Balss afrikandu valodā" + "Voice čehu valodā" + "Voice vācu valodā" + "Balss angļu valodā" "Voice spāņu valodā" "Voice franču valodā" + "Itāļu balss" "Voice japāņu valodā" "Voice korejiešu valodā" + "Holandiešu balss" "Voice poļu valodā" "Voice portugāļu valodā" "Voice krievu valodā" "Voice turku valodā" - "Voice ķīniešu valodā (Ķīna)" - "Voice ķīniešu valodā (Taivāna)" - - + "Balss ķīniešu val. (Kantonas dial.)" + "Balss ķīniešu v. (mandarīnu dial.)" + "Balss zulu valodā" + "Lietojamības izpētes režīms" diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 9ad756af0..0471e74c3 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -26,32 +26,25 @@ "Vibrer ved tastetrykk" "Lyd ved tastetrykk" "Hurtigvindu ved tastetrykk" - "Innstillinger for ordforslag" + "Generelt" + "Tekstkorrigering" "Stor forbokstav" "Autokorrektur" "Retter vanlige stavefeil" - - - - - - - - - - + "Vis rettingsforslag" + "Vis ordforslag under skriving" + "Vis alltid" + "Vis i stående modus" + "Skjul alltid" "Vis innstillingsnøkkel" "Automatisk" "Vis alltid" "Skjul alltid" - "Autokorrektur" - "Autokorriger forrige ord" - - - - - - + "Automatisk retting" + "Mellomromstast og skilletegn retter automat. feilstavede ord" + "Av" + "Moderat" + "Omfattende" "Bigram-forslag" "Bruk forrige ord til å forbedre forslaget" "%s: Lagret" @@ -59,16 +52,27 @@ "Neste" "Utfør" "Send" - - + "ABC" "Mer" "Pause" "Vent" + "Delete" + "Enter" + "Innstillinger" + "Shift" + "Mellomrom" + "Symboler" + "Tab" + "Taleinndata" + "Symboler er slått på" + "Symboler er slått av" + "Shift på" + "Shift av" "Stemmedata" "Stemmedata håndteres foreløpig ikke på ditt språk, men fungerer på engelsk." - "Talekommandoer er en eksperimentell funksjon som bruker Googles nettverksbaserte talegjenkjenning." - "Gå til innstillinger for tastatur for å slå av stemmedata." - "Du bruker talekommandoer ved å trykke på mikrofonknappen eller skyve fingeren over tastaturet på skjermen." + "Google Voice bruker Googles talegjenkjenning. ""Personvernreglene for mobil"" gjelder." + "Gå til innstillinger for inndatametode for å slå av stemmedata." + "Trykk på mikrofonknappen for å aktivere stemmedata." "Snakk nå" "Arbeider" @@ -85,18 +89,12 @@ "Avbryt" "OK" "Talekommando" - - - - - - - - - - - - + "På hovedtastatur" + "På talltastatur" + "Av" + "Mikrofon på hovedtast." + "Mikrofon på talltastatur" + "Taleinndata er deaktiv." "Velg inndatametode" "Inndataspråk" "Dra fingeren på mellomromstasten for å endre språk" @@ -104,75 +102,41 @@ "Ordbok tilgjengelig" "Aktiver brukertilbakemelding" "Ved å sende bruksstatistikk og programstopprapporter til Google automatisk, hjelper du oss med å gjøre redigeringsfunksjonen for denne inndatametoden enda bedre." - "Trykk for å endre ord" - "Du kan endre innskrevne ord ved å trykke på dem" + "Trykk for å endre ord" + "Når forslag er synlige, kan du trykke på ord du har skrevet inn, for å endre dem" "Tastaturtema" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tsjekkisk tastatur" + "Dansk tastatur" + "Tysk tastatur" + "Engelsk tastatur (Storbritannia)" + "Engelsk tastatur (USA)" + "Spansk tastatur" + "Spansk tastatur (USA)" + "Fransk tastatur" + "Fransk tastatur (Canada)" + "Fransk tastatur (Sveits)" + "Italiensk tastatur" + "Norsk tastatur" + "Nederlandsk tastatur" + "Russisk tastatur" + "Serbisk tastatur" + "Svensk tastatur" + "Afrikaans tale" + "Tsjekkisk tale" + "Tysk tale" + "Engelsk tale" + "Spansk tale" + "Fransk tale" + "Italiensk stemme" + "Japansk tale" + "Koreansk tale" + "Nederlandsk stemme" + "Polsk tale" + "Portugisisk tale" + "Russisk tale" + "Tyrkisk tale" + "Kinesisk (yue) tale" + "Kinesisk (mandarin) tale" + "isiZulu tale" + "Nyttighetsmodus" diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index 2203152ff..e5439eb38 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -23,35 +23,28 @@ "Android-toetsenbord" "Instellingen voor Android-toetsenbord" "Invoeropties" - "Trillen bij druk op toets" - "Geluid bij druk op een toets" + "Trillen bij toetsaanslag" + "Geluid bij toetsaanslag" "Pop-up bij toetsaanslag" - "Instellingen voor woordsuggesties" + "Algemeen" + "Tekstcorrectie" "Auto-hoofdlettergebruik" "Snelle oplossingen" "Hiermee worden veelvoorkomende typefouten gecorrigeerd" - - - - - - - - - - + "Correctievoorstellen weergeven" + "Voorgestelde woorden weergeven tijdens typen" + "Altijd weergeven" + "Weergeven in staande modus" + "Altijd verbergen" "Instellingscode weergeven" "Automatisch" "Altijd weergeven" "Altijd verbergen" - "Woordsuggesties" - "Het vorige woord automatisch corrigeren" - - - - - - + "Autocorrectie" + "Met spatiebalk en interpunctie worden verkeerd gespelde woorden automatisch gecorrigeerd" + "Uitgeschakeld" + "Normaal" + "Agressief" "Digram-suggesties" "Vorig woord gebruiken om suggestie te verbeteren" "%s: opgeslagen" @@ -59,16 +52,27 @@ "Volgende" "Gereed" "Verzenden" - - + "ABC" "Meer" "Onderbr." "Wacht" + "Delete" + "Return" + "Instellingen" + "Shift" + "Spatie" + "Symbolen" + "Tab" + "Spraakinvoer" + "Symbolen aan" + "Symbolen uit" + "Shift aan" + "Shift uit" "Spraakinvoer" "Spraakinvoer wordt momenteel niet ondersteund in uw taal, maar is wel beschikbaar in het Engels." - "Spraakinvoer is een experimentele functie met de spraakherkenning van het Google-netwerk." - "Als u spraakinvoer wilt uitschakelen, gaat u naar de toetsenbordinstellingen." - "Als u spraakinvoer gebruikt, drukt u op de microfoonknop of schuift u uw vinger over het schermtoetsenbord." + "Spraakinvoer maakt gebruik van de spraakherkenning van Google. Het ""Privacybeleid van Google Mobile"" is van toepassing." + "Als u spraakinvoer wilt uitschakelen, gaat u naar de instellingen voor invoermethoden." + "Druk op de microfoontoets om spraakinvoer te gebruiken." "Nu spreken" "Wordt uitgevoerd" @@ -79,24 +83,18 @@ "Serverfout" "Geen spraak te horen" "Geen resultaten gevonden" - "Voice Search is niet geïnstalleerd" + "Spraakgestuurd zoeken is niet geïnstalleerd" "Hint:"" schuif over het toetsenbord om te spreken" "Hint:"" spreek de volgende keer interpunctie uit, zoals \'period\' (punt), \'comma\' (komma) of \'question mark\' (vraagteken)." "Annuleren" "OK" "Spraakinvoer" - - - - - - - - - - - - + "Op hoofdtoetsenbord" + "Op symbooltoetsenb." + "Uitgeschakeld" + "Mic op hoofdtoetsb." + "Mic op symb.toetsb." + "Spraakinvoer is uit" "Invoermethode selecteren" "Invoertalen" "Schuif uw vinger over de spatiebalk om de taal te wijzigen" @@ -104,75 +102,41 @@ "Woordenboek beschikbaar" "Gebruikersfeedback inschakelen." "Help deze invoermethode te verbeteren door automatisch gebruiksstatistieken en crashmeldingen naar Google te verzenden." - "Raak aan om woorden opnieuw te corrigeren" - "U kunt woorden opnieuw corrigeren door woorden aan te raken die u heeft getypt" + "Raak aan om woorden te corrigeren" + "Ingevoerde woorden aanraken om ze te verbeteren, alleen mogelijk wanneer suggesties zichtbaar zijn" "Toetsenbordthema" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tsjechisch toetsenbord" + "Deens toetsenbord" + "Duits toetsenbord" + "Engels toetsenbord (VK)" + "Engels toetsenbord (VS)" + "Spaans toetsenbord" + "Spaans toetsenbord (VS)" + "Frans toetsenbord" + "Frans toetsenbord (Canada)" + "Frans toetsenbord (Zwitserland)" + "Italiaans toetsenbord" + "Noors toetsenbord" + "Nederlands toetsenbord" + "Russisch toetsenbord" + "Servisch toetsenbord" + "Zweeds toetsenbord" + "Afrikaanse stem" + "Tsjechische stem" + "Duitse stem" + "Engelse stem" + "Spaanse stem" + "Franse stem" + "Italiaanse stem" + "Japanse stem" + "Koreaanse stem" + "Nederlandse stem" + "Poolse stem" + "Portugese stem" + "Russische stem" + "Turkse stem" + "Chinese stem (Yue)" + "Chinese stem (Mandarijn)" + "isiZulu stem" + "Modus voor gebruiksvriendelijkheidsonderzoek" diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 78a35663b..7edd98840 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -25,50 +25,54 @@ "Opcje wprowadzania" "Wibracja przy naciśnięciu" "Dźwięk przy naciśnięciu" - "Wyświetlaj po naciśnięciu klawisza" - "Ustawienia propozycji słów" + "Powiększ po naciśnięciu" + "Ogólne" + "Korekta tekstu" "Wstawiaj wielkie litery" "Szybkie poprawki" "Poprawia częste błędy wpisywania" - - - - - - - - - - + "Pokazuj propozycje poprawek" + "Wyświetl proponowane słowa podczas wpisywania" + "Zawsze pokazuj" + "Pokaż w trybie pionowym" + "Zawsze ukrywaj" "Pokaż klawisz ustawień" "Automatycznie" "Zawsze pokazuj" "Zawsze ukrywaj" - "Sugestie słów" - "Automatycznie poprawiaj poprzednie słowo" - - - - - - + "Autokorekta" + "Spacja i znaki przestankowe powodują automatyczne poprawianie błędnie napisanych słów" + "Wyłącz" + "Umiarkowana" + "Agresywna" "Sugestie dla bigramów" "Używaj poprzedniego wyrazu, aby polepszyć sugestię" "%s : Zapisano" "OK" "Dalej" - "Gotowe" + "OK" "Wyślij" - - + "ABC" "Więcej" "Pauza" "Czekaj" + "Delete" + "Enter" + "Ustawienia" + "Shift" + "Spacja" + "Symbole" + "Tab" + "Wprowadzanie głosowe" + "Symbole włączone" + "Symbole wyłączone" + "Shift włączony" + "Shift wyłączony" "Wprowadzanie głosowe" "Wprowadzanie głosowe obecnie nie jest obsługiwane w Twoim języku, ale działa w języku angielskim." - "Wprowadzanie głosowe to funkcja eksperymentalna wykorzystująca funkcję firmy Google umożliwiającą rozpoznawanie mowy przy użyciu sieci." - "Aby wyłączyć wprowadzanie głosowe, przejdź do ustawień klawiatury." - "Aby skorzystać z wprowadzania głosowego, naciśnij przycisk mikrofonu lub przesuń palcem po klawiaturze ekranowej." + "Funkcja wprowadzania głosowego wykorzystuje mechanizm rozpoznawania mowy. Obowiązuje ""Polityka prywatności Google Mobile""." + "Aby wyłączyć wprowadzanie głosowe, przejdź do ustawień metody wprowadzania." + "Aby użyć wprowadzania głosowego, naciśnij przycisk mikrofonu." "Mów teraz" "W toku" @@ -85,18 +89,12 @@ "Anuluj" "OK" "Wprowadzanie głosowe" - - - - - - - - - - - - + "Na klawiaturze głównej" + "Na klawiaturze z symbolami" + "Wyłącz" + "Mikrofon na klawiaturze głównej" + "Mikrofon na klawiaturze z symbolami" + "Wprowadzanie głosowe jest wyłączone" "Wybierz metodę wprowadzania" "Języki wprowadzania" "Przesuń palcem po spacji, aby zmienić język" @@ -104,75 +102,41 @@ "Słownik dostępny" "Włącz przesyłanie opinii użytkownika" "Pomóż ulepszyć edytor tej metody wprowadzania, automatycznie wysyłając do Google statystyki użycia i raporty o awariach." - "Dotknij, aby ponownie poprawić słowa" - "Możesz ponownie poprawiać wprowadzone słowa, dotykając ich" + "Popraw dotknięte słowo" + "Dotykaj wprowadzonych słów, aby je poprawiać tylko wówczas, gdy widoczne są sugestie." "Motyw klawiatury" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Klawiatura czeska" + "Klawiatura duńska" + "Klawiatura niemiecka" + "Klawiatura angielska (UK)" + "Klawiatura angielska (USA)" + "Klawiatura hiszpańska" + "Klawiatura hiszpańska (USA)" + "Klawiatura francuska" + "Klawiatura francuska (Kanada)" + "Klawiatura francuska (Szwajcaria)" + "Klawiatura włoska" + "Klawiatura norweska" + "Klawiatura holenderska" + "Klawiatura rosyjska" + "Klawiatura serbska" + "Klawiatura szwedzka" + "Mowa afrikaans" + "Mowa czeska" + "Mowa niemiecka" + "Mowa angielska" + "Mowa hiszpańska" + "Mowa francuska" + "Mowa włoska" + "Mowa japońska" + "Mowa koreańska" + "Mowa holenderska" + "Mowa polska" + "Mowa portugalska" + "Mowa rosyjska" + "Mowa turecka" + "Mowa chińska (kantoński)" + "Mowa chińska (mandaryński)" + "Mowa isiZulu" + "Tryb badania przydatności" diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index c9ba0e552..f06d64c70 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -26,32 +26,25 @@ "Vibrar ao primir as teclas" "Som ao premir as teclas" "Mostrar popup ao premir tecla" - "Definições de sugestão de palavras" + "Geral" + "Correcção de texto" "Letras maiúsculas automáticas" "Correcções rápidas" "Corrige os erros de escrita comuns" - - - - - - - - - - + "Mostrar sugestões de correcção" + "Apresentar sugestões de palavras ao escrever" + "Mostrar sempre" + "Mostrar no modo de retrato" + "Ocultar sempre" "Mostrar tecla das definições" "Automático" "Mostrar sempre" "Ocultar sempre" - "Sugestões de palavras" - "Corrigir automaticamente a palavra anterior" - - - - - - + "Auto correcção" + "Correcção automática de palavras mal escritas c/ barra de espaços e pontuação" + "Desligar" + "Moderada" + "Agressiva" "Sugestões Bigram" "Utilizar a palavra anterior para melhorar a sugestão" "%s: guardada" @@ -59,16 +52,27 @@ "Seguinte" "Feito" "Enviar" - - + "ABC" "Mais" "Pausa" "Esp." + "Delete" + "Enter" + "Definições" + "Shift" + "Espaço" + "Símbolos" + "Tab" + "Entrada de voz" + "Símbolos ativados" + "Símbolos desativados" + "Shift ativado" + "Shift desativado" "Entrada de voz" "Actualmente, a entrada de voz não é suportada para o seu idioma, mas funciona em inglês." - "A entrada de voz é uma funcionalidade experimental que utiliza o reconhecimento de voz em rede da Google." - "Para desactivar a entrada de voz, aceda às definições do teclado." - "Para utilizar a entrada de voz, prima o botão do microfone ou deslize o dedo no teclado do ecrã." + "A entrada de voz utiliza o reconhecimento de voz da Google. É aplicável a ""Política de privacidade do Google Mobile""." + "Para desactivar a entrada de voz, aceda às definições do método de entrada." + "Para utilizar a entrada de voz, prima o botão do microfone." "Falar agora" "A executar" @@ -85,18 +89,12 @@ "Cancelar" "OK" "Entrada de voz" - - - - - - - - - - - - + "No teclado principal" + "No teclado símbolos" + "Desligar" + "Mic. tecl. principal" + "Mic. tecl. símbolos" + "Entr. voz desact." "Seleccionar método de entrada" "Idiomas de entrada" "Deslize o dedo pela barra de espaço para alterar o idioma" @@ -104,75 +102,41 @@ "Dicionário disponível" "Activar comentários do utilizador" "Envie automaticamente estatísticas de utilização e relatórios de falhas para a Google e ajude-nos a melhor este editor de método de introdução." - "Tocar para voltar a corrigir palavras" - "Pode voltar a corrigir palavras tocando naquelas que escreveu" + "Tocar para corrigir palavras" + "Tocar nas palavras introduzidas para as corrigir, apenas quando as sugestões estiverem visíveis" "Tema do teclado" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Teclado checo" + "Teclado dinamarquês" + "Teclado alemão" + "Teclado inglês (Reino Unido)" + "Teclado inglês (EUA)" + "Teclado espanhol" + "Teclado espanhol (EUA)" + "Teclado francês" + "Teclado francês (Canadá)" + "Teclado francês (Suíça)" + "Teclado italiano" + "Teclado norueguês" + "Teclado holandês" + "Teclado russo" + "Teclado sérvio" + "Teclado sueco" + "Voz em africânder" + "Voz checa" + "Voz alemã" + "Voz em inglês" + "Voz espanhola" + "Voz francesa" + "Voz italiana" + "Voz japonesa" + "Voz coreana" + "Voz holandesa" + "Voz polaca" + "Voz portuguesa" + "Voz russa" + "Voz turca" + "Voz em yue, chinês" + "Voz em mandarim, chinês" + "Voz em isiZulu" + "Modo de estudo da capacidade de utilização" diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index bc4981734..9fc1a97f2 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -25,33 +25,26 @@ "Opções de entrada" "Vibrar ao tocar a tecla" "Som ao tocar a tecla" - "Exibir pop-up ao tocar a tecla" - "Configurações de sugestão de palavra" + "Exibir pop-up ao digitar" + "Geral" + "Correção de texto" "Capitaliz. automática" "Reparos rápidos" "Corrige erros comuns de digitação" - - - - - - - - - - - "Mostrar tecla de configurações" + "Exibir sugestões de correção" + "Exibir sugestões de palavras durante a digitação" + "Mostrar sempre" + "Mostrar em modo retrato" + "Sempre ocultar" + "Mostrar tecla de config." "Automático" "Mostrar sempre" "Sempre ocultar" - "Sugestões de palavra" - "Corrigir automaticamente a palavra anterior" - - - - - - + "Autocorreção" + "A barra de espaço e a pontuação corrigem automaticamente palavras com erro de digitação" + "Desativado" + "Moderado" + "Agressivo" "Sugestões de bigrama" "Usar palavra anterior para melhorar a sugestão" "%s : Salvo" @@ -59,16 +52,27 @@ "Avançar" "Feito" "Enviar" - - + "ABC" "Mais" "Pausa" "Esp." + "Excluir" + "Voltar" + "Configurações" + "Shift" + "Espaço" + "Símbolos" + "Tab" + "Entrada de texto por voz" + "Símbolos ativados" + "Símbolos desativados" + "Shift ativado" + "Shift desativado" "Entrada de voz" "A entrada de voz não é suportada no momento para o seu idioma, mas funciona em inglês." - "A entrada de voz é um recurso experimental que usa o reconhecimento de fala de rede do Google." - "Para desativar a entrada de voz, vá para as configurações do teclado." - "Para usar a entrada de voz, pressione o botão com o microfone ou deslize o dedo sobre o teclado na tela." + "A entrada de texto por voz usa o reconhecimento de voz do Google. ""A política de privacidade para celulares"" é aplicada." + "Para desativar a entrada de texto por voz, vá para configurações do método de entrada." + "Para usar a entrada de texto por voz, pressione o botão do microfone." "Fale agora" "Trabalhando" @@ -85,18 +89,12 @@ "Cancelar" "OK" "Entrada de voz" - - - - - - - - - - - - + "No teclado principal" + "No teclado de símb." + "Desativado" + "Mic. no teclado" + "Mic. no teclado" + "Texto por voz desat." "Selecionar método de entrada" "Idiomas de entrada" "Deslize o dedo na barra de espaços para alterar o idioma" @@ -104,75 +102,41 @@ "Dicionário disponível" "Ativar comentário do usuário" "Ajude a melhorar este editor de método de entrada enviando automaticamente ao Google estatísticas de uso e relatórios de falhas." - "Toque para corrigir novamente as palavras" - "Você pode fazer novamente a correção tocando nas palavras digitadas" + "Tocar para corrigir" + "Toque nas palavras digitadas para corrigi-las apenas quando as sugestões estiverem visíveis" "Tema do teclado" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Teclado em tcheco" + "Teclado para dinamarquês" + "Teclado para alemão" + "Teclado para inglês (Reino Unido)" + "Teclado para inglês (EUA)" + "Teclado para espanhol" + "Teclado para espanhol" + "Teclado para francês" + "Teclado para francês (Canadá)" + "Teclado para francês (Suíça)" + "Teclado para italiano" + "Teclado para norueguês" + "Teclado para holandês" + "Teclado para russo" + "Teclado para sérvio" + "Teclado para sueco" + "Voz em africâner" + "Voz em tcheco" + "Voz em alemão" + "Voz em inglês" + "Voz em espanhol" + "Voz em francês" + "Voz italiana" + "Voz em japonês" + "Voz em coreano" + "Voz holandesa" + "Voz em polonês" + "Voz em português" + "Voz em russo" + "Voz em turco" + "Voz em chinês, cantonês" + "Voz em chinês, mandarim" + "Voz em zulu" + "Modo de estudo de utilização" diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 4f7007332..57548b5e4 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -27,11 +27,13 @@ "Vibrar cun smatgar in buttun" "Tun cun smatgar in buttun" "Pop-up cun smatgar ina tasta" - "Parameters da las propostas per pleds" + + + "Parameters da las propostas per pleds" "Maiusclas automaticas" "Correcturas sveltas" "Curregia sbagls da tippar currents" - + @@ -50,7 +52,7 @@ "Propostas da pleds" - "Curreger automaticamain il pled precedent" + "Curreger automaticamain il pled precedent" @@ -72,6 +74,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + "Cumonds vocals" "\"Cumonds vocals en Vossa lingua na vegnan actualmain betg sustegnids, ma la funcziun è disponibla per englais.\"" "Ils cumonds vocals èn ina funcziunalitad experimentala che utilisescha la renconuschientscha vocala da rait da Google." @@ -150,28 +176,26 @@ + + - - - - - - - - - + + + + + @@ -180,9 +204,11 @@ - + - + + + diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 0c5d62e32..52bed2785 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -26,32 +26,25 @@ "Vibrare la apăsarea tastei" "Sunet la apăsarea tastei" "Fereastră pop-up la apăsarea tastei" - "Setările sugestiei de cuvinte" + "General" + "Corectare text" "Auto-capitalizare" "Remedieri rapide" "Corectează greşelile introduse frecvent" - - - - - - - - - - + "Afişaţi sugestii de corectare" + "Afişaţi sugestii de cuvinte în timpul introducerii textului" + "Afişaţi întotdeauna" + "Afişaţi în modul Portret" + "Ascundeţi întotdeauna" "Afişaţi tasta setări" "Automat" "Afişaţi întotdeauna" "Ascundeţi întotdeauna" - "Sugestii de cuvinte" - "Corecţie automată a cuvântului anterior" - - - - - - + "Autocorecţie" + "Corectare automată cuvinte prin bară spaţiu/semne punctuaţie" + "Dezactivată" + "Moderată" + "Agresivă" "Sugestii pentru cuvinte de două litere" "Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestia" "%s: salvat" @@ -59,16 +52,27 @@ "Înainte" "Terminat" "Trimiteţi" - - + "ABC" "Mai multe" "Pauză" "Aşt." + "Ştergeţi" + "Tasta Enter" + "Setări" + "Shift" + "Tasta Space" + "Simboluri" + "Tasta Tab" + "Intrare vocală" + "Simbolurile sunt activate" + "Simbolurile sunt dezactivate" + "Tasta Shift este activată" + "Tasta Shift este dezactivată" "Intrare voce" "Intrarea vocală nu este acceptată în prezent pentru limba dvs., însă funcţionează în limba engleză." - "Intrarea vocală este o funcţie experimentală ce utilizează recunoaşterea vocală în reţea oferită de Google." - "Pentru a dezactiva intrarea vocală, accesaţi setările tastaturii." - "Pentru a utiliza intrarea vocală, apăsaţi butonul de microfon sau glisaţi degetul de-a lungul tastaturii de pe ecran." + "Intrarea vocală utilizează funcţia Google de recunoaştere vocală. Se aplică ""Politica de confidenţialitate Google Mobil""." + "Pentru a dezactiva intrarea vocală, accesaţi setările metodei de intrare." + "Pentru a utiliza intrarea vocală, apăsaţi pe butonul Microfon." "Vorbiţi acum" "Se analizează" @@ -85,18 +89,12 @@ "Anulaţi" "OK" "Intrare voce" - - - - - - - - - - - - + "Pe tastat. princip." + "Pe tastat. simbol." + "Dezactivată" + "Mic. pe tast. princ." + "Micr. pe tast. simb." + "Intr. vocală dezact." "Selectaţi metoda de introducere a textului" "Selectaţi limba" "Glisaţi degetul pe bara de spaţiu pentru a schimba limba" @@ -105,44 +103,40 @@ "Activaţi feedback de la utilizatori" "Ajutaţi la îmbunătăţirea acestui instrument de editare a metodelor de introducere a textului trimiţând în mod automat la Google statistici de utilizare şi rapoarte de blocare." "Atingeţi pentru a corecta cuvintele" - "Atingeţi cuvintele introduse pentru a le corecta" + "Atingeţi cuvintele introduse pentru a le corecta, numai când pot fi văzute sugestii" "Temă pentru tastatură" "Tastatură cehă" "Tastatură daneză" - "Tastatură germană" - "Tastatură engleză (Marea Britanie)" - "Tastatură engleză (S.U.A.)" + "Tastatură germană" + "Tastatură engleză (Marea Britanie)" + "Tastatură engleză (S.U.A.)" "Tastatură spaniolă" - - + "Tastatură spaniolă (S.U.A.)" "Tastatură franceză" - - - - - "Tastatură italiană" + "Tastatură franceză (Canada)" + "Tastatură franceză (Elveţia)" + "Tastatură italiană" "Tastatură norvegiană" "Tastatură olandeză" "Tastatură rusă" "Tastatură sârbă" "Tastatură suedeză" - "Voce cehă" - "Voce germană" - "Voce engleză (Australia)" - "Voce engleză (Marea Britanie)" - "Voce engleză (India)" - "Voce engleză (Noua Zeelandă)" - "Voce engleză (S.U.A.)" + "Voce afrikaans" + "Voce cehă" + "Voce germană" + "Voce engleză" "Voce spaniolă" "Voce franceză" + "Voce italiană" "Voce japoneză" "Voce coreeană" + "Voce olandeză" "Voce poloneză" "Voce portugheză" "Voce rusă" "Voce turcă" - "Voce chineză (China)" - "Voce chineză (Taiwan)" - - + "Voce chineză, yue" + "Voce chineză, mandarină" + "Voce isiZulu" + "Modul Studiu privind utilizarea" diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index a3fc39738..21fd02bd3 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -25,33 +25,26 @@ "Параметры ввода" "Виброотклик клавиш" "Звук клавиш" - "Показывать при нажатии" - "Настройки подсказок" + "Увеличение нажатых" + "Общие" + "Коррекция текста" "Автоподст. заглавных" "Быстрое исправление" "Исправлять распространенные опечатки" - - - - - - - - - - - "Показывать кнопку настроек" + "Показать варианты исправлений" + "Предлагать варианты слов во время ввода" + "Всегда показывать" + "Показать вертикально" + "Всегда скрывать" + "Кнопка настроек" "Автоматически" "Всегда показывать" "Всегда скрывать" - "Предложение слов" - "Автоматически исправлять предыдущее слово" - - - - - - + "Автоисправление" + "Автоматическое исправление опечаток при вводе знака препинания или пробела" + "Откл." + "Умеренное" + "Активное" "Биграммные подсказки" "Используйте предыдущее слово, чтобы исправить подсказку" "%s: сохранено" @@ -59,18 +52,29 @@ "Далее" "Готово" "Отправить" - - - "Еще" + "АБВ" + "Ещё" "Приостановить" "Подождите" + "Клавиша удаления" + "Клавиша \"Ввод\"" + "Клавиша настроек" + "Клавиша верхнего регистра" + "Клавиша \"Пробел\"" + "Клавиша символов" + "Клавиша табуляции" + "Клавиша голосового ввода" + "Клавиши символов выключены" + "Клавиши символов включены" + "Верхний регистр включен" + "Верхний регистр выключен" "Голосовой ввод" "В настоящее время функция голосового ввода не поддерживает ваш язык, но вы можете пользоваться ей на английском." - "Голосовой ввод – экспериментальная функция на основе технологии сетевого распознавания речи от Google." - "Функция голосового ввода отключается в настройках клавиатуры." - "Чтобы использовать голосовой ввод, нажмите кнопку микрофона или проведите пальцем по экранной клавиатуре." + "Голосовой ввод использует алгоритмы распознавания речи Google. Действует ""политика конфиденциальности для мобильных устройств""." + "Функция голосового ввода отключается в настройках способа ввода." + "Чтобы использовать голосовой ввод, нажмите кнопку микрофона." "Говорите" - "Выполняется обработка" + "Обработка запроса" "Ошибка. Повторите попытку." "Ошибка подключения" @@ -85,18 +89,12 @@ "Отмена" "ОК" "Голосовой ввод" - - - - - - - - - - - - + "Значок на основной клавиатуре" + "Значок на клавиатуре символов" + "Выкл." + "Значок на основной клавиатуре" + "Значок на клавиатуре символов" + "Голосовой ввод отключен" "Выбрать способ ввода" "Языки ввода" "Для изменения языка проведите пальцем по пробелу" @@ -104,75 +102,41 @@ "Доступен словарь" "Включить отправку сведений" "Помогите усовершенствовать редактор способа ввода, разрешив отправку статистики и отчетов о сбоях в Google." - "Исправление нажатием" - "Нажмите на слово, чтобы исправить его" + "Исправление нажатием" + "Нажмите на слово, чтобы исправить его (при наличии подсказок)" "Вид клавиатуры" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Клавиатура: чешская" + "Клавиатура: датская" + "Клавиатура: немецкая" + "Клавиатура: английская (Великобритания)" + "Клавиатура: английская (США)" + "Клавиатура: испанская" + "Клавиатура: испанская (США)" + "Клавиатура: французская" + "Клавиатура: французская" + "Клавиатура: французская (Швейцария)" + "Клавиатура: итальянская" + "Клавиатура: норвежская" + "Клавиатура: голландская" + "Клавиатура: русская" + "Клавиатура: сербская" + "Голос: шведский" + "Голосовой ввод на африкаанс" + "Голос: чешский" + "Голос: немецкий" + "Голосовой ввод на английском" + "Голос: испанский" + "Голос: французский" + "Голосовой ввод на итальянском" + "Голос: японский" + "Голос: корейский" + "Голосовой ввод на голландском" + "Голос: польский" + "Голос: португальский" + "Голос: русский" + "Голос: турецкий" + "Голосовой ввод на китайском (диалект юэ)" + "Голосовой ввод на китайском (мандаринский диалект)" + "Голосовой ввод на зулу" + "Режим проверки удобства использования" diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 9fc89945b..569f273a4 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -26,32 +26,25 @@ "Pri stlačení klávesu vibrovať" "Zvuk pri stlačení klávesu" "Zobraziť znaky pri stlačení klávesu" - "Nastavenia návrhov slov" + "Všeobecné" + "Oprava textu" "Veľké písmená automaticky" "Rýchle opravy" "Opravuje najčastejšie chyby pri písaní" - - - - - - - - - - + "Zobraziť návrhy opráv" + "Zobrazovať navrhované slová počas písania" + "Vždy zobrazovať" + "Zobraziť v režime na výšku" + "Vždy skrývať" "Zobraziť kláves Nastavenia" "Automaticky" "Vždy zobrazovať" "Vždy skrývať" - "Návrhy slov" - "Automaticky opraviť predchádzajúce slovo" - - - - - - + "Automatické opravy" + "Stlačením medzerníka a interpunkcie sa aut. opravia chybné slová" + "Vypnuté" + "Mierne" + "Agresívne" "Návrh Bigram" "Na zlepšenie návrhu použiť predchádzajúce slovo" "%s : Uložené" @@ -59,16 +52,27 @@ "Ďalej" "Hotovo" "Odoslať" - - + "ABC" "Viac" "Pozastaviť" "Čakajte" + "Delete" + "Return" + "Nastavenia" + "Shift" + "Medzera" + "Symboly" + "Tab" + "Hlasový vstup" + "Symboly zapnuté" + "Symboly vypnuté" + "Shift zapnutý" + "Shift vypnutý" "Hlasový vstup" "Pre váš jazyk aktuálne nie je hlasový vstup podporovaný, ale funguje v angličtine." - "Hlasový vstup je experimentálna funkcia, ktorá využíva sieťové rozpoznanie reči spoločnosti Google." - "Ak chcete vypnúť hlasový vstup, prejdite na nastavenia klávesnice." - "Ak chcete použiť hlasový vstup, stlačte tlačidlo mikrofónu alebo prejdite prstom po klávesnici na obrazovke." + "Hlasový vstup používa rozpoznávanie hlasu Google. Na používanie hlasového vstupu sa vzťahujú ""Pravidlá ochrany osobných údajov pre mobilné služby""." + "Ak chcete vypnúť hlasový vstup, prejdite na nastavenia metódy vstupu." + "Ak chcete použiť hlasový vstup, stlačte tlačidlo mikrofón." "Hovorte" "Prebieha spracovanie" @@ -85,18 +89,12 @@ "Zrušiť" "OK" "Hlasový vstup" - - - - - - - - - - - - + "Na hlavnej klávesnici" + "Na klávesnici so symbolmi" + "Vypnuté" + "Mikrofón na hlavnej klávesnici" + "Mikrofón na klávesnici so symbolmi" + "Hlasový vstup je zakázaný" "Výber metódy vstupu" "Jazyky vstupu" "Jazyk môžete zmeniť posunutím prsta po medzerníku." @@ -105,44 +103,40 @@ "Povoliť spätnú väzbu od používateľov" "Automatickým zasielaním štatistík o využívaní editora metódy vstupu a správ o jeho zlyhaní do služby Google môžete prispieť k vylepšeniu tohto nástroja." "Dotykom opravíte slová" - "Dotykom zadaným slov ich opravíte" + "Dotykom zadaných slov tieto slová opravíte, musia však byť viditeľné návrhy" "Motív klávesnice" "klávesnica – čeština" "klávesnica – dánčina" - "klávesnica – nemčina" - "hlas – angličtina (Veľká Británia)" - "klávesnica – angličtina (Spojené štáty)" + "klávesnica – nemčina" + "klávesnica – angličtina (br.)" + "klávesnica – angličtina (am.)" "klávesnica – španielčina" - - + "klávesnica – španielčina (am.)" "klávesnica – francúzština" - - - - - "klávesnica – taliančina" + "klávesnica – francúzština (Kanada)" + "klávesnica – francúzština (Švajč.)" + "klávesnica – taliančina" "klávesnica – nórčina" "klávesnica – holandčina" "klávesnica – ruština" "hlas – srbčina" "klávesnica – švédčina" - "hlas – čeština" - "hlas – nemčina" - "hlas – angličtina (Austrália)" - "hlas – angličtina (Veľká Británia)" - "hlas – angličtina (India)" - "hlas – angličtina (Nový Zéland)" - "klávesnica – angličtina (Spojené štáty)" + "Hlas – afrikánčina" + "hlas – čeština" + "hlas – nemčina" + "Hlas – angličtina" "hlas – španielčina" "hlas – francúzština" + "hlas – taliančina" "hlas – japončina" "hlas – kórejčina" + "hlas – holandčina" "hlas – poľština" "hlas – portugalčina" "hlas – ruština" "hlas – turečtina" - "hlas – čínština (Čína)" - "hlas – čínština (Taiwan)" - - + "Hlas – čínština, kantónčina" + "Hlas – čínština, mandarínska" + "Hlas – Zulu" + "Režim Štúdia vhodnosti použitia" diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 4a4093152..715e6c567 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -26,32 +26,25 @@ "Vibriranje ob pritisku tipke" "Zvok ob pritisku tipke" "Pojavno okno ob pritisku tipke" - "Nastavitve za predlaganje besede" + "Splošno" + "Popravek besedila" "Samodejne velike začetnice" "Hitri popravki" "Popravi pogoste tipkarske napake" - - - - - - - - - - + "Pokaži predloge popravkov" + "Pokaži predlagane besede med tipkanjem" + "Vedno pokaži" + "Pokaži v pokončnem načinu" + "Vedno skrij" "Pokaži tipko za nastavitve" "Samodejno" "Vedno pokaži" "Vedno skrij" - "Predlogi besed" - "Samodejno popravi prejšnjo besedo" - - - - - - + "Samodejni popravek" + "Preslednica in ločila samodejno popravijo napačno vtipkane besede" + "Izklopljeno" + "Zmerno" + "Agresivno" "Bigramni predlogi" "Predlog izboljšaj s prejšnjo besedo" "%s: shranjeno" @@ -59,16 +52,27 @@ "Naprej" "Dokončano" "Pošlji" - - + "ABC" "Več" "Premor" "Čakaj" + "Izbriši" + "Vračalka" + "Nastavitve" + "Dvigalka" + "Preslednica" + "Znaki" + "Tabulatorka" + "Glasovni vnos" + "Znaki vklopljeni" + "Znaki izklopljeni" + "Dvigalka vklopljena" + "Dvigalka izklopljena" "Glasovni vnos" "Glasovni vnos trenutno ni podprt v vašem jeziku, deluje pa v angleščini." - "Glasovni vnos je poskusna funkcija, ki uporablja Googlovo omrežno prepoznavanje govora." - "Če želite izklopiti glasovni vnos, pojdite na nastavitve tipkovnice." - "Če želite uporabiti glasovni vnos, pritisnite gumb z mikrofonom ali podrsajte s prstom po zaslonski tipkovnici." + "Glasovni vnos uporablja Googlovo prepoznavanje govora. Zanj velja ""pravilnik o zasebnosti za mobilne naprave""." + "Glasovni vnos izklopite v nastavitvah načina vnosa." + "Če želite uporabljati glasovni vnos, pritisnite gumb z mikrofonom." "Začnite govoriti" "Obdelava" @@ -85,18 +89,12 @@ "Prekliči" "V redu" "Glasovni vnos" - - - - - - - - - - - - + "Na glavni tipkovnici" + "Na tipk. s simboli" + "Izklopljeno" + "Mik. na glavni tipk." + "Mik. na tipk. s sim." + "Glas. vnos je onem." "Izberite način vnosa" "Jeziki vnosa" "Podrsajte s prstom po preslednici, da zamenjate jezik" @@ -105,44 +103,40 @@ "Omogoči povratne informacije uporabnikov" "S samodejnim pošiljanjem statističnih podatkov o uporabi in poročil o zrušitvah Googlu nam lahko pomagate izboljšati urejevalnik načina vnosa." "Dotaknite se besed in jih popravite" - "Dotaknite se vnesenih besed in jih popravite" + "Dotaknite se vnesenih besed in jih popravite, samo ko so predlogi vidni" "Tema tipkovnice" "Češka tipkovnica" "Danska tipkovnica" - "Nemška tipkovnica" - "Tipkovnica za britansko angleščino" - "Tipkovnica za ameriško angleščino" + "Nemška tipkovnica" + "Tipkovnica za britansko angleščino" + "Tipkovnica za ameriško angleščino" "Španska tipkovnica" - - + "Tipkovnica za ameriško španščino" "Francoska tipkovnica" - - - - - "Italijanska tipkovnica" + "Tipkovnica za kanadsko francoščino" + "Tipkovnica za švicarsko francoščino" + "Italijanska tipkovnica" "Norveška tipkovnica" "Nizozemska tipkovnica" "Ruska tipkovnica" "Srbska tipkovnica" "Švedska tipkovnica" - "Govor v češčini" - "Govor v nemščini" - "Govor v avstralski angleščini" - "Govor v britanski angleščini" - "Govor v indijski angleščini" - "Govor v novozelandski angleščini" - "Govor v ameriški angleščini" + "Govor v afrikanščini" + "Govor v češčini" + "Govor v nemščini" + "Govor v angleščini" "Govor v španščini" "Govor v francoščini" + "Govor v italijanščini" "Govor v japonščini" "Govor v korejščini" + "Govor v nizozemščini" "Govor v poljščini" "Govor v portugalščini" "Govor v ruščini" "Govor v turščini" - "Govor v kitajščini" - "Govor v tajvanski kitajščini" - - + "Govor v kitajščini, jue" + "Govor v kitajščini, mandarinščini" + "Govor v zulujščini" + "Način za preučevanje uporabnosti" diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 952192996..115732779 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -26,32 +26,25 @@ "Вибрирај на притисак тастера" "Звук на притисак тастера" "Искачући прозор приликом притиска тастера" - "Подешавања за предлагање речи" + "Опште" + "Исправљање текста" "Аутоматски унос великих слова" "Брзе исправке" "Исправља честе грешке у куцању" - - - - - - - - - - + "Прикажи предлоге за исправку" + "Приказивање предложених речи током уноса текста" + "Увек прикажи" + "Прикажи у усправном режиму" + "Увек сакриј" "Прикажи тастер за подешавања" "Аутоматски" "Увек прикажи" "Увек сакриј" - "Предлагање речи" - "Аутоматско исправљање претходне речи" - - - - - - + "Аутоматско исправљање" + "Размак и интерпункција аутоматски исправљају грешке у куцању" + "Искључи" + "Умерено" + "Агресивно" "Bigram предлози" "Користи претходну реч за побољшање предлога" "%s : Сачувано" @@ -59,16 +52,27 @@ "Следеће" "Готово" "Пошаљи" - - + "ABC" "Још" "Паузирај" "Сачекајте" + "Delete" + "Return" + "Подешавања" + "Shift" + "Размак" + "Симболи" + "Tab" + "Гласовни унос" + "Симболи су укључени" + "Симболи су искључени" + "Shift је укључен" + "Shift је искључен" "Гласовни унос" "Гласовни унос тренутно није подржан за ваш језик, али функционише на енглеском." - "Гласовни унос је експериментална функција која користи Google-ово мрежно препознавање гласа." - "Да бисте искључили гласовни унос, идите на подешавања тастатуре." - "Да бисте користили гласовни унос, притисните дугме за микрофон или превуците прст преко тастатуре на екрану." + "Гласовни унос користи Google-ову функцију за препознавање гласа. Примењује се ""политика приватности за мобилне уређаје""." + "Да бисте искључили гласовни унос, идите на подешавања за начин уноса." + "Да бисте користили гласовни унос, притисните дугме за микрофон." "Говорите сада" "Обрада" @@ -85,18 +89,12 @@ "Откажи" "Потврди" "Гласовни унос" - - - - - - - - - - - - + "На главној тастатури" + "На тастатури са симболима" + "Искључи" + "Микрофон на главној тастатури" + "Микрофон на тастатури са симболима" + "Гласовни унос је онемогућен" "Изаберите метод уноса" "Језици за унос" "Превуците прст преко тастера за размак да бисте променили језик" @@ -105,44 +103,40 @@ "Омогући повратну информацију корисника" "Помозите да се побољша овај уређивач режима уноса тако што ће се аутоматски послати статистика о коришћењу и извештаји о грешкама компанији Google." "Додирните да бисте исправили речи" - "Додирните унете речи да бисте их исправили" + "Додирните унете речи да бисте их исправили само када су предлози видљиви" "Тема тастатуре" "Језик тастатуре: чешки" "Језик тастатуре: дански" - "Језик тастатуре: немачки" - "Језик тастатуре: енглески (Велика Британија)" - "Језик тастатуре: енглески (САД)" + "Језик тастатуре: немачки" + "Језик тастатуре: енглески (УК)" + "Језик тастатуре: енглески (САД)" "Језик тастатуре: шпански" - - + "Језик тастатуре: шпански (САД)" "Језик тастатуре: француски" - - - - - "Језик тастатуре: италијански" + "Језик тастатуре: француски (Канада)" + "Језик тастатуре: француски (Швајц.)" + "Језик тастатуре: италијански" "Језик тастатуре: норвешки" "Језик тастатуре: холандски" "Језик тастатуре: руски" "Језик тастатуре: српски" "Језик тастатуре: шведски" - "глас на чешком" - "глас на немачком" - "глас на енглеском (Аустралија)" - "глас на енглеском (Велика Британија)" - "глас на енглеском (Индија)" - "енглески (Нови Зеланд) аудио" - "глас на енглеском (САД)" - "глас на шпанском" - "глас на француском" - "глас на јапанском" - "глас на корејском" - "глас на пољском" - "глас на португалском" - "глас на руском" - "глас на турском" - "глас на кинеском (Кина)" - "глас на кинеском (Тајван)" - - + "Глас на африкансу" + "Глас на чешком" + "Глас на немачком" + "Глас на енглеском" + "Глас на шпанском" + "Глас на француском" + "Глас на италијанском" + "Глас на јапанском" + "Глас на корејском" + "Глас на холандском" + "Глас на пољском" + "Глас на португалском" + "Глас на руском" + "Глас на турском" + "Глас на јуе кинеском" + "Глас на мандаринском кинеском" + "Глас на језику исизулу" + "Режим за студију могућности коришћења" diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 5666a2526..b8c62f46e 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -26,32 +26,25 @@ "Vibrera vid tangenttryck" "Knappljud" "Popup vid knapptryck" - "Inställningar för ordförslag" + "Allmänt" + "Textkorrigering" "Automatiska versaler" "Snabba lösningar" "Åtgärdar automatiskt vanliga misstag" - - - - - - - - - - + "Visa rättningsförslag" + "Visar ordförslag när du skriver" + "Visa alltid" + "Visa stående" + "Dölj alltid" "Visa inställningsknapp" "Automatiskt" "Visa alltid" "Dölj alltid" - "Ordförslag" - "Rätta automatiskt föregående ord" - - - - - - + "Autokorrigering" + "Blanksteg/skiljetecken rättar felstavning" + "Av" + "Måttlig" + "Aggressiv" "Bigramförslag" "Förbättra förslaget med föregående ord" "%s: sparat" @@ -59,16 +52,27 @@ "Nästa" "Färdig" "Skicka" - - + "ABC" "Mer" "Pausa" "Vänta" + "Ta bort" + "Retur" + "Inställningar" + "Skift" + "Blanksteg" + "Symboler" + "Tabb" + "Röstinmatning" + "Aktivera symboler" + "Inaktivera symboler" + "Aktivera Skift" + "Inaktivera Skift" "Röstindata" "Röstindata stöds inte på ditt språk än, men tjänsten fungerar på engelska." - "Röstinmatning är en funktion på experimentstadiet som använder Googles nätverks taligenkänning." - "Om du vill stänga av röstindata öppnar du inställningarna för tangentbordet." - "Om du vill använda röstinmatning trycker du på mikrofonknappen eller drar fingret över tangentbordet på skärmen." + "Röstinmatning använder sig av Googles tjänst för taligenkänning. ""Sekretesspolicyn för mobila enheter"" gäller." + "Om du vill stänga av röstinmatning öppnar du inställningarna för inmatningsmetod." + "Om du vill använda röstinmatning trycker du på mikrofonknappen." "Tala nu" "Fungerar" @@ -85,18 +89,12 @@ "Avbryt" "OK" "Röstindata" - - - - - - - - - - - - + "På huvudtangentbord" + "På symboltangentbord" + "Av" + "Mick huvudtangentbord" + "Mick bland symboler" + "Röstinmatning inaktiv" "Välj inmatningsmetod" "Inmatningsspråk" "Dra med fingret på blanksteg om du vill ändra språk" @@ -104,75 +102,41 @@ "En ordlista är tillgänglig" "Aktivera synpunkter från användare" "Du kan hjälpa till att förbättra inmatningsmetoden genom att automatiskt skicka användningsstatistik och felrapporter till Google." - "Tryck om du vill korrigera om ord" - "Du kan korrigera om ord genom att trycka på ord som du har skrivit" + "Tryck om du vill korrigera ord" + "Tryck på skrivna ord om du vill korrigera dem, endast när förslag visas" "Tangentbordstema" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Tjeckiskt tangentbord" + "Danskt tangentbord" + "Tyskt tangentbord" + "Engelskt tangentbord (Storbrit.)" + "Engelskt tangentbord (USA)" + "Spanskt tangentbord" + "Spanskt tangentbord (USA)" + "Franskt tangentbord" + "Franskt tangentbord (Kanada)" + "Franskt tangentbord (Schweiz)" + "Italienskt tangentbord" + "Norskt tangentbord" + "Holländskt tangentbord" + "Ryskt tangentbord" + "Serbiskt tangentbord" + "Svenskt tangentbord" + "Röst, afrikaans" + "Röst på tjeckiska" + "Röst på tyska" + "Röst, engelska" + "Röst på spanska" + "Röst på franska" + "Röst på italienska" + "Röst på japanska" + "Röst på koreanska" + "Röst på holländska" + "Röst på polska" + "Röst på portugisiska" + "Röst på ryska" + "Röst på turkiska" + "Röst, kinesiska, yue" + "Röst, kinesiska, mandarin" + "Röst, isiZulusk" + "Läge för användbarhetsstudie" diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 44f6ba8a9..836b98724 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -26,32 +26,25 @@ "สั่นเมื่อกดปุ่ม" "ส่งเสียงเมื่อกดปุ่ม" "ป๊อปอัปเมื่อกดแป้น" - "การตั้งค่าการแนะนำคำ" + "ทั่วไป" + "การแก้ไขข้อความ" "ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ" "แก้ไขด่วน" "แก้ไขข้อผิดพลาดในการพิมพ์ที่พบบ่อย" - - - - - - - - - - + "แสดงคำแนะนำการแก้ไข" + "แสดงคำที่แนะนำขณะพิมพ์" + "แสดงทุกครั้ง" + "แสดงในโหมดแนวตั้ง" + "ซ่อนทุกครั้ง" "แสดงแป้นการตั้งค่า" "อัตโนมัติ" "แสดงตลอดเวลา" "ซ่อนตลอดเวลา" - "การแนะนำคำ" - "แก้ไขคำก่อนหน้าอัตโนมัติ" - - - - - - + "การแก้ไขอัตโนมัติ" + "กดเว้นวรรคและเครื่องหมายจะแก้คำผิดอัตโนมัติ" + "ปิด" + "ปานกลาง" + "เข้มงวด" "คำแนะนำ Bigram" "ใช้คำก่อนหน้านี้เพื่อปรับปรุงคำแนะนำ" "%s : บันทึกแล้ว" @@ -59,16 +52,27 @@ "ถัดไป" "เสร็จสิ้น" "ส่ง" - - + "ABC" "เพิ่มเติม" "หยุดชั่วคราว" "รอ" + "ลบ" + "Return" + "การตั้งค่า" + "Shift" + "Space" + "สัญลักษณ์" + "Tab" + "ป้อนข้อมูลด้วยเสียง" + "สัญลักษณ์เปิดอยู่" + "สัญลักษณ์ปิดอยู่" + "Shift เปิดอยู่" + "Shift ปิดอยู่" "การป้อนข้อมูลด้วยเสียง" "ขณะนี้การป้อนข้อมูลด้วยเสียงยังไม่ได้รับการสนับสนุนในภาษาของคุณ แต่ใช้ได้ในภาษาอังกฤษ" - "การป้อนข้อมูลด้วยเสียงเป็นคุณลักษณะทดลองที่ใช้การจดจำเสียงที่มีการสร้างเครือข่ายไว้ของ Google" - "หากต้องการปิดการป้อนข้อมูลด้วยเสียง ไปที่การตั้งค่าแป้นพิมพ์" - "หากต้องการใช้การป้อนข้อมูลด้วยเสียง กดปุ่มไมโครโฟนหรือเลื่อนนิ้วผ่านแป้นพิมพ์บนหน้าจอ" + "ป้อนข้อมูลด้วยเสียงใช้การจดจำคำพูดของ Google "" นโยบายส่วนบุคคลของมือถือ""มีผลบังคับใช้" + "หากต้องการปิดการป้อนข้อมูลด้วยเสียง ไปที่การตั้งค่าวิธีการป้อนข้อมูล" + "หากต้องการใช้การป้อนข้อมูลด้วยเสียง ให้กดปุ่มไมโครโฟน" "พูดได้เลย" "กำลังทำงาน" @@ -85,18 +89,12 @@ "ยกเลิก" "ตกลง" "การป้อนข้อมูลด้วยเสียง" - - - - - - - - - - - - + "บนแป้นพิมพ์หลัก" + "บนแป้นพิมพ์สัญลักษณ์" + "ปิด" + "ไมค์บนแป้นพิมพ์หลัก" + "ไมค์บนแป้นพิมพ์สัญลักษณ์" + "ปิดใช้งานป้อนข้อมูลด้วยเสียง" "เลือกวิธีการป้อนข้อมูล" "ภาษาในการป้อนข้อมูล" "เลื่อนนิ้วไปบนแป้นเคาะวรรคเพื่อเปลี่ยนภาษา" @@ -105,44 +103,40 @@ "เปิดใช้งานการแสดงความคิดเห็นจากผู้ใช้" "ช่วยปรับปรุงตัวแก้ไขวิธีการป้อนข้อมูลนี้โดยการส่งสถิติการใช้งานและรายงานการขัดข้องถึง Google โดยอัตโนมัติ" "แตะเพื่อแก้ไขคำ" - "แตะคำที่ป้อนไว้เพื่อแก้ไข" + "แตะคำที่ป้อนไว้เพื่อแก้ไข เฉพาะเมื่อเห็นข้อเสนอแนะเท่านั้น" "ชุดรูปแบบแป้นพิมพ์" "แป้นพิมพ์ภาษาเช็ก" "แป้นพิมพ์ภาษาเดนมาร์ก" - "แป้นพิมพ์ภาษาเยอรมัน" - "แป้นพิมพ์ภาษาอังกฤษ (สหราชอาณาจักร)" - "แป้นพิมพ์ภาษาอังกฤษ (สหรัฐอเมริกา)" + "แป้นพิมพ์ภาษาเยอรมัน" + "แป้นพิมพ์ภาษาอังกฤษ (สหราชอาณาจักร)" + "แป้นพิมพ์ภาษาอังกฤษ (สหรัฐอเมริกา)" "แปันพิมพ์ภาษาสเปน" - - + "แป้นพิมพ์ภาษาสเปน (สหรัฐอเมริกา)" "แป้นพิมพ์ภาษาฝรั่งเศส" - - - - - "แป้นพิมพ์ภาษาอิตาลี" + "แป้นพิมพ์ภาษาฝรั่งเศส (แคนาดา)" + "แป้นพิมพ์ภาษาฝรั่งเศส (สวิตเซอร์แลนด์)" + "แป้นพิมพ์ภาษาอิตาลี" "แป้นพิมพ์ภาษานอร์เวย์" "แป้นพิมพ์ภาษาดัตช์" "แป้นพิมพ์ภาษารัสเซีย" "แป้นพิมพ์ภาษาเซอร์เบีย" "แป้นพิมพ์ภาษาสวีเดน" - "เสียงภาษาเช็ก" - "เสียงภาษาเยอรมัน" - "เสียงภาษาอังกฤษ (ออสเตรเลีย)" - "เสียงภาษาอังกฤษ (สหราชอาณาจักร)" - "เสียงภาษาอังกฤษ (อินเดีย)" - "เสียงภาษาอังกฤษ (นิวซีแลนด์)" - "เสียงภาษาอังกฤษ (สหรัฐอเมริกา)" + "เสียงภาษาแอฟริกัน" + "เสียงภาษาเช็ก" + "เสียงภาษาเยอรมัน" + "เสียงภาษาอังกฤษ" "เสียงภาษาสเปน" "เสียงภาษาฝรั่งเศส" + "เสียงภาษาอิตาลี" "เสียงภาษาญี่ปุ่น" "เสียงภาษาเกาหลี" + "เสียงภาษาดัตช์" "เสียงภาษาโปแลนด์" "เสียงภาษาโปรตุเกส" "เสียงภาษารัสเซีย" "เสียงภาษาตุรกี" - "เสียงภาษาจีน (จีน)" - "เสียงภาษาจีน (ไต้หวัน)" - - + "เสียงภาษาจีนกวางตุ้ง" + "เสียงภาษาจีนกลาง" + "เสียงภาษาซูลู" + "โหมดศึกษาประโยชน์ในการใช้งาน" diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index 1441480b5..55f98de10 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -26,32 +26,25 @@ "Mag-vibrate sa keypress" "Tunog sa keypress" "Popup sa keypress" - "Mga setting ng suhestiyon ng salita" + "Pangkalahatan" + "Pagwawasto ng teksto" "Auto-capitalization" "Mga mabilisang pagsasaayos" "Itinatama ang mga karaniwang na-type na mali" - - - - - - - - - - + "Magpakita ng mga suhestiyon ng pagwawasto" + "Ipakita ang mga iminumungkahing salita habang nagta-type" + "Palaging ipakita" + "Ipakita sa portrait mode" + "Palaging itago" "Ipakita ang key ng mga setting" "Awtomatiko" "Palaging ipakita" "Palaging itago" - "Mga suhestiyon ng salita" - "Awtomatikong itama ang nakaraang salita" - - - - - - + "Awtomatikong pagwasto" + "Awto tinatama ng spacebar at bantas ang maling na-type" + "Naka-off" + "Modest" + "Agresibo" "Mga Suhestiyon na Bigram" "Gamitin ang nakaraang salita upang pahusayin ang suhestiyon" "%s : Na-save" @@ -59,16 +52,27 @@ "Susunod" "Tapos na" "Ipadala" - - + "ABC" "Higit pa" "Pause" "Intay" + "Tanggalin" + "Bumalik" + "Mga Setting" + "Shift" + "Puwang" + "Mga Simbolo" + "Tab" + "Input ng Boses" + "Naka-on ang mga simbolo" + "Naka-off ang mga simbolo" + "Naka-on ang shift" + "Naka-off ang shift" "Pag-input ng boses" "Hindi kasalukuyang suportado ang pag-input ng boses para sa iyong wika, ngunit gumagana sa Ingles." - "Ang pag-input ng boses ay isang tampok na pang-eksperimento na gumagamit ng naka-network na pagkilala sa pananalita ng Google." - "Upang i-off ang pag-input ng boses, pumunta sa mga setting ng keyboard." - "Upang gumamit ng pag-input ng boses, pindutin ang pindutang microphone o i-slide ang iyong daliri sa screen keyboard." + "Gumagamit ang pag-input ng boses ng speech recognition ng Google. Nalalapat ""Ang Patakaran sa Privacy ng Mobile""." + "Upang i-off ang pag-input ng boses, pumunta sa mga setting ng pamamaraan ng pag-input." + "Upang gamitin ang pag-input ng boses, pindutin ang pindutan na mikropono." "Magsalita ngayon" "Nagtatrabaho" @@ -85,18 +89,12 @@ "Kanselahin" "OK" "Pag-input ng boses" - - - - - - - - - - - - + "Sa pangunahing keyboard" + "Sa keyboard ng mga simbolo" + "Naka-off" + "Mic sa pangunahing keyboard" + "Mic sa keyboard ng mga simbolo" + "Hindi pinagana ang voice input" "Pumili ng paraan ng pag-input" "Mag-input ng mga wika" "I-slide ang daliri sa spacebar upang palitan ang wika" @@ -105,44 +103,40 @@ "Paganahin ang feedback ng user" "Tumulong na pahusayin ang editor ng paraan ng pag-input na ito sa pamamagitan ng awtomatikong pagpapadala ng mga istatistika ng paggamit at mga ulat ng crash sa Google." "Pindutin upang itama ang mga salita" - "Pindutin ang mga ipinasok na salita upang itama ang mga ito" + "Pindutin ang mga inilagay na salita upang iwasto ang mga ito, kapag nakikita lang ang mga suhestiyon" "Tema ng Keyboard" "Czech na Keyboard" "Danish na Keyboard" - "German Keyboard" - "Ingles (Great Britain) na Keyboard" - "Ingles (Estados Unidos) na Keyboard" + "German na Keyboard" + "Ingles (UK) na Keyboard" + "Ingles (US) na Keyboard" "Spanish na Keyboard" - - + "Spanish (US) na Keyboard" "French na Keyboard" - - - - - "Italian Keyboard" + "French (Canada) na Keyboard" + "French (Switzerland) na Keyboard" + "Italian na Keyboard" "Norwegian na Keyboard" "Dutch na Keyboard" "Russian na Keyboard" "Serbian na Keyboard" "Swedish na Keyboard" - "Czech na Boses" - "German Boses" - "Ingles (Australia) na Boses" - "Ingles (Great Britain) na Boses" - "Ingles (India) na Boses" - "Ingles (New Zealand) na Boses" - "Ingles (Estados Unidos) na Boses" + "Afrikaans na Boses" + "Czech na Boses" + "German na Boses" + "Ingles na Boses" "Spanish na Boses" "French na Boses" + "Italian na Boses" "Japanese na Boses" "Korean na Boses" + "Dutch na Boses" "Polish na Boses" "Portuguese na Boses" "Russian na Boses" "Turkish na Boses" - "Chinese (China) na Boses" - "Chinese (Taiwan) na Boses" - - + "Chinese, Yue na Boses" + "Chinese, Mandarin na Boses" + "isiZulu na Boses" + "Usability Study Mode" diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 69e5642c7..1baed4de6 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -26,32 +26,25 @@ "Tuşa basıldığında titret" "Tuşa basıldığında ses çıkar" "Tuşa basıldığında pop-up aç" - "Kelime önerme ayarları" + "Genel" + "Metin düzeltme" "Otomatik olarak büyük harf yap" "Hızlı onarımlar" "Yaygın olarak yapılan yazım hatalarını düzeltir" - - - - - - - - - - + "Düzeltme önerilerini göster" + "Yazarken, önerilen kelimeleri görüntüle" + "Her zaman göster" + "Dikey modda göster" + "Her zaman gizle" "Ayarları göster tuşu" "Otomatik" "Her zaman göster" "Her zaman gizle" - "Kelime önerileri" - "Önceki kelimeyi otomatik olarak düzelt" - - - - - - + "Otomatik düzeltme" + "Boşluk çbğ ve nokt işr yanlış yazılan kelimeleri oto düzeltir" + "Kapalı" + "Ölçülü" + "Agresif" "Bigram Önerileri" "Öneriyi geliştirmek için önceki kelimeyi kullanın" "%s : Kaydedildi" @@ -59,16 +52,27 @@ "İleri" "Bitti" "Gönder" - - + "ABC" "Diğer" "Durkl" "Bekle" + "Sil" + "Return" + "Ayarlar" + "Üst Karakter" + "Boşluk" + "Simgeler" + "Sekme" + "Ses Girişi" + "Simgeler açık" + "Simgeler kapalı" + "Üst Karakter açık" + "Üst Karakter kapalı" "Ses girişi" "Ses girişi, şu anda sizin diliniz için desteklenmiyor ama İngilizce dilinde kullanılabilir." - "Ses girişi, Google\'ın ağ bağlantılı ses tanıma işlevini kullanan deneysel bir özelliktir." - "Ses girişini kapatmak için klavye ayarlarına gidin." - "Ses girişini kullanmak için mikrofon düğmesine basın veya parmağınızı dokunmatik klavye üzerinde kaydırın." + "Ses girişi Google\'ın konuşma tanıma işlevini kullanır. "" Mobil Gizlilik Politikası"" geçerlidir." + "Ses girişini kapatmak için giriş yöntemi ayarlarına gidin." + "Ses girişini kullanmak için mikrofon düğmesine basın." "Şimdi konuşun" "Çalışıyor" @@ -85,18 +89,12 @@ "İptal" "Tamam" "Ses girişi" - - - - - - - - - - - - + "Ana klavyede" + "Simge klavyesinde" + "Kapalı" + "Ana klavyede mikrfn" + "Simge klavysnd mikrf" + "Sesle giriş devr dşı" "Giriş yöntemini seç" "Giriş dilleri" "Dili değiştirmek için parmağınızı boşluk çubuğu üzerinde kaydırın" @@ -104,75 +102,41 @@ "Sözlük kullanılabilir" "Kullanıcı geri bildirimini etkinleştir" "Kullanım istatistiklerini ve kilitlenme raporlarını Google\'a otomatik olarak göndererek bu giriş yöntemi düzenleyicisinin iyileştirilmesine yardımcı olun." - "Kelimeleri düzeltmek için dokunun" - "Yazdığınız kelimelere dokunarak kelimelerde düzeltme yapabilirsiniz" + "Kelimeleri düzeltmek için dokunun" + "Yalnızca öneriler görünür olduğunda, düzeltmek için girilen kelimelere dokunun" "Klavye Teması" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "Çekçe Klavye" + "Danca Klavye" + "Almanca Klavye" + "İngilizce (İngiltere) Klavye" + "İngilizce (ABD) Klavye" + "İspanyolca Klavye" + "İspanyolca (ABD) Klavye" + "Fransızca Klavye" + "Fransızca (Kanada) Klavye" + "Fransızca (İsviçre) Klavye" + "İtalyanca Klavye" + "Norveççe Klavye" + "Felemenkçe Klavye" + "Rusça Klavye" + "Sırpça Klavye" + "İsveççe Klavye" + "Afrikanca Ses" + "Çekçe Ses" + "Almanca Ses" + "İngilizce Ses" + "İspanyolca Ses" + "Fransızca Ses" + "İtalyanca Ses" + "Japonca Ses" + "Korece Ses" + "Felemenkçe Ses" + "Lehçe Ses" + "Portekizce Ses" + "Rusça Ses" + "Türkçe Ses" + "Çince, Yue Ses" + "Çince, Mandarin Ses" + "Zuluca Ses" + "Kullanılabilirlik Çalışması Modu" diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 4dc591b13..26906329a 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -26,32 +26,25 @@ "Вібр при натиску клав." "Звук при натиску клав." "Сплив. при нат.клав." - "Налашт-ня пропозицій слів" + "Загальні" + "Виправлення тексту" "Авто викор. вел. літер" "Шв. виправлення" "Виправляє поширені помилки" - - - - - - - - - - + "Показувати пропозиції виправлень" + "Відображати пропоновані слова під час вводу" + "Завжди показувати" + "Показувати в книжковому режимі" + "Завжди ховати" "Показ. клав. налашт." "Автоматично" "Завжди показ." "Завжди ховати" - "Пропозиції слів" - "Автоматично виправляти попереднє слово" - - - - - - + "Автомат. виправлення" + "Пробіл і пунктуація автоматично виправляють слова з помилками" + "Вимк." + "Середнє" + "Повне" "Двобуквені пропозиції" "Викор. попер. слово для покращ. пропозиції" "%s : збережено" @@ -59,16 +52,27 @@ "Далі" "Готово" "Надісл." - - + "Алфавіт" "Більше" "Пауза" "Чек." + "Клавіша Delete" + "Клавіша Return" + "Клавіша Settings" + "Клавіша Shift" + "Клавіша Space" + "Клавіша Symbols" + "Клавіша Tab" + "Клавіша Voice Input" + "Символи ввімкнено" + "Символи вимкнено" + "Shift увімкнено" + "Shift вимкнено" "Голос. ввід" "Голос. ввід наразі не підтрим. для вашої мови, але можна користуватися англійською." - "Голос. ввід є експеремент. ф-цією, яка викор. мережеве розпізнавання голосу Google." - "Щоб вимкн. голос ввід, йдіть до налашт-нь клавіатури." - "Щоб викор. голос. ввід, натисніть кнопку мікрофона або пересуньте палець на екранній клавіатурі." + "Голосовий ввід використовує розпізнавання мовлення Google. Застосовується ""Політика конфіденційності для мобільних пристроїв""." + "Щоб вимкнути голосовий ввід, перейдіть до налаштувань методів введення." + "Щоб використовувати голосовий ввід, натисніть кнопку мікрофона." "Диктуйте" "Працює" @@ -85,18 +89,12 @@ "Скасувати" "OK" "Голос. ввід" - - - - - - - - - - - - + "На основ. клавіатурі" + "Символьна клавіатура" + "Вимк." + "Miкр. на осн. клав." + "Miкр. на симв. клавіат." + "Голос. ввід вимкнено" "Вибрати метод введення" "Мови вводу" "Переміст. палець на пробіл, щоб змін. мову" @@ -105,44 +103,40 @@ "Увімк. відгуки корист." "Допоможіть покращ. редактор методу введ., автомат. надсилаючи в Google статистику використ. та звіти про збої." "Торкн., щоб виправ. слова" - "Торкн. введених слів, щоб виправити їх" + "Торкніться введених слів, щоб виправити їх, лише коли ввімкнено пропозиції" "Тема клавіатури" "Чеська розкладка" "Данська розкладка" - "Німецька розкладка" - "Англ. розкладка (Великобританія)" - "Англійська розкладка (США)" + "Німецька розкладка" + "Англ. розкладка (Великобританія)" + "Англійська розкладка (США)" "Іспанська розкладка" - - - "Франц. розкладка" - - - - - "Італ. розкладка" + "Іспанська розкладка (США)" + "Французька розкладка" + "Французька розкладка (Канада)" + "Французька розкладка (Швейцарія)" + "Італійська розкладка" "Норвезька розкладка" "Голланд. розклад." "Російська розкладка" "Сербська розкладка" "Шведська розкладка" - "Чеський голос" - "Німецький голос" - "Англ. голос (Австралія)" - "Англ. голос (Великобританія)" - "Англ. голос (Індія)" - "Англ. голос (Нова Зеландія)" - "Англійський голос (США)" - "Іспанський голос" - "Франц. голос" - "Японський голос" - "Корейськ. голос" - "Польський голос" - "Португал. голос" - "Російський голос" - "Турецький голос" - "Китайський голос (Китай)" - "Китайськ. голос (Тайвань)" - - + "Voice мовою африкаанс" + "Голос чеською" + "Голос німецькою" + "Voice англійською мовою" + "Голос іспанською" + "Голос французькою" + "Voice італійською мовою" + "Голос японською" + "Голос корейською" + "Voice голландською мовою" + "Голос польською" + "Голос португальською" + "Голос російською" + "Голос турецькою" + "Voice китайською, юеською мовою" + "Voice китайською, мандарин. мовою" + "Voice мовою ісізулу" + "Режим вивчення зручності у використанні" diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index e03fb3706..70defe3b7 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -26,32 +26,25 @@ "Rung khi nhấn phím" "Âm thanh khi nhấn phím" "Cửa sổ bật lên khi nhấn phím" - "Cài đặt đề xuất từ" + "Chung" + "Sửa văn bản" "Tự động viết hoa" "Sửa nhanh" "Sửa lỗi nhập thông thường" - - - - - - - - - - + "Hiển thị gợi ý sửa" + "Hiển thị từ được đề xuất khi nhập" + "Luôn hiển thị" + "Hiển thị trên chế độ khổ đứng" + "Luôn ẩn" "Hiển thị phím cài đặt" "Tự động" "Luôn hiển thị" "Luôn ẩn" - "Đề xuất từ" - "Tự động sửa từ trước đó" - - - - - - + "Tự động sửa" + "Phím cách và dấu câu tự động sửa từ nhập sai" + "Tắt" + "Đơn giản" + "Linh hoạt" "Đề xuất Bigram" "Sử dụng từ trước đó để cải tiến đề xuất" "%s : Đã lưu" @@ -59,16 +52,27 @@ "Tiếp theo" "Xong" "Gửi" - - + "ABC" "Khác" "Tạm dừng" "Đợi" + "Xóa" + "Quay lại" + "Cài đặt" + "Shift" + "Dấu cách" + "Biểu tượng" + "Tab" + "Nhập liệu bằng giọng nói" + "Bật biểu tượng" + "Tắt biểu tượng" + "Bật Shift" + "Tắt Shift" "Nhập liệu bằng giọng nói" "Nhập liệu bằng giọng nói hiện không được hỗ trợ cho ngôn ngữ của bạn nhưng hoạt động với ngôn ngữ tiếng Anh." - "Nhập liệu bằng giọng nói là tính năng thử nghiệm sử dụng nhận dạng tiếng nói được kết nối mạng của Google." - "Để tắt nhập liệu bằng giọng nói, đi tới cài đặt bàn phím." - "Để sử dụng nhập liệu bằng giọng nói, hãy nhấn nút micrô hoặc trượt ngón tay trên bàn phím ảo." + "Nhập liệu bằng giọng nói sử dụng nhận dạng giọng nói của Google. Áp dụng ""Chính sách bảo mật dành cho điện thoại di động""." + "Để tắt nhập liệu bằng giọng nói, đi tới cài đặt phương pháp nhập liệu." + "Để sử dụng nhập liệu bằng giọng nói, hãy nhấn nút micrô." "Xin mời nói" "Đang hoạt động" @@ -85,18 +89,12 @@ "Huỷ" "OK" "Nhập liệu bằng giọng nói" - - - - - - - - - - - - + "Trên bàn phím chính" + "Trên bàn phím biểu tượng" + "Tắt" + "Micrô trên bàn phím chính" + "Micrô trên bàn phím biểu tượng" + "Nhập liệu bằng giọng nói đã bị vô hiệu hóa" "Chọn phương thức nhập" "Ngôn ngữ nhập" "Trượt ngón tay trên phím cách để thay đổi ngôn ngữ" @@ -105,44 +103,40 @@ "Bật phản hồi của người dùng" "Giúp nâng cao trình chỉnh sửa phương thức nhập này bằng cách tự động gửi thống kê sử dụng và báo cáo sự cố cho Google." "Chạm để sửa từ" - "Chạm các từ đã nhập để sửa" + "Chạm các từ đã nhập để sửa, chỉ khi các đề xuất hiển thị" "Chủ đề bàn phím" "Bàn phím tiếng Séc" "Bàn phím tiếng Đan Mạch" - "Bàn phím tiếng Đức" - "Bàn phím tiếng Anh (Anh)" - "Bàn phím tiếng Anh (Mỹ)" + "Bàn phím tiếng Đức" + "Bàn phím tiếng Anh (Anh)" + "Bàn phím tiếng Anh (Mỹ)" "Bàn phím tiếng Tây Ban Nha" - - + "Bàn phím tiếng Tây Ban Nha (Mỹ)" "Bàn phím tiếng Pháp" - - - - - "Bàn phím tiếng Ý" + "Bàn phím tiếng Pháp (Canada)" + "Bàn phím tiếng Pháp (Thụy Sĩ)" + "Bàn phím tiếng Ý" "Bàn phím tiếng Na Uy" "Bàn phím tiếng Hà Lan" "Bàn phím tiếng Nga" "Bàn phím tiếng Serbia" "Bàn phím tiếng Thụy Điển" - "Giọng nói tiếng Séc" - "Giọng nói tiếng Đức" - "Giọng nói tiếng Anh (Úc)" - "Giọng nói tiếng Anh (Anh)" - "Giọng nói tiếng Anh (Ấn Độ)" - "Giọng nói tiếng Anh (New Zealand)" - "Giọng nói tiếng Anh (Mỹ)" + "Giọng tiếng Hà Lan ở Nam Phi" + "Giọng nói tiếng Séc" + "Giọng nói tiếng Đức" + "Giọng tiếng Anh" "Giọng nói tiếng Tây Ban Nha" "Giọng nói tiếng Pháp" + "Giọng nói tiếng Ý" "Giọng nói tiếng Nhật" "Giọng nói tiếng Hàn" + "Giọng nói tiếng Hà Lan" "Giọng nói tiếng Ba Lan" "Giọng nói tiếng Bồ Đào Nha" "Giọng nói tiếng Nga" "Giọng nói tiếng Thổ Nhĩ Kỳ" - "Giọng nói tiếng Trung (Trung Quốc)" - "Giọng nói tiếng Trung (Đài Loan)" - - + "Giọng tiếng Trung, Yue" + "Giọng tiếng Trung, tiếng Quan thoại" + "Giọng isiZulu" + "Chế độ nghiên cứu tính khả dụng" diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 2ef0c9c55..3b092bfcc 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -26,32 +26,25 @@ "按键时振动" "按键时播放音效" "按键时显示弹出窗口" - "字词建议设置" + "常规" + "文本更正" "自动大写" "快速纠正" "纠正常见的输入错误" - - - - - - - - - - + "显示更正建议" + "键入时显示建议的字词" + "始终显示" + "以纵向模式显示" + "始终隐藏" "显示设置键" "自动" "始终显示" "始终隐藏" - "字词建议" - "自动纠正前面的字词" - - - - - - + "自动更正" + "空格键或标点自动更正拼写错误的字词" + "关闭" + "部分" + "全部" "双连词建议" "使用以前的字词改进建议" "%s:已保存" @@ -59,16 +52,27 @@ "下一步" "完成" "发送" - - + "ABC" "更多" "暂停" "等待" + "删除" + "回车" + "设置" + "Shift" + "空格" + "符号" + "Tab" + "语音输入" + "符号模式已打开" + "符号模式已关闭" + "Shift 模式已打开" + "Shift 模式已关闭" "语音输入" "语音输入功能当前还不支持您的语言,您只能输入英语语音。" - "语音输入是一项试验性的功能,它采用了 Google 的网络语音识别功能。" - "要关闭语音输入功能,请转至键盘设置。" - "要使用语音输入,请按麦克风按钮或者在屏幕键盘上滑动手指。" + "语音输入采用了 Google 的语音识别技术,因此请遵守""“Google 移动”隐私权政策""。" + "要关闭语音输入功能,请转至输入法设置。" + "要使用语音输入功能,请按“麦克风”按钮。" "请开始说话" "正在处理" @@ -85,18 +89,12 @@ "取消" "确定" "语音输入" - - - - - - - - - - - - + "主键盘上" + "符号键盘上" + "关闭" + "主键盘上的麦克风" + "符号键盘上的麦克风" + "语音输入功能已停用" "选择输入法" "输入语言" "在空格键上滑动手指可更改语言" @@ -104,75 +102,41 @@ "提供字典" "启用用户反馈" "自动向 Google 发送使用情况统计信息和崩溃报告,帮助改进该输入法编辑器。" - "触摸即可更正字词" - "您可以触摸已键入的字词,对其进行更正" + "触摸以更正字词" + "仅在系统显示建议后,才触摸输入的字词进行更正" "键盘主题" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "捷克语键盘" + "丹麦语键盘" + "德语键盘" + "英语(英国)键盘" + "英语(美国)键盘" + "西班牙语键盘" + "西班牙语(美国)键盘" + "法语键盘" + "法语(加拿大)键盘" + "法语(瑞士)键盘" + "意大利语键盘" + "挪威语键盘" + "荷兰语键盘" + "俄语键盘" + "塞尔维亚语键盘" + "瑞典语键盘" + "南非荷兰语语音" + "捷克语语音" + "德语语音" + "英语语音" + "西班牙语语音" + "法语语音" + "意大利语语音" + "日语语音" + "韩语语音" + "荷兰语语音" + "波兰语语音" + "葡萄牙语语音" + "俄语语音" + "土耳其语语音" + "中文,粤语语音" + "中文,普通话语音" + "祖鲁语语音" + "可用性研究模式" diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 0f127aec3..c9c7ae88a 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -26,32 +26,25 @@ "按鍵時震動" "按鍵時播放音效" "按鍵時顯示彈出式視窗" - "字詞建議設定" + "一般設定" + "文字修正" "自動大寫" "快速修正" "修正一般打字錯誤" - - - - - - - - - - + "顯示修正建議" + "輸入時顯示建議字詞" + "永遠顯示" + "以垂直模式顯示" + "永遠隱藏" "顯示設定金鑰" "自動" "永遠顯示" "永遠隱藏" - "字詞建議" - "自動修正前一個字詞" - - - - - - + "自動修正" + "自動插入空白鍵和標點符號鍵盤,以修正拼字錯誤" + "關閉" + "部分" + "全部" "雙連詞建議" "根據前一個字詞自動找出更適合的建議" "%s:已儲存" @@ -59,16 +52,27 @@ "繼續" "完成" "傳送" - - + "ABC" "更多" "暫停" "等候" + "刪除" + "返回" + "設定" + "Shift 鍵" + "空白鍵" + "符號" + "Tab 鍵" + "語音輸入" + "開啟符號" + "關閉符號" + "開啟位移" + "關閉位移" "語音輸入" "語音輸入目前不支援您的語言,但是可以辨識英文。" - "語音輸入這項實驗功能運用了 Google 的網路語音辨識系統。" - "請前往鍵盤設定來關閉語音輸入。" - "如要使用語音輸入,按下 [麥克風] 按鈕,或將手指滑過螢幕小鍵盤即可。" + "語音輸入使用 Google 的語音辨識功能,並遵循《""行動服務隱私權政策""》。" + "如要關閉語音輸入,請前往輸入方式設定。" + "如要使用語音輸入,請按下麥克風按鈕。" "請說話" "辨識中" @@ -85,18 +89,12 @@ "取消" "確定" "語音輸入" - - - - - - - - - - - - + "主鍵盤上" + "符號鍵盤上" + "關閉" + "主鍵盤上的麥克風" + "符號鍵盤上的麥克風" + "語音輸入已停用" "選取輸入法" "輸入語言" "以手指在空白鍵上滑動可變更語言" @@ -104,75 +102,41 @@ "可使用字典" "啟用使用者意見回饋" "自動將使用統計資料和當機報告傳送給 Google,協助改善這個輸入法編輯器。" - "輕觸即可重新修正字詞" - "只要輕觸您所輸入的字詞,即可重新予以修正" + "輕觸此處可修正字詞" + "輕觸輸入的字詞即可加以修正 (出現建議時才適用)" "鍵盤主題" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "捷克文鍵盤" + "丹麥文鍵盤" + "德文鍵盤" + "英文 (英國) 鍵盤" + "英文 (美國) 鍵盤" + "西班牙文鍵盤" + "西班牙文 (美國) 鍵盤" + "法文鍵盤" + "法文 (加拿大) 鍵盤" + "法文 (瑞士) 鍵盤" + "義大利文鍵盤" + "挪威文鍵盤" + "荷蘭文鍵盤" + "俄文鍵盤" + "塞爾維亞文鍵盤" + "瑞典文語音" + "南非荷蘭文語音" + "捷克文語音" + "德文語音" + "英文語音" + "西班牙文語音" + "法文語音" + "義大利文語音" + "日文語音" + "韓文語音" + "荷蘭文語音" + "波蘭文語音" + "葡萄牙文語音" + "俄文語音" + "土耳其文語音" + "中文 (粵語) 語音" + "中文 (華語) 語音" + "祖魯文語音" + "使用習慣學習模式" diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 9759e0eb6..f0da2744b 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -140,6 +140,8 @@ + + @@ -166,10 +168,11 @@ + - + @@ -180,6 +183,8 @@ + + diff --git a/java/res/values/config.xml b/java/res/values/config.xml index bf42589ea..bdb4409f0 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -60,6 +60,7 @@ 400 1200 40 + 1100 2.0mm 4 diff --git a/java/res/values/donottranslate-altchars.xml b/java/res/values/donottranslate-altchars.xml index 4b1a6ae6d..518e74af1 100644 --- a/java/res/values/donottranslate-altchars.xml +++ b/java/res/values/donottranslate-altchars.xml @@ -44,6 +44,9 @@ + ¢,£,€,¥,₱ + ¢,£,$,¥,₱ + ¢,$,€,¥,₱ "\@drawable/sym_keyboard_settings|\@integer/key_settings,\@drawable/sym_keyboard_mic|\@integer/key_voice" ":-)|:-) ,:-(|:-( ,;-)|;-) ,:-P|:-P ,=-O|=-O ,:-*|:-* ,:O|:O ,B-)|B-) ,:-$|:-$ ,:-!|:-! ,:-[|:-[ ,O:-)|O:-) ,:-\\\\\\\\|:-\\\\\\\\ ,:\'(|:\'( ,:-D|:-D " "\@drawable/sym_keyboard_settings|\@integer/key_settings,/" diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml index 6c18cb42a..d6f9bfc28 100644 --- a/java/res/values/keycodes.xml +++ b/java/res/values/keycodes.xml @@ -28,4 +28,24 @@ -5 -100 -102 + + + + @integer/key_tab + @string/description_tab_key + @integer/key_return + @string/description_return_key + @integer/key_space + @string/description_space_key + @integer/key_shift + @string/description_shift_key + @integer/key_switch_alpha_symbol + @string/description_switch_alpha_symbol_key + @integer/key_delete + @string/description_delete_key + @integer/key_settings + @string/description_settings_key + @integer/key_voice + @string/description_voice_key + diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 72601c37f..e19203e6c 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -102,6 +102,31 @@ Wait + + Delete + + Return + + Settings + + Shift + + Space + + Symbols + + Tab + + Voice Input + + Symbols on + + Symbols off + + Shift on + + Shift off + Czech Keyboard + + Arabic Keyboard Danish Keyboard @@ -235,6 +262,9 @@ French (Canada) Keyboard French (Switzerland) Keyboard + + + Hebrew Keyboard Italian Keyboard diff --git a/java/res/values/whitelist.xml b/java/res/values/whitelist.xml new file mode 100644 index 000000000..ced52e70e --- /dev/null +++ b/java/res/values/whitelist.xml @@ -0,0 +1,38 @@ + + + + + + + 255 + ill + I\'ll + + 255 + thisd + this\'d + + + diff --git a/java/res/xml-ar/kbd_qwerty.xml b/java/res/xml-ar/kbd_qwerty.xml new file mode 100644 index 000000000..4c6a4f50a --- /dev/null +++ b/java/res/xml-ar/kbd_qwerty.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/java/res/xml-iw/kbd_qwerty.xml b/java/res/xml-iw/kbd_qwerty.xml index 98bfd7e0b..72826d461 100644 --- a/java/res/xml-iw/kbd_qwerty.xml +++ b/java/res/xml-iw/kbd_qwerty.xml @@ -1,19 +1,19 @@ @@ -30,83 +30,5 @@ latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + latin:keyboardLayout="@xml/kbd_iw_rows" /> diff --git a/java/res/xml-xlarge/kbd_ar_rows.xml b/java/res/xml-xlarge/kbd_ar_rows.xml new file mode 100644 index 000000000..e84aae6b5 --- /dev/null +++ b/java/res/xml-xlarge/kbd_ar_rows.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_iw_rows.xml b/java/res/xml-xlarge/kbd_iw_rows.xml new file mode 100644 index 000000000..a3a239dbf --- /dev/null +++ b/java/res/xml-xlarge/kbd_iw_rows.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_key_styles.xml b/java/res/xml-xlarge/kbd_key_styles.xml index d211e5e61..fc06d00fc 100644 --- a/java/res/xml-xlarge/kbd_key_styles.xml +++ b/java/res/xml-xlarge/kbd_key_styles.xml @@ -165,4 +165,19 @@ latin:keyOutputText="@string/keylabel_for_popular_domain" latin:keyHintIcon="@drawable/hint_popup_holo" latin:popupCharacters="@string/alternates_for_popular_domain" /> + + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_number.xml b/java/res/xml-xlarge/kbd_number.xml index 875548ba7..012b75115 100644 --- a/java/res/xml-xlarge/kbd_number.xml +++ b/java/res/xml-xlarge/kbd_number.xml @@ -31,120 +31,199 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_phone.xml b/java/res/xml-xlarge/kbd_phone.xml index b9444ad50..9122176a9 100644 --- a/java/res/xml-xlarge/kbd_phone.xml +++ b/java/res/xml-xlarge/kbd_phone.xml @@ -129,9 +129,17 @@ the touch event on the area, "space" is intentionally not marked as a left edge key. --> - + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_phone_symbols.xml b/java/res/xml-xlarge/kbd_phone_symbols.xml index 690bcde0c..055c14867 100644 --- a/java/res/xml-xlarge/kbd_phone_symbols.xml +++ b/java/res/xml-xlarge/kbd_phone_symbols.xml @@ -141,9 +141,17 @@ the touch event on the area, "space" is intentionally not marked as a left edge key. --> - + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_qwerty_row4.xml b/java/res/xml-xlarge/kbd_qwerty_row4.xml index 9d0fd81c7..f36b61fc7 100644 --- a/java/res/xml-xlarge/kbd_qwerty_row4.xml +++ b/java/res/xml-xlarge/kbd_qwerty_row4.xml @@ -27,29 +27,36 @@ > - - + - - - - - + latin:keyStyle="settingsKeyStyle" /> + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/java/res/xml-xlarge/kbd_ru_rows.xml b/java/res/xml-xlarge/kbd_ru_rows.xml index 008988a84..c5cd04371 100644 --- a/java/res/xml-xlarge/kbd_ru_rows.xml +++ b/java/res/xml-xlarge/kbd_ru_rows.xml @@ -105,11 +105,11 @@ latin:keyEdgeFlags="right" /> @@ -130,9 +130,15 @@ latin:keyLabel="б" /> + + + latin:keyStyle="currencyKeyStyle" /> @@ -125,20 +126,53 @@ - + + + + + + + + + + + - - - - + + + + + + + + + + + + + + - + + + + + + + + - - + + + + + + + + + + - + + + + + + + + diff --git a/java/res/xml/kbd_currency_key_styles.xml b/java/res/xml/kbd_currency_key_styles.xml new file mode 100644 index 000000000..b30dd6451 --- /dev/null +++ b/java/res/xml/kbd_currency_key_styles.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/res/xml/kbd_iw_rows.xml b/java/res/xml/kbd_iw_rows.xml new file mode 100644 index 000000000..db49ad76f --- /dev/null +++ b/java/res/xml/kbd_iw_rows.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml index 3b35f3560..473510ec4 100644 --- a/java/res/xml/kbd_key_styles.xml +++ b/java/res/xml/kbd_key_styles.xml @@ -182,7 +182,7 @@ + + @@ -71,8 +73,7 @@ + latin:keyStyle="currencyKeyStyle" /> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 3167ae8a8..a9883b118 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -20,7 +20,13 @@ + + + + + + android:isDefault="@bool/im_is_default"> + diff --git a/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java new file mode 100644 index 000000000..99262c434 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.util.Log; + +public abstract class AbstractCompatWrapper { + private static final String TAG = AbstractCompatWrapper.class.getSimpleName(); + protected final Object mObj; + + public AbstractCompatWrapper(Object obj) { + if (obj == null) { + Log.e(TAG, "Invalid input to AbstructCompatWrapper"); + } + mObj = obj; + } + + public Object getOriginalObject() { + return mObj; + } +} diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java new file mode 100644 index 000000000..a8086919c --- /dev/null +++ b/java/src/com/android/inputmethod/compat/CompatUtils.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.content.Intent; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class CompatUtils { + private static final String TAG = CompatUtils.class.getSimpleName(); + private static final String EXTRA_INPUT_METHOD_ID = "input_method_id"; + // TODO: Can these be constants instead of literal String constants? + private static final String INPUT_METHOD_SUBTYPE_SETTINGS = + "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS"; + private static final String INPUT_LANGUAGE_SELECTION = + "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"; + + public static Intent getInputLanguageSelectionIntent(String inputMethodId, + int flagsForSubtypeSettings) { + final String action; + Intent intent; + if (android.os.Build.VERSION.SDK_INT + >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) { + // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS + action = INPUT_METHOD_SUBTYPE_SETTINGS; + intent = new Intent(action); + if (!TextUtils.isEmpty(inputMethodId)) { + intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId); + } + if (flagsForSubtypeSettings > 0) { + intent.setFlags(flagsForSubtypeSettings); + } + } else { + action = INPUT_LANGUAGE_SELECTION; + intent = new Intent(action); + } + return intent; + } + + public static Class getClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; + } + } + + public static Method getMethod(Class targetClass, String name, + Class... parameterTypes) { + try { + return targetClass.getMethod(name, parameterTypes); + } catch (SecurityException e) { + // ignore + return null; + } catch (NoSuchMethodException e) { + // ignore + return null; + } + } + + public static Field getField(Class targetClass, String name) { + try { + return targetClass.getField(name); + } catch (SecurityException e) { + // ignore + return null; + } catch (NoSuchFieldException e) { + // ignore + return null; + } + } + + public static Constructor getConstructor(Class targetClass, Class[] types) { + if (targetClass == null || types == null) return null; + try { + return targetClass.getConstructor(types); + } catch (SecurityException e) { + // ignore + return null; + } catch (NoSuchMethodException e) { + // ignore + return null; + } + } + + public static Object invoke( + Object receiver, Object defaultValue, Method method, Object... args) { + if (receiver == null || method == null) return defaultValue; + try { + return method.invoke(receiver, args); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Exception in invoke: IllegalArgumentException"); + return defaultValue; + } catch (IllegalAccessException e) { + Log.e(TAG, "Exception in invoke: IllegalAccessException"); + return defaultValue; + } catch (InvocationTargetException e) { + Log.e(TAG, "Exception in invoke: IllegalTargetException"); + return defaultValue; + } + } + + public static Object getFieldValue(Object receiver, Object defaultValue, Field field) { + if (receiver == null || field == null) return defaultValue; + try { + return field.get(receiver); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Exception in getFieldValue: IllegalArgumentException"); + return defaultValue; + } catch (IllegalAccessException e) { + Log.e(TAG, "Exception in getFieldValue: IllegalAccessException"); + return defaultValue; + } + } + + public static void setFieldValue(Object receiver, Field field, Object value) { + if (receiver == null || field == null) return; + try { + field.set(receiver, value); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Exception in setFieldValue: IllegalArgumentException"); + } catch (IllegalAccessException e) { + Log.e(TAG, "Exception in setFieldValue: IllegalAccessException"); + } + } + + public static List copyInputMethodSubtypeListToWrapper( + Object listObject) { + if (!(listObject instanceof List)) return null; + final List subtypes = + new ArrayList(); + for (Object o: (List)listObject) { + subtypes.add(new InputMethodSubtypeCompatWrapper(o)); + } + return subtypes; + } +} diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java new file mode 100644 index 000000000..f6f4f7a59 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +import java.lang.reflect.Field; + +public class EditorInfoCompatUtils { + private static final Field FIELD_IME_FLAG_NAVIGATE_NEXT = CompatUtils.getField( + EditorInfo.class, "IME_FLAG_NAVIGATE_NEXT"); + private static final Field FIELD_IME_FLAG_NAVIGATE_PREVIOUS = CompatUtils.getField( + EditorInfo.class, "IME_FLAG_NAVIGATE_PREVIOUS"); + private static final Field FIELD_IME_ACTION_PREVIOUS = CompatUtils.getField( + EditorInfo.class, "IME_FLAG_ACTION_PREVIOUS"); + private static final Integer OBJ_IME_FLAG_NAVIGATE_NEXT = (Integer) CompatUtils + .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_NEXT); + private static final Integer OBJ_IME_FLAG_NAVIGATE_PREVIOUS = (Integer) CompatUtils + .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_PREVIOUS); + private static final Integer OBJ_IME_ACTION_PREVIOUS = (Integer) CompatUtils + .getFieldValue(null, null, FIELD_IME_ACTION_PREVIOUS); + + public static boolean hasFlagNavigateNext(int imeOptions) { + if (OBJ_IME_FLAG_NAVIGATE_NEXT == null) + return false; + return (imeOptions & OBJ_IME_FLAG_NAVIGATE_NEXT) != 0; + } + + public static boolean hasFlagNavigatePrevious(int imeOptions) { + if (OBJ_IME_FLAG_NAVIGATE_PREVIOUS == null) + return false; + return (imeOptions & OBJ_IME_FLAG_NAVIGATE_PREVIOUS) != 0; + } + + public static void performEditorActionNext(InputConnection ic) { + ic.performEditorAction(EditorInfo.IME_ACTION_NEXT); + } + + public static void performEditorActionPrevious(InputConnection ic) { + if (OBJ_IME_ACTION_PREVIOUS == null) + return; + ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS); + } + + public static String imeOptionsName(int imeOptions) { + if (imeOptions == -1) + return null; + final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION; + final String action; + switch (actionId) { + case EditorInfo.IME_ACTION_UNSPECIFIED: + action = "actionUnspecified"; + break; + case EditorInfo.IME_ACTION_NONE: + action = "actionNone"; + break; + case EditorInfo.IME_ACTION_GO: + action = "actionGo"; + break; + case EditorInfo.IME_ACTION_SEARCH: + action = "actionSearch"; + break; + case EditorInfo.IME_ACTION_SEND: + action = "actionSend"; + break; + case EditorInfo.IME_ACTION_DONE: + action = "actionDone"; + break; + default: { + if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) { + action = "actionPrevious"; + } else { + action = "actionUnknown(" + actionId + ")"; + } + break; + } + } + if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { + return "flagNoEnterAction|" + action; + } else { + return action; + } + } +} diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java new file mode 100644 index 000000000..b4fe32765 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.util.Log; +import android.view.inputmethod.InputConnection; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class InputConnectionCompatUtils { + private static final String TAG = InputConnectionCompatUtils.class.getSimpleName(); + private static final Class CLASS_CorrectionInfo = CompatUtils + .getClass("android.view.inputmethod.CorrectionInfo"); + private static final Class[] INPUT_TYPE_CorrectionInfo = new Class[] { int.class, + CharSequence.class, CharSequence.class }; + private static final Constructor CONSTRUCTOR_CorrectionInfo = CompatUtils + .getConstructor(CLASS_CorrectionInfo, INPUT_TYPE_CorrectionInfo); + private static final Method METHOD_InputConnection_commitCorrection = CompatUtils + .getMethod(InputConnection.class, "commitCorrection", CLASS_CorrectionInfo); + + public static void commitCorrection(InputConnection ic, int offset, CharSequence oldText, + CharSequence newText) { + if (ic == null || CONSTRUCTOR_CorrectionInfo == null + || METHOD_InputConnection_commitCorrection == null) { + return; + } + Object[] args = { offset, oldText, newText }; + try { + Object correctionInfo = CONSTRUCTOR_CorrectionInfo.newInstance(args); + CompatUtils.invoke(ic, null, METHOD_InputConnection_commitCorrection, + correctionInfo); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error in commitCorrection: IllegalArgumentException"); + } catch (InstantiationException e) { + Log.e(TAG, "Error in commitCorrection: InstantiationException"); + } catch (IllegalAccessException e) { + Log.e(TAG, "Error in commitCorrection: IllegalAccessException"); + } catch (InvocationTargetException e) { + Log.e(TAG, "Error in commitCorrection: InvocationTargetException"); + } + } +} diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java new file mode 100644 index 000000000..8e22bbc79 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.content.pm.ServiceInfo; +import android.view.inputmethod.InputMethodInfo; + +import java.lang.reflect.Method; + +public class InputMethodInfoCompatWrapper { + private final InputMethodInfo mImi; + private static final Method METHOD_getSubtypeAt = CompatUtils.getMethod( + InputMethodInfo.class, "getSubtypeAt", int.class); + private static final Method METHOD_getSubtypeCount = CompatUtils.getMethod( + InputMethodInfo.class, "getSubtypeCount"); + + public InputMethodInfoCompatWrapper(InputMethodInfo imi) { + mImi = imi; + } + + public InputMethodInfo getInputMethodInfo() { + return mImi; + } + + public String getId() { + return mImi.getId(); + } + + public String getPackageName() { + return mImi.getPackageName(); + } + + public ServiceInfo getServiceInfo() { + return mImi.getServiceInfo(); + } + + public int getSubtypeCount() { + return (Integer) CompatUtils.invoke(mImi, 0, METHOD_getSubtypeCount); + } + + public InputMethodSubtypeCompatWrapper getSubtypeAt(int index) { + return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null, + METHOD_getSubtypeAt, index)); + } +} diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java new file mode 100644 index 000000000..e0d54da3b --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.content.Context; +import android.os.IBinder; +import android.util.Log; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// TODO: Override this class with the concrete implementation if we need to take care of the +// performance. +public class InputMethodManagerCompatWrapper { + private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName(); + private static final Method METHOD_getCurrentInputMethodSubtype = + CompatUtils.getMethod(InputMethodManager.class, "getCurrentInputMethodSubtype"); + private static final Method METHOD_getEnabledInputMethodSubtypeList = + CompatUtils.getMethod(InputMethodManager.class, "getEnabledInputMethodSubtypeList", + InputMethodInfo.class, boolean.class); + private static final Method METHOD_getShortcutInputMethodsAndSubtypes = + CompatUtils.getMethod(InputMethodManager.class, "getShortcutInputMethodsAndSubtypes"); + private static final Method METHOD_setInputMethodAndSubtype = + CompatUtils.getMethod( + InputMethodManager.class, "setInputMethodAndSubtype", IBinder.class, + String.class, InputMethodSubtypeCompatWrapper.CLASS_InputMethodSubtype); + + private static final InputMethodManagerCompatWrapper sInstance = + new InputMethodManagerCompatWrapper(); + + private InputMethodManager mImm; + private InputMethodManagerCompatWrapper() { + } + + public static InputMethodManagerCompatWrapper getInstance(Context context) { + if (sInstance.mImm == null) { + sInstance.init(context); + } + return sInstance; + } + + private synchronized void init(Context context) { + mImm = (InputMethodManager) context.getSystemService( + Context.INPUT_METHOD_SERVICE); + } + + public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() { + return new InputMethodSubtypeCompatWrapper( + CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype)); + } + + public List getEnabledInputMethodSubtypeList( + InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) { + Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList, + (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes); + return CompatUtils.copyInputMethodSubtypeListToWrapper((List)retval); + } + + public Map> + getShortcutInputMethodsAndSubtypes() { + Object retval = CompatUtils.invoke(mImm, null, METHOD_getShortcutInputMethodsAndSubtypes); + if (!(retval instanceof Map)) return null; + Map> shortcutMap = + new HashMap>(); + final Map retvalMap = (Map)retval; + for (Object key : retvalMap.keySet()) { + if (!(key instanceof InputMethodInfo)) { + Log.e(TAG, "Class type error."); + return null; + } + shortcutMap.put(new InputMethodInfoCompatWrapper((InputMethodInfo)key), + CompatUtils.copyInputMethodSubtypeListToWrapper(retvalMap.get(key))); + } + return shortcutMap; + } + + public void setInputMethodAndSubtype( + IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) { + CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype, + token, id, subtype.getOriginalObject()); + } + + public boolean switchToLastInputMethod(IBinder token) { + return false; + } + + public List getEnabledInputMethodList() { + if (mImm == null) return null; + List imis = new ArrayList(); + for (InputMethodInfo imi : mImm.getEnabledInputMethodList()) { + imis.add(new InputMethodInfoCompatWrapper(imi)); + } + return imis; + } + + public void showInputMethodPicker() { + if (mImm == null) return; + mImm.showInputMethodPicker(); + } +} diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java new file mode 100644 index 000000000..88167ae74 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import com.android.inputmethod.latin.SubtypeSwitcher; + +import android.inputmethodservice.InputMethodService; +import android.view.View; +// import android.view.inputmethod.InputMethodSubtype; +import android.widget.HorizontalScrollView; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class InputMethodServiceCompatWrapper extends InputMethodService { + private static final Method METHOD_HorizontalScrollView_setOverScrollMode = + CompatUtils.getMethod(HorizontalScrollView.class, "setOverScrollMode", int.class); + private static final Field FIELD_View_OVER_SCROLL_NEVER = + CompatUtils.getField(View.class, "OVER_SCROLL_NEVER"); + private static final Integer View_OVER_SCROLL_NEVER = + (Integer)CompatUtils.getFieldValue(null, null, FIELD_View_OVER_SCROLL_NEVER); + // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10 + // or previous. Note that InputMethodSubtype was added in the API level 11. + // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged(). + // For the API level 10 or previous, we handle the "subtype changed" events by ourselves + // without having support from framework -- onCurrentInputMethodSubtypeChanged(). + private static final boolean CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED = false; + + private InputMethodManagerCompatWrapper mImm; + + @Override + public void onCreate() { + super.onCreate(); + mImm = InputMethodManagerCompatWrapper.getInstance(this); + } + + // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should + // handle the event the current subtype was changed. LatinIME calls + // notifyOnCurrentInputMethodSubtypeChanged every time LatinIME + // changes the current subtype. + // This call is required to let LatinIME itself know a subtype changed + // event when the API level is 10 or previous. + @SuppressWarnings("unused") + public void notifyOnCurrentInputMethodSubtypeChanged(InputMethodSubtypeCompatWrapper subtype) { + // Do nothing when the API level is 11 or later + if (CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return; + if (subtype == null) { + subtype = mImm.getCurrentInputMethodSubtype(); + } + if (subtype != null) { + SubtypeSwitcher.getInstance().updateSubtype(subtype); + } + } + + protected static void setOverScrollModeNever(HorizontalScrollView scrollView) { + if (View_OVER_SCROLL_NEVER != null) { + CompatUtils.invoke(scrollView, null, METHOD_HorizontalScrollView_setOverScrollMode, + View_OVER_SCROLL_NEVER); + } + } + + ////////////////////////////////////// + // Functions using API v11 or later // + ////////////////////////////////////// + /*@Override + public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) { + // Do nothing when the API level is 10 or previous + if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return; + SubtypeSwitcher.getInstance().updateSubtype( + new InputMethodSubtypeCompatWrapper(subtype)); + }*/ + + protected static void setTouchableRegionCompat(InputMethodService.Insets outInsets, + int x, int y, int width, int height) { + //outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; + //outInsets.touchableRegion.set(x, y, width, height); + } +} diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtype.java b/java/src/com/android/inputmethod/compat/InputMethodSubtype.java deleted file mode 100644 index 6630dbe75..000000000 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtype.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Note: This class has been copied from Honeycomb framework. -// Original class in Honeycomb framework is {@link android.view.inputmethod.InputMethodSubtype}. - -package com.android.inputmethod.compat; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * Information given to an {@link InputMethod} about a client connecting - * to it. - */ -/** - * InputMethodSubtype is a subtype contained in the input method. Subtype can describe - * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for - * IME switch. The subtype allows the system to call the specified subtype of IME directly. - */ -public final class InputMethodSubtype implements Parcelable { - private final int mSubtypeNameResId; - private final int mSubtypeIconResId; - private final String mSubtypeLocale; - private final String mSubtypeMode; - private final String mSubtypeExtraValue; - private final int mSubtypeHashCode; - - /** - * Constructor - * @param nameId The name of the subtype - * @param iconId The icon of the subtype - * @param locale The locale supported by the subtype - * @param modeId The mode supported by the subtype - * @param extraValue The extra value of the subtype - */ - InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) { - mSubtypeNameResId = nameId; - mSubtypeIconResId = iconId; - mSubtypeLocale = locale != null ? locale : ""; - mSubtypeMode = mode != null ? mode : ""; - mSubtypeExtraValue = extraValue != null ? extraValue : ""; - mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale, - mSubtypeMode, mSubtypeExtraValue); - } - - InputMethodSubtype(Parcel source) { - String s; - mSubtypeNameResId = source.readInt(); - mSubtypeIconResId = source.readInt(); - s = source.readString(); - mSubtypeLocale = s != null ? s : ""; - s = source.readString(); - mSubtypeMode = s != null ? s : ""; - s = source.readString(); - mSubtypeExtraValue = s != null ? s : ""; - mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale, - mSubtypeMode, mSubtypeExtraValue); - } - - /** - * @return the name of the subtype - */ - public int getNameResId() { - return mSubtypeNameResId; - } - - /** - * @return the icon of the subtype - */ - public int getIconResId() { - return mSubtypeIconResId; - } - - /** - * @return the locale of the subtype - */ - public String getLocale() { - return mSubtypeLocale; - } - - /** - * @return the mode of the subtype - */ - public String getMode() { - return mSubtypeMode; - } - - /** - * @return the extra value of the subtype - */ - public String getExtraValue() { - return mSubtypeExtraValue; - } - - @Override - public int hashCode() { - return mSubtypeHashCode; - } - - @Override - public boolean equals(Object o) { - if (o instanceof InputMethodSubtype) { - InputMethodSubtype subtype = (InputMethodSubtype) o; - return (subtype.hashCode() == hashCode()) - && (subtype.getNameResId() == getNameResId()) - && (subtype.getMode().equals(getMode())) - && (subtype.getIconResId() == getIconResId()) - && (subtype.getLocale().equals(getLocale())) - && (subtype.getExtraValue().equals(getExtraValue())); - } - return false; - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeInt(mSubtypeNameResId); - dest.writeInt(mSubtypeIconResId); - dest.writeString(mSubtypeLocale); - dest.writeString(mSubtypeMode); - dest.writeString(mSubtypeExtraValue); - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public InputMethodSubtype createFromParcel(Parcel source) { - return new InputMethodSubtype(source); - } - - public InputMethodSubtype[] newArray(int size) { - return new InputMethodSubtype[size]; - } - }; - - private static int hashCodeInternal(int nameResId, int iconResId, String locale, - String mode, String extraValue) { - return Arrays.hashCode(new Object[] {nameResId, iconResId, locale, mode, extraValue}); - } -} diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java new file mode 100644 index 000000000..90b7df949 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import com.android.inputmethod.latin.LatinImeLogger; + +import android.util.Log; + +import java.lang.reflect.Method; + +// TODO: Override this class with the concrete implementation if we need to take care of the +// performance. +public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper { + private static final boolean DBG = LatinImeLogger.sDBG; + private static final String TAG = InputMethodSubtypeCompatWrapper.class.getSimpleName(); + private static final String DEFAULT_LOCALE = "en_US"; + private static final String DEFAULT_MODE = "keyboard"; + + public static final Class CLASS_InputMethodSubtype = + CompatUtils.getClass("android.view.inputmethod.InputMethodSubtype"); + private static final Method METHOD_getNameResId = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getNameResId"); + private static final Method METHOD_getIconResId = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getIconResId"); + private static final Method METHOD_getLocale = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getLocale"); + private static final Method METHOD_getMode = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getMode"); + private static final Method METHOD_getExtraValue = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValue"); + private static final Method METHOD_containsExtraValueKey = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "containsExtraValueKey", String.class); + private static final Method METHOD_getExtraValueOf = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class); + + public InputMethodSubtypeCompatWrapper(Object subtype) { + super(CLASS_InputMethodSubtype.isInstance(subtype) ? subtype : null); + if (DBG) { + Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper"); + } + } + + public int getNameResId() { + return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getNameResId); + } + + public int getIconResId() { + return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getIconResId); + } + + public String getLocale() { + final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale); + if (s == null) return DEFAULT_LOCALE; + return s; + } + + public String getMode() { + String s = (String)CompatUtils.invoke(mObj, null, METHOD_getMode); + if (s == null) return DEFAULT_MODE; + return s; + } + + public String getExtraValue() { + return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValue); + } + + public boolean containsExtraValueKey(String key) { + return (Boolean)CompatUtils.invoke(mObj, false, METHOD_containsExtraValueKey, key); + } + + public String getExtraValueOf(String key) { + return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValueOf, key); + } + + @Override + public boolean equals(Object o) { + if (o instanceof InputMethodSubtypeCompatWrapper) { + InputMethodSubtypeCompatWrapper subtype = (InputMethodSubtypeCompatWrapper)o; + return mObj.equals(subtype.getOriginalObject()); + } else { + return mObj.equals(o); + } + } + +} diff --git a/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java new file mode 100644 index 000000000..d85174188 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.compat; + +import android.text.InputType; + +import java.lang.reflect.Field; + +public class InputTypeCompatUtils { + private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = + CompatUtils.getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"); + private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD = CompatUtils + .getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_PASSWORD"); + private static final Field FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD = CompatUtils + .getField(InputType.class, "TYPE_NUMBER_VARIATION_PASSWORD"); + private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = + (Integer) CompatUtils.getFieldValue(null, null, + FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS); + private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD = + (Integer) CompatUtils.getFieldValue(null, null, + FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD); + private static final Integer OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD = + (Integer) CompatUtils.getFieldValue(null, null, + FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD); + private static final int WEB_TEXT_PASSWORD_INPUT_TYPE; + private static final int NUMBER_PASSWORD_INPUT_TYPE; + private static final int TEXT_PASSWORD_INPUT_TYPE = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD; + private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + + static { + WEB_TEXT_PASSWORD_INPUT_TYPE = + OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD != null + ? InputType.TYPE_CLASS_TEXT | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD + : 0; + NUMBER_PASSWORD_INPUT_TYPE = + OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD != null + ? InputType.TYPE_CLASS_NUMBER | OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD + : 0; + } + + private static boolean isWebPasswordInputType(int inputType) { + return WEB_TEXT_PASSWORD_INPUT_TYPE != 0 + && inputType == WEB_TEXT_PASSWORD_INPUT_TYPE; + } + + private static boolean isNumberPasswordInputType(int inputType) { + return NUMBER_PASSWORD_INPUT_TYPE != 0 + && inputType == NUMBER_PASSWORD_INPUT_TYPE; + } + + private static boolean isTextPasswordInputType(int inputType) { + return inputType == TEXT_PASSWORD_INPUT_TYPE; + } + + private static boolean isWebEmailAddressVariation(int variation) { + return OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null + && variation == OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; + } + + public static boolean isEmailVariation(int variation) { + return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + || isWebEmailAddressVariation(variation); + } + + // Please refer to TextView.isPasswordInputType + public static boolean isPasswordInputType(int inputType) { + final int maskedInputType = + inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION); + return isTextPasswordInputType(maskedInputType) || isWebPasswordInputType(maskedInputType) + || isNumberPasswordInputType(maskedInputType); + } + + // Please refer to TextView.isVisiblePasswordInputType + public static boolean isVisiblePasswordInputType(int inputType) { + final int maskedInputType = + inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION); + return maskedInputType == TEXT_VISIBLE_PASSWORD_INPUT_TYPE; + } +} diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java new file mode 100644 index 000000000..8e2a2e0b8 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.content.Context; +import android.os.Vibrator; + +import java.lang.reflect.Method; + +public class VibratorCompatWrapper { + private static final Method METHOD_hasVibrator = CompatUtils.getMethod(Vibrator.class, + "hasVibrator", int.class); + + private static final VibratorCompatWrapper sInstance = new VibratorCompatWrapper(); + private Vibrator mVibrator; + + private VibratorCompatWrapper() { + } + + public static VibratorCompatWrapper getInstance(Context context) { + if (sInstance.mVibrator == null) { + sInstance.mVibrator = + (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + } + return sInstance; + } + + public boolean hasVibrator() { + if (mVibrator == null) + return false; + return (Boolean) CompatUtils.invoke(mVibrator, true, METHOD_hasVibrator); + } +} diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java similarity index 83% rename from java/src/com/android/inputmethod/voice/VoiceIMEConnector.java rename to java/src/com/android/inputmethod/deprecated/VoiceProxy.java index 267bef21d..5fba29d90 100644 --- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java +++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java @@ -14,8 +14,14 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.deprecated.voice.FieldContext; +import com.android.inputmethod.deprecated.voice.Hints; +import com.android.inputmethod.deprecated.voice.SettingsUtil; +import com.android.inputmethod.deprecated.voice.VoiceInput; +import com.android.inputmethod.deprecated.voice.VoiceInputLogger; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.EditingUtils; import com.android.inputmethod.latin.LatinIME; @@ -25,8 +31,10 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SharedPreferencesCompat; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.Utils; import android.app.AlertDialog; +import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -53,7 +61,6 @@ import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import java.util.ArrayList; @@ -61,8 +68,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class VoiceIMEConnector implements VoiceInput.UiListener { - private static final VoiceIMEConnector sInstance = new VoiceIMEConnector(); +public class VoiceProxy implements VoiceInput.UiListener { + private static final VoiceProxy sInstance = new VoiceProxy(); public static final boolean VOICE_INSTALLED = true; private static final boolean ENABLE_VOICE_BUTTON = true; @@ -74,13 +81,9 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // For example, the user has a Chinese UI but activates voice input. private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE = "has_used_voice_input_unsupported_locale"; - // The private IME option used to indicate that no microphone should be shown for a - // given text field. For instance this is specified by the search dialog when the - // dialog is already showing a voice search button. - private static final String IME_OPTION_NO_MICROPHONE = "nm"; private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6; - private static final String TAG = VoiceIMEConnector.class.getSimpleName(); + private static final String TAG = VoiceProxy.class.getSimpleName(); private static final boolean DEBUG = LatinImeLogger.sDBG; private boolean mAfterVoiceInput; @@ -96,7 +99,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { private boolean mVoiceButtonOnPrimary; private boolean mVoiceInputHighlighted; - private InputMethodManager mImm; + private InputMethodManagerCompatWrapper mImm; private LatinIME mService; private AlertDialog mVoiceWarningDialog; private VoiceInput mVoiceInput; @@ -108,19 +111,19 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { private final Map> mWordToSuggestions = new HashMap>(); - public static VoiceIMEConnector init(LatinIME context, SharedPreferences prefs, UIHandler h) { + public static VoiceProxy init(LatinIME context, SharedPreferences prefs, UIHandler h) { sInstance.initInternal(context, prefs, h); return sInstance; } - public static VoiceIMEConnector getInstance() { + public static VoiceProxy getInstance() { return sInstance; } private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) { mService = service; mHandler = h; - mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE); + mImm = InputMethodManagerCompatWrapper.getInstance(service); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); if (VOICE_INSTALLED) { mVoiceInput = new VoiceInput(service, this); @@ -136,7 +139,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } } - private VoiceIMEConnector() { + private VoiceProxy() { // Intentional empty constructor for singleton. } @@ -554,10 +557,36 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } private void switchToLastInputMethod() { - /* @@@ - IBinder token = mContext.getWindow().getWindow().getAttributes().token; - mImm.switchToLastInputMethod(token); - */ + final IBinder token = mService.getWindow().getWindow().getAttributes().token; + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... params) { + return mImm.switchToLastInputMethod(token); + } + + @Override + protected void onPostExecute(Boolean result) { + // Calls in this method need to be done in the same thread as the thread which + // called switchToLastInputMethod() + if (!result) { + if (DEBUG) { + Log.d(TAG, "Couldn't switch back to last IME."); + } + // Because the current IME and subtype failed to switch to any other IME and + // subtype by switchToLastInputMethod, the current IME and subtype should keep + // being LatinIME and voice subtype in the next time. And for re-showing voice + // mode, the state of voice input should be reset and the voice view should be + // hidden. + mVoiceInput.reset(); + mService.requestHideSelf(0); + } else { + // Notify an event that the current subtype was changed. This event will be + // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented + // when the API level is 10 or previous. + mService.notifyOnCurrentInputMethodSubtypeChanged(null); + } + } + }.execute(); } private void reallyStartListening(boolean swipe) { @@ -611,9 +640,12 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) { - return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext) - && !(attribute != null - && IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions)) + @SuppressWarnings("deprecation") + final boolean noMic = Utils.inPrivateImeOptions(null, + LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, attribute) + || Utils.inPrivateImeOptions(mService.getPackageName(), + LatinIME.IME_OPTION_NO_MICROPHONE, attribute); + return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext) && !noMic && SpeechRecognizer.isRecognitionAvailable(mService); } @@ -659,7 +691,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { public void onAttachedToWindow() { // After onAttachedToWindow, we can show the voice warning dialog. See startListening() // above. - mSubtypeSwitcher.setVoiceInput(mVoiceInput); + VoiceInputWrapper.getInstance().setVoiceInput(mVoiceInput, mSubtypeSwitcher); } public void onConfigurationChanged(Configuration configuration) { @@ -710,4 +742,91 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { List candidates; Map> alternatives; } + + public static class VoiceLoggerWrapper { + private static final VoiceLoggerWrapper sInstance = new VoiceLoggerWrapper(); + private VoiceInputLogger mLogger; + + public static VoiceLoggerWrapper getInstance(Context context) { + if (sInstance.mLogger == null) { + // Not thread safe, but it's ok. + sInstance.mLogger = VoiceInputLogger.getLogger(context); + } + return sInstance; + } + + // private for the singleton + private VoiceLoggerWrapper() { + } + + public void settingsWarningDialogCancel() { + mLogger.settingsWarningDialogCancel(); + } + + public void settingsWarningDialogOk() { + mLogger.settingsWarningDialogOk(); + } + + public void settingsWarningDialogShown() { + mLogger.settingsWarningDialogShown(); + } + + public void settingsWarningDialogDismissed() { + mLogger.settingsWarningDialogDismissed(); + } + + public void voiceInputSettingEnabled(boolean enabled) { + if (enabled) { + mLogger.voiceInputSettingEnabled(); + } else { + mLogger.voiceInputSettingDisabled(); + } + } + } + + public static class VoiceInputWrapper { + private static final VoiceInputWrapper sInstance = new VoiceInputWrapper(); + private VoiceInput mVoiceInput; + public static VoiceInputWrapper getInstance() { + return sInstance; + } + public void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) { + if (mVoiceInput == null && voiceInput != null) { + mVoiceInput = voiceInput; + } + switcher.setVoiceInputWrapper(this); + } + + private VoiceInputWrapper() { + } + + public void cancel() { + if (mVoiceInput != null) mVoiceInput.cancel(); + } + + public void reset() { + if (mVoiceInput != null) mVoiceInput.reset(); + } + } + + // A list of locales which are supported by default for voice input, unless we get a + // different list from Gservices. + private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES = + "en " + + "en_US " + + "en_GB " + + "en_AU " + + "en_CA " + + "en_IE " + + "en_IN " + + "en_NZ " + + "en_SG " + + "en_ZA "; + + public static String getSupportedLocalesString (ContentResolver resolver) { + return SettingsUtil.getSettingsString( + resolver, + SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES, + DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES); + } } diff --git a/java/src/com/android/inputmethod/voice/FieldContext.java b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java similarity index 98% rename from java/src/com/android/inputmethod/voice/FieldContext.java rename to java/src/com/android/inputmethod/deprecated/voice/FieldContext.java index dfdfbaa9f..0ef73d2d7 100644 --- a/java/src/com/android/inputmethod/voice/FieldContext.java +++ b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import android.os.Bundle; import android.util.Log; diff --git a/java/src/com/android/inputmethod/voice/Hints.java b/java/src/com/android/inputmethod/deprecated/voice/Hints.java similarity index 99% rename from java/src/com/android/inputmethod/voice/Hints.java rename to java/src/com/android/inputmethod/deprecated/voice/Hints.java index d11d3b042..52a4f4e58 100644 --- a/java/src/com/android/inputmethod/voice/Hints.java +++ b/java/src/com/android/inputmethod/deprecated/voice/Hints.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SharedPreferencesCompat; diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java similarity index 99% rename from java/src/com/android/inputmethod/voice/RecognitionView.java rename to java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java index 95a79f463..52c73ce90 100644 --- a/java/src/com/android/inputmethod/voice/RecognitionView.java +++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; diff --git a/java/src/com/android/inputmethod/voice/SettingsUtil.java b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java similarity index 98% rename from java/src/com/android/inputmethod/voice/SettingsUtil.java rename to java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java index 4d746e120..7721fe268 100644 --- a/java/src/com/android/inputmethod/voice/SettingsUtil.java +++ b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import android.content.ContentResolver; import android.provider.Settings; diff --git a/java/src/com/android/inputmethod/voice/SoundIndicator.java b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java similarity index 98% rename from java/src/com/android/inputmethod/voice/SoundIndicator.java rename to java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java index 543290b32..8cc79de1e 100644 --- a/java/src/com/android/inputmethod/voice/SoundIndicator.java +++ b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import android.content.Context; import android.graphics.Bitmap; diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java similarity index 99% rename from java/src/com/android/inputmethod/voice/VoiceInput.java rename to java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java index 2df9e8588..7ee0de9c9 100644 --- a/java/src/com/android/inputmethod/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import com.android.inputmethod.latin.EditingUtils; import com.android.inputmethod.latin.LatinImeLogger; diff --git a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java similarity index 98% rename from java/src/com/android/inputmethod/voice/VoiceInputLogger.java rename to java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java index 1cea681f8..3f59ad099 100644 --- a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java +++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import com.android.common.speech.LoggingEvents; import com.android.common.userhappiness.UserHappinessSignals; @@ -212,7 +212,7 @@ public class VoiceInputLogger { setHasLoggingInfo(true); Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED); i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, suggestionLength); - i.putExtra("rlength", replacedPhraseLength); + i.putExtra("length", replacedPhraseLength); i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE, LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION); @@ -257,7 +257,7 @@ public class VoiceInputLogger { // 2. type subject in subject field // 3. speak message in message field // 4. press send - // @@@ UserHappinessSignals.setHasVoiceLoggingInfo(hasLoggingInfo); + // UserHappinessSignals.setHasVoiceLoggingInfo(hasLoggingInfo); } private boolean hasLoggingInfo(){ diff --git a/java/src/com/android/inputmethod/voice/WaveformImage.java b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java similarity index 98% rename from java/src/com/android/inputmethod/voice/WaveformImage.java rename to java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java index 8bac669fc..a3025f252 100644 --- a/java/src/com/android/inputmethod/voice/WaveformImage.java +++ b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import android.graphics.Bitmap; import android.graphics.Canvas; diff --git a/java/src/com/android/inputmethod/voice/Whitelist.java b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java similarity index 97% rename from java/src/com/android/inputmethod/voice/Whitelist.java rename to java/src/com/android/inputmethod/deprecated/voice/Whitelist.java index f4c24de0c..310689cb2 100644 --- a/java/src/com/android/inputmethod/voice/Whitelist.java +++ b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.deprecated.voice; import android.os.Bundle; diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 23886ad97..7396f0518 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -95,7 +95,7 @@ public class Key { public boolean mPressed; /** If this is a sticky key, is it on? */ public boolean mOn; - /** Key is enabled or not. */ + /** Key is enabled and responds on press */ public boolean mEnabled = true; private final static int[] KEY_STATE_NORMAL_ON = { @@ -226,6 +226,7 @@ public class Key { mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); mModifier = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier, false); mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false); + mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0) | row.mRowEdgeFlags; diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 3979fb53e..1a4f90195 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -22,6 +22,7 @@ import java.util.List; public abstract class KeyDetector { public static final int NOT_A_KEY = -1; + public static final int NOT_A_CODE = -1; protected Keyboard mKeyboard; @@ -105,10 +106,10 @@ public abstract class KeyDetector { * * @param x The x-coordinate of a touch point * @param y The y-coordinate of a touch point - * @param allKeys All nearby key indices are returned in this array + * @param allCodes All nearby key code except functional key are returned in this array * @return The nearest key index */ - abstract public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys); + abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes); /** * Compute the most common key width in order to use it as proximity key detection threshold. @@ -116,14 +117,14 @@ public abstract class KeyDetector { * @param keyboard The keyboard to compute the most common key width * @return The most common key width in the keyboard */ - public static int getMostCommonKeyWidth(Keyboard keyboard) { + public static int getMostCommonKeyWidth(final Keyboard keyboard) { if (keyboard == null) return 0; final List keys = keyboard.getKeys(); if (keys == null || keys.size() == 0) return 0; final HashMap histogram = new HashMap(); int maxCount = 0; int mostCommonWidth = 0; - for (Key key : keys) { + for (final Key key : keys) { final Integer width = key.mWidth + key.mGap; Integer count = histogram.get(width); if (count == null) diff --git a/java/src/com/android/inputmethod/keyboard/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/KeyStyles.java index 44ec53181..169f2e6c3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/KeyStyles.java @@ -188,6 +188,7 @@ public class KeyStyles { readBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier); readBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky); readBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable); + readBoolean(keyAttr, R.styleable.Keyboard_Key_enabled); } private void readDrawable(TypedArray a, int index) { diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 3a0bf53ab..06d44680d 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -132,6 +132,7 @@ public class Keyboard { // Variables for pre-computing nearest keys. + // TODO: Change GRID_WIDTH and GRID_HEIGHT to private. public final int GRID_WIDTH; public final int GRID_HEIGHT; private final int GRID_SIZE; @@ -143,6 +144,8 @@ public class Keyboard { /** Number of key widths from current touch point to search for nearest keys. */ private static float SEARCH_DISTANCE = 1.2f; + private final ProximityInfo mProximityInfo; + /** * Creates a keyboard from the given xml key layout file. * @param context the application or service context @@ -171,6 +174,11 @@ public class Keyboard { mDefaultHeight = mDefaultWidth; mId = id; loadKeyboard(context, xmlLayoutResId); + mProximityInfo = new ProximityInfo(GRID_WIDTH, GRID_HEIGHT); + } + + public int getProximityInfo() { + return mProximityInfo.getNativeProximityInfo(this); } public List getKeys() { @@ -345,7 +353,8 @@ public class Keyboard { return mId != null && mId.isNumberKeyboard(); } - private void computeNearestNeighbors() { + // TODO: Move this function to ProximityInfo and make this private. + public void computeNearestNeighbors() { // Round-up so we don't have any pixels outside the grid mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH; mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT; @@ -369,6 +378,7 @@ public class Keyboard { mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell; } } + mProximityInfo.setProximityInfo(mGridNeighbors, getMinWidth(), getHeight(), mKeys); } public boolean isInside(Key key, int x, int y) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 734a55a79..098af214e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -24,16 +24,20 @@ public interface KeyboardActionListener { * * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key, * the value will be zero. + * @param withSliding true if pressing has occurred because the user slid finger from other key + * to this key without releasing the finger. */ - public void onPress(int primaryCode); + public void onPress(int primaryCode, boolean withSliding); /** * Called when the user releases a key. This is sent after the {@link #onCodeInput} is called. * For keys that repeat, this is only called once. * * @param primaryCode the code of the key that was released + * @param withSliding true if releasing has occurred because the user slid finger from the key + * to other key without releasing the finger. */ - public void onRelease(int primaryCode); + public void onRelease(int primaryCode, boolean withSliding); /** * Send a key code to the listener. diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 8c70d8ffe..f68b68f1d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -16,6 +16,8 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.latin.R; import android.view.inputmethod.EditorInfo; @@ -41,29 +43,35 @@ public class KeyboardId { public final int mMode; public final int mXmlId; public final int mColorScheme; + public final boolean mPasswordInput; public final boolean mHasSettingsKey; public final boolean mVoiceKeyEnabled; public final boolean mHasVoiceKey; - public final int mImeOptions; + public final int mImeAction; public final boolean mEnableShiftLock; public final String mXmlName; private final int mHashCode; - public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int mode, - int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey, - int imeOptions, boolean enableShiftLock) { + public KeyboardId(String xmlName, int xmlId, int colorScheme, Locale locale, int orientation, + int mode, EditorInfo attribute, boolean hasSettingsKey, boolean voiceKeyEnabled, + boolean hasVoiceKey, boolean enableShiftLock) { + final int inputType = (attribute != null) ? attribute.inputType : 0; + final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; this.mLocale = locale; this.mOrientation = orientation; this.mMode = mode; this.mXmlId = xmlId; this.mColorScheme = colorScheme; + this.mPasswordInput = InputTypeCompatUtils.isPasswordInputType(inputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(inputType); this.mHasSettingsKey = hasSettingsKey; this.mVoiceKeyEnabled = voiceKeyEnabled; this.mHasVoiceKey = hasVoiceKey; - // We are interested only in IME_MASK_ACTION enum value and IME_FLAG_NO_ENTER_ACTION. - this.mImeOptions = imeOptions - & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); + // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and + // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}. + this.mImeAction = imeOptions & ( + EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); this.mEnableShiftLock = enableShiftLock; this.mXmlName = xmlName; @@ -73,10 +81,11 @@ public class KeyboardId { mode, xmlId, colorScheme, + mPasswordInput, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, - imeOptions, + mImeAction, enableShiftLock, }); } @@ -112,10 +121,11 @@ public class KeyboardId { && other.mMode == this.mMode && other.mXmlId == this.mXmlId && other.mColorScheme == this.mColorScheme + && other.mPasswordInput == this.mPasswordInput && other.mHasSettingsKey == this.mHasSettingsKey && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled && other.mHasVoiceKey == this.mHasVoiceKey - && other.mImeOptions == this.mImeOptions + && other.mImeAction == this.mImeAction && other.mEnableShiftLock == this.mEnableShiftLock; } @@ -126,17 +136,19 @@ public class KeyboardId { @Override public String toString() { - return String.format("[%s.xml %s %s %s imeOptions=%s %s%s%s%s%s]", + return String.format("[%s.xml %s %s %s imeAction=%s %s%s%s%s%s%s]", mXmlName, mLocale, (mOrientation == 1 ? "port" : "land"), modeName(mMode), - imeOptionsName(mImeOptions), - colorSchemeName(mColorScheme), + EditorInfoCompatUtils.imeOptionsName(mImeAction), + (mPasswordInput ? " passwordInput" : ""), (mHasSettingsKey ? " hasSettingsKey" : ""), (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""), (mHasVoiceKey ? " hasVoiceKey" : ""), - (mEnableShiftLock ? " enableShiftLock" : "")); + (mEnableShiftLock ? " enableShiftLock" : ""), + colorSchemeName(mColorScheme) + ); } public static String modeName(int mode) { @@ -159,26 +171,4 @@ public class KeyboardId { } return null; } - - public static String imeOptionsName(int imeOptions) { - if (imeOptions == -1) return null; - final int actionNo = imeOptions & EditorInfo.IME_MASK_ACTION; - final String action; - switch (actionNo) { - case EditorInfo.IME_ACTION_UNSPECIFIED: action = "actionUnspecified"; break; - case EditorInfo.IME_ACTION_NONE: action = "actionNone"; break; - case EditorInfo.IME_ACTION_GO: action = "actionGo"; break; - case EditorInfo.IME_ACTION_SEARCH: action = "actionSearch"; break; - case EditorInfo.IME_ACTION_SEND: action = "actionSend"; break; - case EditorInfo.IME_ACTION_DONE: action = "actionDone"; break; - // @@@ case EditorInfo.IME_ACTION_PREVIOUS: action = "actionPrevious"; break; - default: action = "actionUnknown(" + actionNo + ")"; break; - } - if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { - return "flagNoEnterAction|" + action; - } else { - return action; - } - } } - diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java index e8324e5fd..62e6f302d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.latin.R; import org.xmlpull.v1.XmlPullParser; @@ -103,7 +104,7 @@ import java.util.List; */ public class KeyboardParser { - private static final String TAG = "KeyboardParser"; + private static final String TAG = KeyboardParser.class.getSimpleName(); private static final boolean DEBUG = false; // Keyboard XML Tags @@ -279,8 +280,8 @@ public class KeyboardParser { checkEndTag(TAG_KEY, parser); } else { Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles); - if (DEBUG) Log.d(TAG, String.format("<%s keyLabel=%s code=%d popupCharacters=%s />", - TAG_KEY, key.mLabel, key.mCode, + if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />", + TAG_KEY, (key.mEnabled ? "" : " disabled"), key.mLabel, key.mCode, Arrays.toString(key.mPopupCharacters))); checkEndTag(TAG_KEY, parser); keys.add(key); @@ -419,6 +420,8 @@ public class KeyboardParser { try { final boolean modeMatched = matchInteger(a, R.styleable.Keyboard_Case_mode, id.mMode); + final boolean passwordInputMatched = matchBoolean(a, + R.styleable.Keyboard_Case_passwordInput, id.mPasswordInput); final boolean settingsKeyMatched = matchBoolean(a, R.styleable.Keyboard_Case_hasSettingsKey, id.mHasSettingsKey); final boolean voiceEnabledMatched = matchBoolean(a, @@ -427,24 +430,34 @@ public class KeyboardParser { R.styleable.Keyboard_Case_hasVoiceKey, id.mHasVoiceKey); final boolean colorSchemeMatched = matchInteger(viewAttr, R.styleable.KeyboardView_colorScheme, id.mColorScheme); - // As noted at KeyboardSwitcher.KeyboardId class, we are interested only in - // enum value masked by IME_MASK_ACTION and IME_FLAG_NO_ENTER_ACTION. So matching + // As noted at {@link KeyboardId} class, we are interested only in enum value masked by + // {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and + // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching // this attribute with id.mImeOptions as integer value is enough for our purpose. - final boolean imeOptionsMatched = matchInteger(a, - R.styleable.Keyboard_Case_imeOptions, id.mImeOptions); - final boolean selected = modeMatched && settingsKeyMatched && voiceEnabledMatched - && voiceKeyMatched && colorSchemeMatched && imeOptionsMatched; + final boolean imeActionMatched = matchInteger(a, + R.styleable.Keyboard_Case_imeAction, id.mImeAction); + final boolean languageCodeMatched = matchString(a, + R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); + final boolean countryCodeMatched = matchString(a, + R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); + final boolean selected = modeMatched && passwordInputMatched && settingsKeyMatched + && voiceEnabledMatched && voiceKeyMatched && colorSchemeMatched + && imeActionMatched && languageCodeMatched && countryCodeMatched; - if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s> %s", TAG_CASE, + if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, textAttr(KeyboardId.modeName( a.getInt(R.styleable.Keyboard_Case_mode, -1)), "mode"), textAttr(KeyboardId.colorSchemeName( - a.getInt(R.styleable.KeyboardView_colorScheme, -1)), "colorSchemeName"), + viewAttr.getInt( + R.styleable.KeyboardView_colorScheme, -1)), "colorSchemeName"), + booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"), booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"), booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"), - textAttr(KeyboardId.imeOptionsName( - a.getInt(R.styleable.Keyboard_Case_imeOptions, -1)), "imeOptions"), + textAttr(EditorInfoCompatUtils.imeOptionsName( + a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), + textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"), + textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"), Boolean.toString(selected))); return selected; @@ -466,6 +479,12 @@ public class KeyboardParser { return !a.hasValue(index) || a.getBoolean(index, false) == value; } + private static boolean matchString(TypedArray a, int index, String value) { + // If does not have "index" attribute, that means this is wild-card for the + // attribute. + return !a.hasValue(index) || a.getString(index).equals(value); + } + private boolean parseDefault(XmlResourceParser parser, Row row, List keys) throws XmlPullParserException, IOException { if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 2648ff3d4..cfa3c446e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -28,7 +29,7 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.util.Log; import android.view.InflateException; -import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.EditorInfo; import java.lang.ref.SoftReference; import java.util.HashMap; @@ -66,8 +67,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private final HashMap> mKeyboardCache = new HashMap>(); - private int mMode = KeyboardId.MODE_TEXT; /* default value */ - private int mImeOptions; + private EditorInfo mAttribute; private boolean mIsSymbols; /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of * what user actually typed. */ @@ -83,8 +83,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4; private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; - // Indicates whether or not we have the settings key - private boolean mHasSettingsKey; + // Indicates whether or not we have the settings key in option of settings + private boolean mSettingsKeyEnabledInSettings; private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto; private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = R.string.settings_key_mode_always_show; @@ -122,77 +122,47 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha prefs.registerOnSharedPreferenceChangeListener(sInstance); } - private void makeSymbolsKeyboardIds() { - final Locale locale = mSubtypeSwitcher.getInputLocale(); - final Resources res = mInputMethodService.getResources(); - final int orientation = res.getConfiguration().orientation; - final int mode = mMode; - final int colorScheme = getColorScheme(); - final boolean hasSettingsKey = mHasSettingsKey; - final boolean voiceKeyEnabled = mVoiceKeyEnabled; - final boolean hasVoiceKey = voiceKeyEnabled && !mVoiceButtonOnPrimary; - final int imeOptions = mImeOptions; - // Note: This comment is only applied for phone number keyboard layout. - // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch - // between "phone keyboard" and "phone symbols keyboard". But on xlarge device, - // "@integer/key_shift" key code is used for that purpose in order to properly display - // "more" and "locked more" key labels. To achieve these behavior, we should initialize - // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" - // respectively here for xlarge device's layout switching. - int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols; - mSymbolsId = new KeyboardId( - res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme, - hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); - xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift; - mSymbolsShiftedId = new KeyboardId( - res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme, - hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); - } - - private boolean hasVoiceKey(boolean isSymbols) { - return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary); - } - - public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled, + public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled, boolean voiceButtonOnPrimary) { mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; try { - if (mInputView == null) return; - final Keyboard oldKeyboard = mInputView.getKeyboard(); - loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, false); - final Keyboard newKeyboard = mInputView.getKeyboard(); - if (newKeyboard.isAlphaKeyboard()) { - final boolean localeChanged = (oldKeyboard == null) - || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); - mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); - } + loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, false); } catch (RuntimeException e) { - Log.w(TAG, e); - LatinImeLogger.logOnException(mode + "," + imeOptions, e); + // Get KeyboardId to record which keyboard has been failed to load. + final KeyboardId id = getKeyboardId(attribute, false); + Log.w(TAG, "loading keyboard failed: " + id, e); + LatinImeLogger.logOnException(id.toString(), e); } } - private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled, + private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled, boolean voiceButtonOnPrimary, boolean isSymbols) { if (mInputView == null) return; - mMode = mode; - mImeOptions = imeOptions; + mAttribute = attribute; mVoiceKeyEnabled = voiceButtonEnabled; mVoiceButtonOnPrimary = voiceButtonOnPrimary; mIsSymbols = isSymbols; // Update the settings key state because number of enabled IMEs could have been changed - mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService); - final KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols); + mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService); + final KeyboardId id = getKeyboardId(attribute, isSymbols); final Keyboard oldKeyboard = mInputView.getKeyboard(); if (oldKeyboard != null && oldKeyboard.mId.equals(id)) return; - makeSymbolsKeyboardIds(); + makeSymbolsKeyboardIds(id.mMode, attribute); mCurrentId = id; mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); - mInputView.setKeyboard(getKeyboard(id)); + setKeyboard(getKeyboard(id)); + } + + private void setKeyboard(final Keyboard newKeyboard) { + final Keyboard oldKeyboard = mInputView.getKeyboard(); + mInputView.setKeyboard(newKeyboard); + final boolean localeChanged = (oldKeyboard == null) + || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); + mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); } private LatinKeyboard getKeyboard(KeyboardId id) { @@ -224,11 +194,22 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // displayed on its spacebar, it might have had arbitrary text fade factor. In such case, // we should reset the text fade factor. It is also applicable to shortcut key. keyboard.setSpacebarTextFadeFactor(0.0f, null); - keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutAvailable(), null); + keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null); return keyboard; } - private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) { + private boolean hasVoiceKey(boolean isSymbols) { + return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary); + } + + private boolean hasSettingsKey(EditorInfo attribute) { + return mSettingsKeyEnabledInSettings + && !Utils.inPrivateImeOptions(mInputMethodService.getPackageName(), + LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute); + } + + private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) { + final int mode = Utils.getKeyboardMode(attribute); final boolean hasVoiceKey = hasVoiceKey(isSymbols); final int charColorId = getColorScheme(); final int xmlId; @@ -256,16 +237,40 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha enableShiftLock = true; } } + final boolean hasSettingsKey = hasSettingsKey(attribute); final Resources res = mInputMethodService.getResources(); final int orientation = res.getConfiguration().orientation; final Locale locale = mSubtypeSwitcher.getInputLocale(); return new KeyboardId( - res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, charColorId, - mHasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, imeOptions, enableShiftLock); + res.getResourceEntryName(xmlId), xmlId, charColorId, locale, orientation, mode, + attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, enableShiftLock); + } + + private void makeSymbolsKeyboardIds(final int mode, EditorInfo attribute) { + final Locale locale = mSubtypeSwitcher.getInputLocale(); + final Resources res = mInputMethodService.getResources(); + final int orientation = res.getConfiguration().orientation; + final int colorScheme = getColorScheme(); + final boolean hasVoiceKey = mVoiceKeyEnabled && !mVoiceButtonOnPrimary; + final boolean hasSettingsKey = hasSettingsKey(attribute); + // Note: This comment is only applied for phone number keyboard layout. + // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch + // between "phone keyboard" and "phone symbols keyboard". But on xlarge device, + // "@integer/key_shift" key code is used for that purpose in order to properly display + // "more" and "locked more" key labels. To achieve these behavior, we should initialize + // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" + // respectively here for xlarge device's layout switching. + int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols; + final String xmlName = res.getResourceEntryName(xmlId); + mSymbolsId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode, + attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, true); + xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift; + mSymbolsShiftedId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode, + attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, true); } public int getKeyboardMode() { - return mMode; + return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT; } public boolean isAlphabetMode() { @@ -278,22 +283,19 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha public boolean isKeyboardAvailable() { if (mInputView != null) - return mInputView.getLatinKeyboard() != null; + return mInputView.getKeyboard() != null; return false; } - private LatinKeyboard getLatinKeyboard() { - if (mInputView != null) - return mInputView.getLatinKeyboard(); + public LatinKeyboard getLatinKeyboard() { + if (mInputView != null) { + final Keyboard keyboard = mInputView.getKeyboard(); + if (keyboard instanceof LatinKeyboard) + return (LatinKeyboard)keyboard; + } return null; } - public void setPreferredLetters(int[] frequencies) { - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null) - latinKeyboard.setPreferredLetters(frequencies); - } - public void keyReleased() { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null) @@ -342,7 +344,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // state when shift key is pressed to go to normal mode. // On the other hand, on distinct multi touch panel device, turning off the shift locked // state with shift key pressing is handled by onReleaseShift(). - if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) { + if ((!hasDistinctMultitouch() || isAccessibilityEnabled()) + && !shifted && latinKeyboard.isShiftLocked()) { latinKeyboard.setShiftLocked(false); } if (latinKeyboard.setShifted(shifted)) { @@ -437,14 +440,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha updateShiftState(); } - public void onPressShift() { + public void onPressShift(boolean withSliding) { if (!isKeyboardAvailable()) return; + // If accessibility is enabled, disable momentary shift lock. + if (isAccessibilityEnabled()) + return; ShiftKeyState shiftKeyState = mShiftKeyState; if (DEBUG_STATE) Log.d(TAG, "onPressShift:" + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() - + " shiftKeyState=" + shiftKeyState); + + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding); if (isAlphabetMode()) { if (isShiftLocked()) { // Shift key is pressed while caps lock state, we will treat this state as shifted @@ -472,25 +478,30 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - public void onReleaseShift() { + public void onReleaseShift(boolean withSliding) { if (!isKeyboardAvailable()) return; + // If accessibility is enabled, disable momentary shift lock. + if (isAccessibilityEnabled()) + return; ShiftKeyState shiftKeyState = mShiftKeyState; if (DEBUG_STATE) Log.d(TAG, "onReleaseShift:" + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() - + " shiftKeyState=" + shiftKeyState); + + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding); if (isAlphabetMode()) { if (shiftKeyState.isMomentary()) { // After chording input while normal state. toggleShift(); - } else if (isShiftLocked() && !shiftKeyState.isIgnoring()) { + } else if (isShiftLocked() && !shiftKeyState.isIgnoring() && !withSliding) { // Shift has been pressed without chording while caps lock state. toggleCapsLock(); - } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) { + } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted() + && !withSliding) { // Shift has been pressed without chording while shifted state. toggleShift(); - } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()) { + } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing() + && !withSliding) { // Shift has been pressed without chording while manual temporary upper case // transited from automatic temporary upper case. toggleShift(); @@ -500,6 +511,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void onPressSymbol() { + // If accessibility is enabled, disable momentary symbol lock. + if (isAccessibilityEnabled()) + return; if (DEBUG_STATE) Log.d(TAG, "onPressSymbol:" + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() @@ -510,6 +524,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void onReleaseSymbol() { + // If accessibility is enabled, disable momentary symbol lock. + if (isAccessibilityEnabled()) + return; if (DEBUG_STATE) Log.d(TAG, "onReleaseSymbol:" + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() @@ -522,6 +539,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void onOtherKeyPressed() { + // If accessibility is enabled, disable momentary mode locking. + if (isAccessibilityEnabled()) + return; if (DEBUG_STATE) Log.d(TAG, "onOtherKeyPressed:" + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() @@ -556,7 +576,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // indicator, we need to call enableShiftLock() and setShiftLocked(false). keyboard.setShifted(false); } - mInputView.setKeyboard(keyboard); + setKeyboard(keyboard); } public boolean isInMomentaryAutoModeSwitchState() { @@ -572,8 +592,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } private void toggleKeyboardMode() { - loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary, - !mIsSymbols); + loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols); if (mIsSymbols) { mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN; } else { @@ -581,6 +600,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } + public boolean isAccessibilityEnabled() { + return mInputView != null && mInputView.isAccessibilityEnabled(); + } + public boolean hasDistinctMultitouch() { return mInputView != null && mInputView.hasDistinctMultitouch(); } @@ -696,7 +719,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha createInputViewInternal(layoutId, false); postSetInputView(); } else if (Settings.PREF_SETTINGS_KEY.equals(key)) { - mHasSettingsKey = getSettingsKeyMode(sharedPreferences, mInputMethodService); + mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences, + mInputMethodService); createInputViewInternal(mLayoutId, true); postSetInputView(); } @@ -728,11 +752,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW)) || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO)) && Utils.hasMultipleEnabledIMEsOrSubtypes( - ((InputMethodManager) context.getSystemService( - Context.INPUT_METHOD_SERVICE))))) { + (InputMethodManagerCompatWrapper.getInstance(context))))) { return true; } + return false; } - return false; + // If the show settings key option is disabled, we always try showing the settings key. + return true; } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index d4c5e579b..61af15b1d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.PorterDuff; @@ -36,6 +37,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.os.SystemClock; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -145,6 +147,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private final boolean mHasDistinctMultitouch; private int mOldPointerCount = 1; + // Accessibility + private boolean mIsAccessibilityEnabled; + protected KeyDetector mKeyDetector = new ProximityKeyDetector(); // Swipe gesture detector @@ -522,7 +527,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } /** - * Return whether the device has distinct multi-touch panel. + * Returns whether the device has distinct multi-touch panel. * @return true if the device has distinct multi-touch panel. */ @Override @@ -530,6 +535,28 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { return mHasDistinctMultitouch; } + /** + * Enables or disables accessibility. + * @param accessibilityEnabled whether or not to enable accessibility + */ + public void setAccessibilityEnabled(boolean accessibilityEnabled) { + mIsAccessibilityEnabled = accessibilityEnabled; + + // Propagate this change to all existing pointer trackers. + for (PointerTracker tracker : mPointerTrackers) { + tracker.setAccessibilityEnabled(accessibilityEnabled); + } + } + + /** + * Returns whether the device has accessibility enabled. + * @return true if the device has accessibility enabled. + */ + @Override + public boolean isAccessibilityEnabled() { + return mIsAccessibilityEnabled; + } + /** * Enables or disables the key feedback popup. This is a popup that shows a magnified * version of the depressed key. By default the preview is enabled. @@ -705,8 +732,13 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } else { paint.setColor(mKeyTextColor); } - // Set a drop shadow for the text - paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); + if (key.mEnabled) { + // Set a drop shadow for the text + paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); + } else { + // Make label invisible + paint.setColor(Color.TRANSPARENT); + } canvas.drawText(label, positionX, baseline, paint); // Turn off drop shadow paint.setShadowLayer(0, 0, 0, 0); @@ -756,6 +788,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { canvas.translate(-key.mX - kbdPaddingLeft, -key.mY - kbdPaddingTop); } + // TODO: Move this function to ProximityInfo for getting rid of public declarations for + // GRID_WIDTH and GRID_HEIGHT if (DEBUG_KEYBOARD_GRID) { Paint p = new Paint(); p.setStyle(Paint.Style.STROKE); @@ -1022,7 +1056,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0); } - private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker) { + private void onDoubleTapShiftKey(PointerTracker tracker) { // When shift key is double tapped, the first tap is correctly processed as usual tap. And // the second tap is treated as this double tap event, so that we need not mark tracker // calling setAlreadyProcessed() nor remove the tracker from mPointerQueueueue. @@ -1060,12 +1094,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // Nothing to do. } @Override - public void onPress(int primaryCode) { - mKeyboardActionListener.onPress(primaryCode); + public void onPress(int primaryCode, boolean withSliding) { + mKeyboardActionListener.onPress(primaryCode, withSliding); } @Override - public void onRelease(int primaryCode) { - mKeyboardActionListener.onRelease(primaryCode); + public void onRelease(int primaryCode, boolean withSliding) { + mKeyboardActionListener.onRelease(primaryCode, withSliding); } }); // Override default ProximityKeyDetector. @@ -1204,15 +1238,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // TODO: cleanup this code into a multi-touch to single-touch event converter class? // If the device does not have distinct multi-touch support panel, ignore all multi-touch // events except a transition from/to single-touch. - if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { + if ((!mHasDistinctMultitouch || mIsAccessibilityEnabled) + && pointerCount > 1 && oldPointerCount > 1) { return true; } // Track the last few movements to look for spurious swipes. mSwipeTracker.addMovement(me); - // Gesture detector must be enabled only when mini-keyboard is not on the screen. - if (mMiniKeyboardView == null + // Gesture detector must be enabled only when mini-keyboard is not on the screen and + // accessibility is not enabled. + // TODO: Reconcile gesture detection and accessibility features. + if (mMiniKeyboardView == null && !mIsAccessibilityEnabled && mGestureDetector != null && mGestureDetector.onTouchEvent(me)) { dismissKeyPreview(); mHandler.cancelKeyTimers(); @@ -1257,7 +1294,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // TODO: cleanup this code into a multi-touch to single-touch event converter class? // Translate mutli-touch event to single-touch events on the device that has no distinct // multi-touch panel. - if (!mHasDistinctMultitouch) { + if (!mHasDistinctMultitouch || mIsAccessibilityEnabled) { // Use only main (id=0) pointer tracker. PointerTracker tracker = getPointerTracker(0); if (pointerCount == 1 && oldPointerCount == 2) { diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index ffb8d6410..5820049bb 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -21,6 +21,7 @@ import com.android.inputmethod.latin.SubtypeSwitcher; import android.content.Context; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -31,17 +32,12 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.util.Log; import java.util.List; import java.util.Locale; // TODO: We should remove this class public class LatinKeyboard extends Keyboard { - - private static final boolean DEBUG_PREFERRED_LETTER = false; - private static final String TAG = "LatinKeyboard"; - public static final int OPACITY_FULLY_OPAQUE = 255; private static final int SPACE_LED_LENGTH_PERCENT = 80; @@ -69,15 +65,7 @@ public class LatinKeyboard extends Keyboard { private final Drawable mEnabledShortcutIcon; private final Drawable mDisabledShortcutIcon; - private int[] mPrefLetterFrequencies; - private int mPrefLetter; - private int mPrefLetterX; - private int mPrefLetterY; - private int mPrefDistance; - private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f; - private static final float OVERLAP_PERCENTAGE_LOW_PROB = 0.70f; - private static final float OVERLAP_PERCENTAGE_HIGH_PROB = 0.85f; // Minimum width of space key preview (proportional to keyboard width) private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f; // Height in space key the language name will be drawn. (proportional to space key height) @@ -167,6 +155,8 @@ public class LatinKeyboard extends Keyboard { } private void updateSpacebarForLocale(boolean isAutoCorrection) { + if (mSpaceKey == null) + return; final Resources res = mContext.getResources(); // If application locales are explicitly selected. if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) { @@ -265,7 +255,7 @@ public class LatinKeyboard extends Keyboard { final boolean allowVariableTextSize = true; final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(), mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height, - getTextSizeFromTheme(textStyle, defaultTextSize), + getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize), allowVariableTextSize); // Draw language text with shadow @@ -334,18 +324,9 @@ public class LatinKeyboard extends Keyboard { return mSpaceDragLastDiff > 0 ? 1 : -1; } - public void setPreferredLetters(int[] frequencies) { - mPrefLetterFrequencies = frequencies; - mPrefLetter = 0; - } - public void keyReleased() { mCurrentlyInSpace = false; mSpaceDragLastDiff = 0; - mPrefLetter = 0; - mPrefLetterX = 0; - mPrefLetterY = 0; - mPrefDistance = Integer.MAX_VALUE; if (mSpaceKey != null) { updateLocaleDrag(Integer.MAX_VALUE); } @@ -381,80 +362,6 @@ public class LatinKeyboard extends Keyboard { return isOnSpace; } } - } else if (mPrefLetterFrequencies != null) { - // New coordinate? Reset - if (mPrefLetterX != x || mPrefLetterY != y) { - mPrefLetter = 0; - mPrefDistance = Integer.MAX_VALUE; - } - // Handle preferred next letter - final int[] pref = mPrefLetterFrequencies; - if (mPrefLetter > 0) { - if (DEBUG_PREFERRED_LETTER) { - if (mPrefLetter == code && !key.isOnKey(x, y)) { - Log.d(TAG, "CORRECTED !!!!!!"); - } - } - return mPrefLetter == code; - } else { - final boolean isOnKey = key.isOnKey(x, y); - int[] nearby = getNearestKeys(x, y); - List nearbyKeys = getKeys(); - if (isOnKey) { - // If it's a preferred letter - if (inPrefList(code, pref)) { - // Check if its frequency is much lower than a nearby key - mPrefLetter = code; - mPrefLetterX = x; - mPrefLetterY = y; - for (int i = 0; i < nearby.length; i++) { - Key k = nearbyKeys.get(nearby[i]); - if (k != key && inPrefList(k.mCode, pref)) { - final int dist = distanceFrom(k, x, y); - if (dist < (int) (k.mWidth * OVERLAP_PERCENTAGE_LOW_PROB) && - (pref[k.mCode] > pref[mPrefLetter] * 3)) { - mPrefLetter = k.mCode; - mPrefDistance = dist; - if (DEBUG_PREFERRED_LETTER) { - Log.d(TAG, "CORRECTED ALTHOUGH PREFERRED !!!!!!"); - } - break; - } - } - } - - return mPrefLetter == code; - } - } - - // Get the surrounding keys and intersect with the preferred list - // For all in the intersection - // if distance from touch point is within a reasonable distance - // make this the pref letter - // If no pref letter - // return inside; - // else return thiskey == prefletter; - - for (int i = 0; i < nearby.length; i++) { - Key k = nearbyKeys.get(nearby[i]); - if (inPrefList(k.mCode, pref)) { - final int dist = distanceFrom(k, x, y); - if (dist < (int) (k.mWidth * OVERLAP_PERCENTAGE_HIGH_PROB) - && dist < mPrefDistance) { - mPrefLetter = k.mCode; - mPrefLetterX = x; - mPrefLetterY = y; - mPrefDistance = dist; - } - } - } - // Didn't find any - if (mPrefLetter == 0) { - return isOnKey; - } else { - return mPrefLetter == code; - } - } } // Lock into the spacebar @@ -463,19 +370,6 @@ public class LatinKeyboard extends Keyboard { return key.isOnKey(x, y); } - private boolean inPrefList(int code, int[] pref) { - if (code < pref.length && code >= 0) return pref[code] > 0; - return false; - } - - private int distanceFrom(Key k, int x, int y) { - if (y > k.mY && y < k.mY + k.mHeight) { - return Math.abs(k.mX + k.mWidth / 2 - x); - } else { - return Integer.MAX_VALUE; - } - } - @Override public int[] getNearestKeys(int x, int y) { if (mCurrentlyInSpace) { @@ -487,8 +381,8 @@ public class LatinKeyboard extends Keyboard { } } - private int getTextSizeFromTheme(int style, int defValue) { - TypedArray array = mContext.getTheme().obtainStyledAttributes( + private static int getTextSizeFromTheme(Theme theme, int style, int defValue) { + TypedArray array = theme.obtainStyledAttributes( style, new int[] { android.R.attr.textSize }); int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); return textSize; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index e7246dd6e..d6c3723fd 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -16,9 +16,9 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.Utils; -import com.android.inputmethod.voice.VoiceIMEConnector; import android.content.Context; import android.graphics.Canvas; @@ -66,7 +66,8 @@ public class LatinKeyboardView extends KeyboardView { } } - public void setLatinKeyboard(LatinKeyboard newKeyboard) { + @Override + public void setKeyboard(Keyboard newKeyboard) { final LatinKeyboard oldKeyboard = getLatinKeyboard(); if (oldKeyboard != null) { // Reset old keyboard state before switching to new keyboard. @@ -80,7 +81,7 @@ public class LatinKeyboardView extends KeyboardView { mLastRowY = (newKeyboard.getHeight() * 3) / 4; } - public LatinKeyboard getLatinKeyboard() { + private LatinKeyboard getLatinKeyboard() { Keyboard keyboard = getKeyboard(); if (keyboard instanceof LatinKeyboard) { return (LatinKeyboard)keyboard; @@ -144,6 +145,10 @@ public class LatinKeyboardView extends KeyboardView { // If device has distinct multi touch panel, there is no need to check sudden jump. if (hasDistinctMultitouch()) return false; + // If accessibiliy is enabled, stop looking for sudden jumps because it interferes + // with touch exploration of the keyboard. + if (isAccessibilityEnabled()) + return false; final int action = me.getAction(); final int x = (int) me.getX(); final int y = (int) me.getY(); @@ -259,6 +264,6 @@ public class LatinKeyboardView extends KeyboardView { @Override protected void onAttachedToWindow() { // Token is available from here. - VoiceIMEConnector.getInstance().onAttachedToWindow(); + VoiceProxy.getInstance().onAttachedToWindow(); } } diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java index f04991eb7..a8750d378 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java @@ -35,24 +35,24 @@ public class MiniKeyboardKeyDetector extends KeyDetector { } @Override - public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { + public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { final Key[] keys = getKeys(); final int touchX = getTouchX(x); final int touchY = getTouchY(y); - int closestKeyIndex = NOT_A_KEY; - int closestKeyDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; + int nearestIndex = NOT_A_KEY; + int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; final int keyCount = keys.length; for (int index = 0; index < keyCount; index++) { final int dist = keys[index].squaredDistanceToEdge(touchX, touchY); - if (dist < closestKeyDist) { - closestKeyIndex = index; - closestKeyDist = dist; + if (dist < nearestDist) { + nearestIndex = index; + nearestDist = dist; } } - if (allKeys != null && closestKeyIndex != NOT_A_KEY) - allKeys[0] = keys[closestKeyIndex].mCode; - return closestKeyIndex; + if (allCodes != null && nearestIndex != NOT_A_KEY) + allCodes[0] = keys[nearestIndex].mCode; + return nearestIndex; } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 4c90e2c3f..746857819 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -38,6 +38,7 @@ public class PointerTracker { public void invalidateKey(Key key); public void showPreview(int keyIndex, PointerTracker tracker); public boolean hasDistinctMultitouch(); + public boolean isAccessibilityEnabled(); } public final int mPointerId; @@ -68,6 +69,9 @@ public class PointerTracker { private final PointerTrackerKeyState mKeyState; + // true if accessibility is enabled in the parent keyboard + private boolean mIsAccessibilityEnabled; + // true if keyboard layout has been changed. private boolean mKeyboardLayoutHasBeenChanged; @@ -89,9 +93,9 @@ public class PointerTracker { // Empty {@link KeyboardActionListener} private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() { @Override - public void onPress(int primaryCode) {} + public void onPress(int primaryCode, boolean withSliding) {} @Override - public void onRelease(int primaryCode) {} + public void onRelease(int primaryCode, boolean withSliding) {} @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {} @Override @@ -112,6 +116,7 @@ public class PointerTracker { mKeyDetector = keyDetector; mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mKeyState = new PointerTrackerKeyState(keyDetector); + mIsAccessibilityEnabled = proxy.isAccessibilityEnabled(); mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); @@ -128,33 +133,47 @@ public class PointerTracker { mListener = listener; } - // Returns true if keyboard has been changed by this callback. - private boolean callListenerOnPressAndCheckKeyboardLayoutChange(int primaryCode) { - if (DEBUG_LISTENER) - Log.d(TAG, "onPress : " + keyCodePrintable(primaryCode)); - mListener.onPress(primaryCode); - final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; - mKeyboardLayoutHasBeenChanged = false; - return keyboardLayoutHasBeenChanged; + public void setAccessibilityEnabled(boolean accessibilityEnabled) { + mIsAccessibilityEnabled = accessibilityEnabled; } - private void callListenerOnCodeInput(int primaryCode, int[] keyCodes, int x, int y) { + // Returns true if keyboard has been changed by this callback. + private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) { + if (DEBUG_LISTENER) + Log.d(TAG, "onPress : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding); + if (key.mEnabled) { + mListener.onPress(key.mCode, withSliding); + final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; + mKeyboardLayoutHasBeenChanged = false; + return keyboardLayoutHasBeenChanged; + } + return false; + } + + // Note that we need primaryCode argument because the keyboard may in shifted state and the + // primaryCode is different from {@link Key#mCode}. + private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) { if (DEBUG_LISTENER) Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode) + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y); - mListener.onCodeInput(primaryCode, keyCodes, x, y); + if (key.mEnabled) + mListener.onCodeInput(primaryCode, keyCodes, x, y); } - private void callListenerOnTextInput(CharSequence text) { + private void callListenerOnTextInput(Key key) { if (DEBUG_LISTENER) - Log.d(TAG, "onTextInput: text=" + text); - mListener.onTextInput(text); + Log.d(TAG, "onTextInput: text=" + key.mOutputText); + if (key.mEnabled) + mListener.onTextInput(key.mOutputText); } - private void callListenerOnRelease(int primaryCode) { + // Note that we need primaryCode argument because the keyboard may in shifted state and the + // primaryCode is different from {@link Key#mCode}. + private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { if (DEBUG_LISTENER) - Log.d(TAG, "onRelease : " + keyCodePrintable(primaryCode)); - mListener.onRelease(primaryCode); + Log.d(TAG, "onRelease : " + keyCodePrintable(primaryCode) + " sliding=" + withSliding); + if (key.mEnabled) + mListener.onRelease(primaryCode, withSliding); } private void callListenerOnCancelInput() { @@ -302,9 +321,10 @@ public class PointerTracker { private void onDownEventInternal(int x, int y, long eventTime) { int keyIndex = mKeyState.onDownKey(x, y, eventTime); // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding - // from modifier key, or 3) this pointer is on mini-keyboard. + // from modifier key, 3) this pointer is on mini-keyboard, or 4) accessibility is enabled. mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex) - || mKeyDetector instanceof MiniKeyboardKeyDetector; + || mKeyDetector instanceof MiniKeyboardKeyDetector + || mIsAccessibilityEnabled; mKeyboardLayoutHasBeenChanged = false; mKeyAlreadyProcessed = false; mIsRepeatableKey = false; @@ -313,11 +333,13 @@ public class PointerTracker { // This onPress call may have changed keyboard layout. Those cases are detected at // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new // keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(mKeys[keyIndex].mCode)) + if (callListenerOnPressAndCheckKeyboardLayoutChange(mKeys[keyIndex], false)) keyIndex = mKeyState.onDownKey(x, y, eventTime); } if (isValidKeyIndex(keyIndex)) { - if (mKeys[keyIndex].mRepeatable) { + // Accessibility disables key repeat because users may need to pause on a key to hear + // its spoken description. + if (mKeys[keyIndex].mRepeatable && !mIsAccessibilityEnabled) { repeatKey(keyIndex); mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mIsRepeatableKey = true; @@ -346,7 +368,7 @@ public class PointerTracker { // This onPress call may have changed keyboard layout. Those cases are detected at // {@link #setKeyboard}. In those cases, we should update keyIndex according to the // new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex).mCode)) + if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true)) keyIndex = keyState.onMoveKey(x, y); keyState.onMoveToNewKey(keyIndex, x, y); startLongPressTimer(keyIndex); @@ -355,13 +377,13 @@ public class PointerTracker { // onRelease() first to notify that the previous key has been released, then call // onPress() to notify that the new key is being pressed. mIsInSlidingKeyInput = true; - callListenerOnRelease(oldKey.mCode); + callListenerOnRelease(oldKey, oldKey.mCode, true); mHandler.cancelLongPressTimers(); if (mIsAllowedSlidingKeyInput) { // This onPress call may have changed keyboard layout. Those cases are detected // at {@link #setKeyboard}. In those cases, we should update keyIndex according // to the new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex).mCode)) + if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), true)) keyIndex = keyState.onMoveKey(x, y); keyState.onMoveToNewKey(keyIndex, x, y); startLongPressTimer(keyIndex); @@ -390,7 +412,7 @@ public class PointerTracker { // The pointer has been slid out from the previous key, we must call onRelease() to // notify that the previous key has been released. mIsInSlidingKeyInput = true; - callListenerOnRelease(oldKey.mCode); + callListenerOnRelease(oldKey, oldKey.mCode, true); mHandler.cancelLongPressTimers(); if (mIsAllowedSlidingKeyInput) { keyState.onMoveToNewKey(keyIndex, x ,y); @@ -507,8 +529,9 @@ public class PointerTracker { updateKeyGraphics(keyIndex); // The modifier key, such as shift key, should not be shown as preview when multi-touch is // supported. On the other hand, if multi-touch is not supported, the modifier key should - // be shown as preview. - if (mHasDistinctMultitouch && isModifier()) { + // be shown as preview. If accessibility is turned on, the modifier key should be shown as + // preview. + if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) { mProxy.showPreview(NOT_A_KEY, this); } else { mProxy.showPreview(keyIndex, this); @@ -516,6 +539,11 @@ public class PointerTracker { } private void startLongPressTimer(int keyIndex) { + // Accessibility disables long press because users are likely to need to pause on a key + // for an unspecified duration in order to hear the key's spoken description. + if (mIsAccessibilityEnabled) { + return; + } Key key = getKey(keyIndex); if (key.mCode == Keyboard.CODE_SHIFT) { mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); @@ -539,8 +567,8 @@ public class PointerTracker { return; } if (key.mOutputText != null) { - callListenerOnTextInput(key.mOutputText); - callListenerOnRelease(key.mCode); + callListenerOnTextInput(key); + callListenerOnRelease(key, key.mCode, false); } else { int code = key.mCode; final int[] codes = mKeyDetector.newCodeArray(); @@ -561,9 +589,8 @@ public class PointerTracker { codes[1] = codes[0]; codes[0] = code; } - if (key.mEnabled) - callListenerOnCodeInput(code, codes, x, y); - callListenerOnRelease(code); + callListenerOnCodeInput(key, code, codes, x, y); + callListenerOnRelease(key, code, false); } } diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java new file mode 100644 index 000000000..80d6de952 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard; + +import com.android.inputmethod.latin.Utils; + +import java.util.Arrays; +import java.util.List; + +public class ProximityInfo { + public static final int MAX_PROXIMITY_CHARS_SIZE = 16; + + private final int mGridWidth; + private final int mGridHeight; + private final int mGridSize; + + ProximityInfo(int gridWidth, int gridHeight) { + mGridWidth = gridWidth; + mGridHeight = gridHeight; + mGridSize = mGridWidth * mGridHeight; + } + + private int mNativeProximityInfo; + static { + Utils.loadNativeLibrary(); + } + private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth, + int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray); + private native void releaseProximityInfoNative(int nativeProximityInfo); + + public final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, + int keyboardHeight, List keys) { + int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; + Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE); + for (int i = 0; i < mGridSize; ++i) { + final int proximityCharsLength = gridNeighborKeyIndexes[i].length; + for (int j = 0; j < proximityCharsLength; ++j) { + proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] = + keys.get(gridNeighborKeyIndexes[i][j]).mCode; + } + } + mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, + keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray); + } + + // TODO: Get rid of this function's input (keyboard). + public int getNativeProximityInfo(Keyboard keyboard) { + if (mNativeProximityInfo == 0) { + // TODO: Move this function to ProximityInfo and make this private. + keyboard.computeNearestNeighbors(); + } + return mNativeProximityInfo; + } + + @Override + protected void finalize() throws Throwable { + try { + if (mNativeProximityInfo != 0) { + releaseProximityInfoNative(mNativeProximityInfo); + mNativeProximityInfo = 0; + } + } finally { + super.finalize(); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java index 0920da2cb..c3fd1984b 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java @@ -16,49 +16,106 @@ package com.android.inputmethod.keyboard; +import android.util.Log; + import java.util.Arrays; public class ProximityKeyDetector extends KeyDetector { + private static final String TAG = ProximityKeyDetector.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final int MAX_NEARBY_KEYS = 12; // working area - private int[] mDistances = new int[MAX_NEARBY_KEYS]; + private final int[] mDistances = new int[MAX_NEARBY_KEYS]; + private final int[] mIndices = new int[MAX_NEARBY_KEYS]; @Override protected int getMaxNearbyKeys() { return MAX_NEARBY_KEYS; } + private void initializeNearbyKeys() { + Arrays.fill(mDistances, Integer.MAX_VALUE); + Arrays.fill(mIndices, NOT_A_KEY); + } + + /** + * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance. + * + * @param keyIndex index of the key. + * @param distance distance between the key's edge and user touched point. + * @return order of the key in the nearby buffer, 0 if it is the nearest key. + */ + private int sortNearbyKeys(int keyIndex, int distance) { + final int[] distances = mDistances; + final int[] indices = mIndices; + for (int insertPos = 0; insertPos < distances.length; insertPos++) { + if (distance < distances[insertPos]) { + final int nextPos = insertPos + 1; + if (nextPos < distances.length) { + System.arraycopy(distances, insertPos, distances, nextPos, + distances.length - nextPos); + System.arraycopy(indices, insertPos, indices, nextPos, + indices.length - nextPos); + } + distances[insertPos] = distance; + indices[insertPos] = keyIndex; + return insertPos; + } + } + return distances.length; + } + + private void getNearbyKeyCodes(final int[] allCodes) { + final Key[] keys = getKeys(); + final int[] indices = mIndices; + + // allCodes[0] should always have the key code even if it is a non-letter key. + if (indices[0] == NOT_A_KEY) { + allCodes[0] = NOT_A_CODE; + return; + } + + int numCodes = 0; + for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) { + final int index = indices[j]; + if (index == NOT_A_KEY) + break; + final int code = keys[index].mCode; + // filter out a non-letter key from nearby keys + if (code < Keyboard.CODE_SPACE) + continue; + allCodes[numCodes++] = code; + } + } + @Override - public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { + public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { final Key[] keys = getKeys(); final int touchX = getTouchX(x); final int touchY = getTouchY(y); + initializeNearbyKeys(); int primaryIndex = NOT_A_KEY; - final int[] distances = mDistances; - Arrays.fill(distances, Integer.MAX_VALUE); for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) { final Key key = keys[index]; final boolean isInside = key.isInside(touchX, touchY); - if (isInside) - primaryIndex = index; - final int dist = key.squaredDistanceToEdge(touchX, touchY); - if (isInside || (mProximityCorrectOn && dist < mProximityThresholdSquare)) { - if (allKeys == null) continue; - // Find insertion point - for (int j = 0; j < distances.length; j++) { - if (distances[j] > dist) { - final int nextPos = j + 1; - System.arraycopy(distances, j, distances, nextPos, - distances.length - nextPos); - System.arraycopy(allKeys, j, allKeys, nextPos, - allKeys.length - nextPos); - distances[j] = dist; - allKeys[j] = key.mCode; - break; - } - } + final int distance = key.squaredDistanceToEdge(touchX, touchY); + if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) { + final int insertedPosition = sortNearbyKeys(index, distance); + if (insertedPosition == 0 && isInside) + primaryIndex = index; + } + } + + if (allCodes != null && allCodes.length > 0) { + getNearbyKeyCodes(allCodes); + if (DEBUG) { + Log.d(TAG, "x=" + x + " y=" + y + + " primary=" + + (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode) + + " codes=" + Arrays.toString(allCodes)); } } diff --git a/java/src/com/android/inputmethod/latin/AccessibilityUtils.java b/java/src/com/android/inputmethod/latin/AccessibilityUtils.java new file mode 100644 index 000000000..cd3f9e0ad --- /dev/null +++ b/java/src/com/android/inputmethod/latin/AccessibilityUtils.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardSwitcher; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility functions for accessibility support. + */ +public class AccessibilityUtils { + /** Shared singleton instance. */ + private static final AccessibilityUtils sInstance = new AccessibilityUtils(); + private /* final */ LatinIME mService; + private /* final */ AccessibilityManager mAccessibilityManager; + private /* final */ Map mDescriptions; + + /** + * Returns a shared instance of AccessibilityUtils. + * + * @return A shared instance of AccessibilityUtils. + */ + public static AccessibilityUtils getInstance() { + return sInstance; + } + + /** + * Initializes (or re-initializes) the shared instance of AccessibilityUtils + * with the specified parent service and preferences. + * + * @param service The parent input method service. + * @param prefs The parent preferences. + */ + public static void init(LatinIME service, SharedPreferences prefs) { + sInstance.initialize(service, prefs); + } + + private AccessibilityUtils() { + // This class is not publicly instantiable. + } + + /** + * Initializes (or re-initializes) with the specified parent service and + * preferences. + * + * @param service The parent input method service. + * @param prefs The parent preferences. + */ + private void initialize(LatinIME service, SharedPreferences prefs) { + mService = service; + mAccessibilityManager = (AccessibilityManager) service.getSystemService( + Context.ACCESSIBILITY_SERVICE); + mDescriptions = null; + } + + /** + * Returns true if accessibility is enabled. + * + * @return {@code true} if accessibility is enabled. + */ + public boolean isAccessibilityEnabled() { + return mAccessibilityManager.isEnabled(); + } + + /** + * Speaks a key's action after it has been released. Does not speak letter + * keys since typed keys are already spoken aloud by TalkBack. + *

+ * No-op if accessibility is not enabled. + *

+ * + * @param primaryCode The primary code of the released key. + * @param switcher The input method's {@link KeyboardSwitcher}. + */ + public void onRelease(int primaryCode, KeyboardSwitcher switcher) { + if (!isAccessibilityEnabled()) { + return; + } + + int resId = -1; + + switch (primaryCode) { + case Keyboard.CODE_SHIFT: { + if (switcher.isShiftedOrShiftLocked()) { + resId = R.string.description_shift_on; + } else { + resId = R.string.description_shift_off; + } + break; + } + + case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: { + if (switcher.isAlphabetMode()) { + resId = R.string.description_symbols_off; + } else { + resId = R.string.description_symbols_on; + } + break; + } + } + + if (resId >= 0) { + speakDescription(mService.getResources().getText(resId)); + } + } + + /** + * Speaks a key's description for accessibility. If a key has an explicit + * description defined in keycodes.xml, that will be used. Otherwise, if the + * key is a Unicode character, then its character will be used. + *

+ * No-op if accessibility is not enabled. + *

+ * + * @param primaryCode The primary code of the pressed key. + * @param switcher The input method's {@link KeyboardSwitcher}. + */ + public void onPress(int primaryCode, KeyboardSwitcher switcher) { + if (!isAccessibilityEnabled()) { + return; + } + + // TODO Use the current keyboard state to read "Switch to symbols" + // instead of just "Symbols" (and similar for shift key). + CharSequence description = describeKey(primaryCode); + if (description == null && Character.isDefined((char) primaryCode)) { + description = Character.toString((char) primaryCode); + } + + if (description != null) { + speakDescription(description); + } + } + + /** + * Returns a text description for a given key code. If the key does not have + * an explicit description, returns null. + * + * @param keyCode An integer key code. + * @return A {@link CharSequence} describing the key or null if + * no description is available. + */ + private CharSequence describeKey(int keyCode) { + // If not loaded yet, load key descriptions from XML file. + if (mDescriptions == null) { + mDescriptions = loadDescriptions(); + } + + return mDescriptions.get(keyCode); + } + + /** + * Loads key descriptions from resources. + */ + private Map loadDescriptions() { + final Map descriptions = new HashMap(); + final TypedArray array = mService.getResources().obtainTypedArray(R.array.key_descriptions); + + // Key descriptions are stored as a key code followed by a string. + for (int i = 0; i < array.length() - 1; i += 2) { + int code = array.getInteger(i, 0); + CharSequence desc = array.getText(i + 1); + + descriptions.put(code, desc); + } + + array.recycle(); + + return descriptions; + } + + /** + * Sends a character sequence to be read aloud. + * + * @param description The {@link CharSequence} to be read aloud. + */ + private void speakDescription(CharSequence description) { + // TODO We need to add an AccessibilityEvent type for IMEs. + final AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + event.setPackageName(mService.getPackageName()); + event.setClassName(getClass().getName()); + event.setAddedCount(description.length()); + event.getText().add(description); + + mAccessibilityManager.sendAccessibilityEvent(event); + } +} diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java new file mode 100644 index 000000000..074ecacc5 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import java.io.File; + +/** + * Immutable class to hold the address of an asset. + * As opposed to a normal file, an asset is usually represented as a contiguous byte array in + * the package file. Open it correctly thus requires the name of the package it is in, but + * also the offset in the file and the length of this data. This class encapsulates these three. + */ +class AssetFileAddress { + public final String mFilename; + public final long mOffset; + public final long mLength; + + public AssetFileAddress(final String filename, final long offset, final long length) { + mFilename = filename; + mOffset = offset; + mLength = length; + } + + public static AssetFileAddress makeFromFileName(final String filename) { + if (null == filename) return null; + File f = new File(filename); + if (null == f || !f.isFile()) return null; + return new AssetFileAddress(filename, 0l, f.length()); + } + + public static AssetFileAddress makeFromFileNameAndOffset(final String filename, + final long offset, final long length) { + if (null == filename) return null; + File f = new File(filename); + if (null == f || !f.isFile()) return null; + return new AssetFileAddress(filename, offset, length); + } +} diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java new file mode 100644 index 000000000..d3119792c --- /dev/null +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Map; + +public class AutoCorrection { + private static final boolean DBG = LatinImeLogger.sDBG; + private static final String TAG = AutoCorrection.class.getSimpleName(); + private boolean mHasAutoCorrection; + private CharSequence mAutoCorrectionWord; + private double mNormalizedScore; + + public void init() { + mHasAutoCorrection = false; + mAutoCorrectionWord = null; + mNormalizedScore = Integer.MIN_VALUE; + } + + public boolean hasAutoCorrection() { + return mHasAutoCorrection; + } + + public CharSequence getAutoCorrectionWord() { + return mAutoCorrectionWord; + } + + public double getNormalizedScore() { + return mNormalizedScore; + } + + public void updateAutoCorrectionStatus(Map dictionaries, + WordComposer wordComposer, ArrayList suggestions, int[] sortedScores, + CharSequence typedWord, double autoCorrectionThreshold, int correctionMode, + CharSequence quickFixedWord, CharSequence whitelistedWord) { + if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) { + mHasAutoCorrection = true; + mAutoCorrectionWord = whitelistedWord; + } else if (hasAutoCorrectionForTypedWord( + dictionaries, wordComposer, suggestions, typedWord, correctionMode)) { + mHasAutoCorrection = true; + mAutoCorrectionWord = typedWord; + } else if (hasAutoCorrectionForQuickFix(quickFixedWord)) { + mHasAutoCorrection = true; + mAutoCorrectionWord = quickFixedWord; + } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions, correctionMode, + sortedScores, typedWord, autoCorrectionThreshold)) { + mHasAutoCorrection = true; + mAutoCorrectionWord = suggestions.get(0); + } + } + + public static boolean isValidWord( + Map dictionaries, CharSequence word, boolean ignoreCase) { + if (TextUtils.isEmpty(word)) { + return false; + } + final CharSequence lowerCasedWord = word.toString().toLowerCase(); + for (final String key : dictionaries.keySet()) { + if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue; + final Dictionary dictionary = dictionaries.get(key); + if (dictionary.isValidWord(word) + || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { + return true; + } + } + return false; + } + + public static boolean isValidWordForAutoCorrection( + Map dictionaries, CharSequence word, boolean ignoreCase) { + final Dictionary whiteList = dictionaries.get(Suggest.DICT_KEY_WHITELIST); + // If "word" is in the whitelist dictionary, it should not be auto corrected. + if (whiteList != null && whiteList.isValidWord(word)) { + return false; + } + return isValidWord(dictionaries, word, ignoreCase); + } + + private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) { + return whiteListedWord != null; + } + + private boolean hasAutoCorrectionForTypedWord(Map dictionaries, + WordComposer wordComposer, ArrayList suggestions, CharSequence typedWord, + int correctionMode) { + if (TextUtils.isEmpty(typedWord)) return false; + boolean isValidWord = isValidWordForAutoCorrection(dictionaries, typedWord, false); + return wordComposer.size() > 1 && suggestions.size() > 0 && isValidWord + && (correctionMode == Suggest.CORRECTION_FULL + || correctionMode == Suggest.CORRECTION_FULL_BIGRAM); + } + + private static boolean hasAutoCorrectionForQuickFix(CharSequence quickFixedWord) { + return quickFixedWord != null; + } + + private boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer, + ArrayList suggestions, int correctionMode, int[] sortedScores, + CharSequence typedWord, double autoCorrectionThreshold) { + if (wordComposer.size() > 1 && (correctionMode == Suggest.CORRECTION_FULL + || correctionMode == Suggest.CORRECTION_FULL_BIGRAM) + && typedWord != null && suggestions.size() > 0 && sortedScores.length > 0) { + final CharSequence autoCorrectionCandidate = suggestions.get(0); + final int autoCorrectionCandidateScore = sortedScores[0]; + // TODO: when the normalized score of the first suggestion is nearly equals to + // the normalized score of the second suggestion, behave less aggressive. + mNormalizedScore = Utils.calcNormalizedScore( + typedWord,autoCorrectionCandidate, autoCorrectionCandidateScore); + if (DBG) { + Log.d(TAG, "Normalized " + typedWord + "," + autoCorrectionCandidate + "," + + autoCorrectionCandidateScore + ", " + mNormalizedScore + + "(" + autoCorrectionThreshold + ")"); + } + if (mNormalizedScore >= autoCorrectionThreshold) { + if (DBG) { + Log.d(TAG, "Auto corrected by S-threshold."); + } + return true; + } + } + return false; + } + +} diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java index 307b81d43..a00b0915c 100644 --- a/java/src/com/android/inputmethod/latin/AutoDictionary.java +++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java @@ -27,6 +27,7 @@ import android.provider.BaseColumns; import android.util.Log; import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index d87672c0e..fa90fce67 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -16,29 +16,37 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.ProximityInfo; + import android.content.Context; import android.content.res.AssetFileDescriptor; import android.util.Log; import java.io.File; import java.util.Arrays; +import java.util.Locale; /** * Implements a static, compacted, binary dictionary of standard words. */ public class BinaryDictionary extends Dictionary { + public static final String DICTIONARY_PACK_AUTHORITY = + "com.android.inputmethod.latin.dictionarypack"; + /** - * There is difference between what java and native code can handle. + * There is a difference between what java and native code can handle. * This value should only be used in BinaryDictionary.java * It is necessary to keep it at this value because some languages e.g. German have * really long words. */ - protected static final int MAX_WORD_LENGTH = 48; + public static final int MAX_WORD_LENGTH = 48; + public static final int MAX_WORDS = 18; private static final String TAG = "BinaryDictionary"; - private static final int MAX_ALTERNATIVES = 16; - private static final int MAX_WORDS = 18; + private static final int MAX_PROXIMITY_CHARS_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; private static final int MAX_BIGRAMS = 60; private static final int TYPED_LETTER_MULTIPLIER = 2; @@ -47,28 +55,45 @@ public class BinaryDictionary extends Dictionary { private int mDicTypeId; private int mNativeDict; private long mDictLength; - private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES]; + private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_PROXIMITY_CHARS_SIZE]; private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS]; - private final int[] mFrequencies = new int[MAX_WORDS]; - private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS]; + private final int[] mScores = new int[MAX_WORDS]; + private final int[] mBigramScores = new int[MAX_BIGRAMS]; - static { - try { - System.loadLibrary("jni_latinime2"); - } catch (UnsatisfiedLinkError ule) { - Log.e(TAG, "Could not load native library jni_latinime"); + private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance(); + + public static class Flag { + public final String mName; + public final int mValue; + + public Flag(String name, int value) { + mName = name; + mValue = value; } } + public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = + new Flag("requiresGermanUmlautProcessing", 0x1); + + private static final Flag[] ALL_FLAGS = { + // Here should reside all flags that trigger some special processing + // These *must* match the definition in UnigramDictionary enum in + // unigram_dictionary.h so please update both at the same time. + FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING, + }; + + private int mFlags = 0; + private BinaryDictionary() { } /** - * Initialize a dictionary from a raw resource file + * Initializes a dictionary from a raw resource file * @param context application context for reading resources * @param resId the resource containing the raw binary dictionary - * @return initialized instance of BinaryDictionary + * @param dicTypeId the type of the dictionary being created, out of the list in Suggest.DIC_* + * @return an initialized instance of BinaryDictionary */ public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) { synchronized (sInstance) { @@ -93,12 +118,12 @@ public class BinaryDictionary extends Dictionary { return null; } } + sInstance.mFlags = initFlags(ALL_FLAGS, SubtypeSwitcher.getInstance()); return sInstance; } - // For unit test - /* package */ static BinaryDictionary initDictionary(File dictionary, long startOffset, - long length, int dicTypeId) { + /* package for test */ static BinaryDictionary initDictionary(File dictionary, long startOffset, + long length, int dicTypeId, Flag[] flagArray) { synchronized (sInstance) { sInstance.closeInternal(); if (dictionary.isFile()) { @@ -109,6 +134,51 @@ public class BinaryDictionary extends Dictionary { return null; } } + sInstance.mFlags = initFlags(flagArray, null); + return sInstance; + } + + private static int initFlags(Flag[] flagArray, SubtypeSwitcher switcher) { + int flags = 0; + for (Flag entry : flagArray) { + if (switcher == null || switcher.currentSubtypeContainsExtraValueKey(entry.mName)) + flags |= entry.mValue; + } + return flags; + } + + static { + Utils.loadNativeLibrary(); + } + + /** + * Initializes a dictionary from a dictionary pack. + * + * This searches for a content provider providing a dictionary pack for the specified + * locale. If none is found, it falls back to using the resource passed as fallBackResId + * as a dictionary. + * @param context application context for reading resources + * @param dicTypeId the type of the dictionary being created, out of the list in Suggest.DIC_* + * @param locale the locale for which to create the dictionary + * @param fallBackResId the id of the resource to use as a fallback if no pack is found + * @return an initialized instance of BinaryDictionary + */ + public static BinaryDictionary initDictionaryFromManager(Context context, int dicTypeId, + Locale locale, int fallbackResId) { + if (null == locale) { + Log.e(TAG, "No locale defined for dictionary"); + return initDictionary(context, fallbackResId, dicTypeId); + } + synchronized (sInstance) { + sInstance.closeInternal(); + + final AssetFileAddress dictFile = BinaryDictionaryGetter.getDictionaryFile(locale, + context, fallbackResId); + if (null != dictFile) { + sInstance.loadDictionary(dictFile.mFilename, dictFile.mOffset, dictFile.mLength); + sInstance.mDicTypeId = dicTypeId; + } + } return sInstance; } @@ -117,89 +187,99 @@ public class BinaryDictionary extends Dictionary { int maxWords, int maxAlternatives); private native void closeNative(int dict); private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); - private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize, - char[] outputChars, int[] frequencies, - int[] nextLettersFrequencies, int nextLettersSize); + private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates, + int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars, + int[] scores); private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength, - int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies, + int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, int maxWordLength, int maxBigrams, int maxAlternatives); private final void loadDictionary(String path, long startOffset, long length) { mNativeDict = openNative(path, startOffset, length, - TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER, - MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES); + TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, + MAX_WORD_LENGTH, MAX_WORDS, MAX_PROXIMITY_CHARS_SIZE); mDictLength = length; } @Override public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback, int[] nextLettersFrequencies) { + final WordCallback callback) { if (mNativeDict == 0) return; char[] chars = previousWord.toString().toCharArray(); Arrays.fill(mOutputChars_bigrams, (char) 0); - Arrays.fill(mFrequencies_bigrams, 0); + Arrays.fill(mBigramScores, 0); int codesSize = codes.size(); Arrays.fill(mInputCodes, -1); int[] alternatives = codes.getCodesAt(0); System.arraycopy(alternatives, 0, mInputCodes, 0, - Math.min(alternatives.length, MAX_ALTERNATIVES)); + Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE)); int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize, - mOutputChars_bigrams, mFrequencies_bigrams, MAX_WORD_LENGTH, MAX_BIGRAMS, - MAX_ALTERNATIVES); + mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS, + MAX_PROXIMITY_CHARS_SIZE); for (int j = 0; j < count; ++j) { - if (mFrequencies_bigrams[j] < 1) break; + if (mBigramScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; while (len < MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) { ++len; } if (len > 0) { - callback.addWord(mOutputChars_bigrams, start, len, mFrequencies_bigrams[j], + callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j], mDicTypeId, DataType.BIGRAM); } } } @Override - public void getWords(final WordComposer codes, final WordCallback callback, - int[] nextLettersFrequencies) { - if (mNativeDict == 0) return; - - final int codesSize = codes.size(); - // Won't deal with really long words. - if (codesSize > MAX_WORD_LENGTH - 1) return; - - Arrays.fill(mInputCodes, -1); - for (int i = 0; i < codesSize; i++) { - int[] alternatives = codes.getCodesAt(i); - System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES, - Math.min(alternatives.length, MAX_ALTERNATIVES)); - } - Arrays.fill(mOutputChars, (char) 0); - Arrays.fill(mFrequencies, 0); - - int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, mOutputChars, - mFrequencies, nextLettersFrequencies, - nextLettersFrequencies != null ? nextLettersFrequencies.length : 0); + public void getWords(final WordComposer codes, final WordCallback callback) { + final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(), + mOutputChars, mScores); for (int j = 0; j < count; ++j) { - if (mFrequencies[j] < 1) break; + if (mScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { ++len; } if (len > 0) { - callback.addWord(mOutputChars, start, len, mFrequencies[j], mDicTypeId, + callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId, DataType.UNIGRAM); } } } + /* package for test */ boolean isValidDictionary() { + return mNativeDict != 0; + } + + /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard, + char[] outputChars, int[] scores) { + if (!isValidDictionary()) return -1; + + final int codesSize = codes.size(); + // Won't deal with really long words. + if (codesSize > MAX_WORD_LENGTH - 1) return -1; + + Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE); + for (int i = 0; i < codesSize; i++) { + int[] alternatives = codes.getCodesAt(i); + System.arraycopy(alternatives, 0, mInputCodes, i * MAX_PROXIMITY_CHARS_SIZE, + Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE)); + } + Arrays.fill(outputChars, (char) 0); + Arrays.fill(scores, 0); + + return getSuggestionsNative( + mNativeDict, keyboard.getProximityInfo(), + codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, + mFlags, outputChars, scores); + } + @Override public boolean isValidWord(CharSequence word) { if (word == null) return false; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java new file mode 100644 index 000000000..d0464dd94 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +/** + * Group class for static methods to help with creation and getting of the binary dictionary + * file from the dictionary provider + */ +public class BinaryDictionaryFileDumper { + /** + * The size of the temporary buffer to copy files. + */ + static final int FILE_READ_BUFFER_SIZE = 1024; + + // Prevents this class to be accidentally instantiated. + private BinaryDictionaryFileDumper() { + } + + /** + * Generates a file name that matches the locale passed as an argument. + * The file name is basically the result of the .toString() method, except we replace + * any @File.separator with an underscore to avoid generating a file name that may not + * be created. + * @param locale the locale for which to get the file name + * @param context the context to use for getting the directory + * @return the name of the file to be created + */ + private static String getCacheFileNameForLocale(Locale locale, Context context) { + // The following assumes two things : + // 1. That File.separator is not the same character as "_" + // I don't think any android system will ever use "_" as a path separator + // 2. That no two locales differ by only a File.separator versus a "_" + // Since "_" can't be part of locale components this should be safe. + // Examples: + // en -> en + // en_US_POSIX -> en_US_POSIX + // en__foo/bar -> en__foo_bar + final String[] separator = { File.separator }; + final String[] empty = { "_" }; + final CharSequence basename = TextUtils.replace(locale.toString(), separator, empty); + return context.getFilesDir() + File.separator + basename; + } + + /** + * Return for a given locale the provider URI to query to get the dictionary. + */ + public static Uri getProviderUri(Locale locale) { + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( + locale.toString()).build(); + } + + /** + * Queries a content provider for dictionary data for some locale and returns it as a file name. + * + * This will query a content provider for dictionary data for a given locale, and return + * the name of a file suitable to be mmap'ed. It will copy it to local storage if needed. + * It should also check the dictionary version to avoid unnecessary copies but this is + * still in TODO state. + * This will make the data from the content provider the cached dictionary for this locale, + * overwriting any previous cached data. + * @returns the name of the file, or null if no data could be obtained. + * @throw FileNotFoundException if the provider returns non-existent data. + * @throw IOException if the provider-returned data could not be read. + */ + public static String getDictionaryFileFromContentProvider(Locale locale, Context context) + throws FileNotFoundException, IOException { + // TODO: check whether the dictionary is the same or not and if it is, return the cached + // file. + final ContentResolver resolver = context.getContentResolver(); + final Uri dictionaryPackUri = getProviderUri(locale); + final InputStream stream = resolver.openInputStream(dictionaryPackUri); + if (null == stream) return null; + return copyFileTo(stream, getCacheFileNameForLocale(locale, context)); + } + + /** + * Accepts a file as dictionary data for some locale and returns the name of a file. + * + * This will make the data in the input file the cached dictionary for this locale, overwriting + * any previous cached data. + */ + public static String getDictionaryFileFromFile(String fileName, Locale locale, + Context context) throws FileNotFoundException, IOException { + return copyFileTo(new FileInputStream(fileName), getCacheFileNameForLocale(locale, + context)); + } + + /** + * Accepts a resource number as dictionary data for some locale and returns the name of a file. + * + * This will make the resource the cached dictionary for this locale, overwriting any previous + * cached data. + */ + public static String getDictionaryFileFromResource(int resource, Locale locale, + Context context) throws FileNotFoundException, IOException { + return copyFileTo(context.getResources().openRawResource(resource), + getCacheFileNameForLocale(locale, context)); + } + + /** + * Copies the data in an input stream to a target file, creating the file if necessary and + * overwriting it if it already exists. + */ + private static String copyFileTo(final InputStream input, final String outputFileName) + throws FileNotFoundException, IOException { + final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE]; + final FileOutputStream output = new FileOutputStream(outputFileName); + for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) + output.write(buffer, 0, readBytes); + input.close(); + return outputFileName; + } +} diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java new file mode 100644 index 000000000..72512c7e1 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.util.Log; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Locale; + +/** + * Helper class to get the address of a mmap'able dictionary file. + */ +class BinaryDictionaryGetter { + + /** + * Used for Log actions from this class + */ + private static final String TAG = BinaryDictionaryGetter.class.getSimpleName(); + + // Prevents this from being instantiated + private BinaryDictionaryGetter() {} + + /** + * Returns a file address from a resource, or null if it cannot be opened. + */ + private static AssetFileAddress loadFallbackResource(Context context, int fallbackResId) { + final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId); + if (afd == null) { + Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId=" + + fallbackResId); + return null; + } + return AssetFileAddress.makeFromFileNameAndOffset( + context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength()); + } + + /** + * Returns a file address for a given locale, trying relevant methods in order. + * + * Tries to get a binary dictionary from various sources, in order: + * - Uses a private method of getting a private dictionary, as implemented by the + * PrivateBinaryDictionaryGetter class. + * If that fails: + * - Uses a content provider to get a public dictionary, as per the protocol described + * in BinaryDictionaryFileDumper. + * If that fails: + * - Gets a file name from the fallback resource passed as an argument. + * If that fails: + * - Returns null. + * @return The address of a valid file, or null. + * @throws FileNotFoundException if a dictionary provider returned a file name, but the + * file cannot be found. + * @throws IOException if there was an I/O problem reading or copying a file. + */ + public static AssetFileAddress getDictionaryFile(Locale locale, Context context, + int fallbackResId) { + // Try first to query a private file signed the same way. + final AssetFileAddress privateFile = + PrivateBinaryDictionaryGetter.getDictionaryFile(locale, context); + if (null != privateFile) { + return privateFile; + } else { + try { + // If that was no-go, try to find a publicly exported dictionary. + final String fileName = BinaryDictionaryFileDumper. + getDictionaryFileFromContentProvider(locale, context); + return AssetFileAddress.makeFromFileName(fileName); + } catch (FileNotFoundException e) { + Log.e(TAG, "Unable to create dictionary file from provider for locale " + + locale.toString() + ": falling back to internal dictionary"); + return loadFallbackResource(context, fallbackResId); + } catch (IOException e) { + Log.e(TAG, "Unable to read source data for locale " + + locale.toString() + ": falling back to internal dictionary"); + return loadFallbackResource(context, fallbackResId); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 9699ad136..5719b9012 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -347,9 +347,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo if (mShowingAddToDictionary && index == 0) { addToDictionary(word); } else { - if (!mSuggestions.mIsApplicationSpecifiedCompletions) { - TextEntryState.acceptedSuggestion(mSuggestions.getWord(0), word); - } mService.pickSuggestionManually(index, word); } } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 74933595c..ac43d6477 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -29,7 +29,7 @@ public abstract class Dictionary { /** * The weight to give to a word if it's length is the same as the number of typed characters. */ - protected static final int FULL_WORD_FREQ_MULTIPLIER = 2; + protected static final int FULL_WORD_SCORE_MULTIPLIER = 2; public static enum DataType { UNIGRAM, BIGRAM @@ -42,17 +42,17 @@ public abstract class Dictionary { public interface WordCallback { /** * Adds a word to a list of suggestions. The word is expected to be ordered based on - * the provided frequency. + * the provided score. * @param word the character array containing the word * @param wordOffset starting offset of the word in the character array * @param wordLength length of valid characters in the character array - * @param frequency the frequency of occurrence. This is normalized between 1 and 255, but + * @param score the score of occurrence. This is normalized between 1 and 255, but * can exceed those limits * @param dicTypeId of the dictionary where word was from * @param dataType tells type of this data * @return true if the word was added, false if no more words are required */ - boolean addWord(char[] word, int wordOffset, int wordLength, int frequency, int dicTypeId, + boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId, DataType dataType); } @@ -61,14 +61,9 @@ public abstract class Dictionary { * words are added through the callback object. * @param composer the key sequence to match * @param callback the callback object to send matched words to as possible candidates - * @param nextLettersFrequencies array of frequencies of next letters that could follow the - * word so far. For instance, "bracke" can be followed by "t", so array['t'] will have - * a non-zero value on returning from this method. - * Pass in null if you don't want the dictionary to look up next letters. * @see WordCallback#addWord(char[], int, int) */ - abstract public void getWords(final WordComposer composer, final WordCallback callback, - int[] nextLettersFrequencies); + abstract public void getWords(final WordComposer composer, final WordCallback callback); /** * Searches for pairs in the bigram dictionary that matches the previous word and all the @@ -76,13 +71,9 @@ public abstract class Dictionary { * @param composer the key sequence to match * @param previousWord the word before * @param callback the callback object to send possible word following previous word - * @param nextLettersFrequencies array of frequencies of next letters that could follow the - * word so far. For instance, "bracke" can be followed by "t", so array['t'] will have - * a non-zero value on returning from this method. - * Pass in null if you don't want the dictionary to look up next letters. */ public void getBigrams(final WordComposer composer, final CharSequence previousWord, - final WordCallback callback, int[] nextLettersFrequencies) { + final WordCallback callback) { // empty base implementation } diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java new file mode 100644 index 000000000..c7864621d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.net.Uri; + +/** + * Takes action to reload the necessary data when a dictionary pack was added/removed. + */ +public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver { + + final LatinIME mService; + + public DictionaryPackInstallBroadcastReceiver(final LatinIME service) { + mService = service; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final PackageManager manager = context.getPackageManager(); + + // We need to reread the dictionary if a new dictionary package is installed. + if (action.equals(Intent.ACTION_PACKAGE_ADDED)) { + final Uri packageUri = intent.getData(); + if (null == packageUri) return; // No package name : we can't do anything + final String packageName = packageUri.getSchemeSpecificPart(); + if (null == packageName) return; + final PackageInfo packageInfo; + try { + packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS); + } catch (android.content.pm.PackageManager.NameNotFoundException e) { + return; // No package info : we can't do anything + } + final ProviderInfo[] providers = packageInfo.providers; + if (null == providers) return; // No providers : it is not a dictionary. + + // Search for some dictionary pack in the just-installed package. If found, reread. + for (ProviderInfo info : providers) { + if (BinaryDictionary.DICTIONARY_PACK_AUTHORITY.equals(info.authority)) { + mService.resetSuggestMainDict(); + return; + } + } + // If we come here none of the authorities matched the one we searched for. + // We can exit safely. + return; + } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) + && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + // When the dictionary package is removed, we need to reread dictionary (to use the + // next-priority one, or stop using a dictionary at all if this was the only one, + // since this is the user request). + // If we are replacing the package, we will receive ADDED right away so no need to + // remove the dictionary at the moment, since we will do it when we receive the + // ADDED broadcast. + + // TODO: Only reload dictionary on REMOVED when the removed package is the one we + // read dictionary from? + mService.resetSuggestMainDict(); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 0fc86c335..d87fbce51 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -37,7 +37,6 @@ public class ExpandableDictionary extends Dictionary { private int mDicTypeId; private int mMaxDepth; private int mInputLength; - private int[] mNextLettersFrequencies; private StringBuilder sb = new StringBuilder(MAX_WORD_LENGTH); private static final char QUOTE = '\''; @@ -191,8 +190,7 @@ public class ExpandableDictionary extends Dictionary { } @Override - public void getWords(final WordComposer codes, final WordCallback callback, - int[] nextLettersFrequencies) { + public void getWords(final WordComposer codes, final WordCallback callback) { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); @@ -201,7 +199,6 @@ public class ExpandableDictionary extends Dictionary { } mInputLength = codes.size(); - mNextLettersFrequencies = nextLettersFrequencies; if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; // Cache the codes so that we don't have to lookup an array list for (int i = 0; i < mInputLength; i++) { @@ -228,11 +225,21 @@ public class ExpandableDictionary extends Dictionary { /** * Returns the word's frequency or -1 if not found */ - public int getWordFrequency(CharSequence word) { + protected int getWordFrequency(CharSequence word) { Node node = searchNode(mRoots, word, 0, word.length()); return (node == null) ? -1 : node.mFrequency; } + private static int computeSkippedWordFinalFreq(int freq, int snr, int inputLength) { + // The computation itself makes sense for >= 2, but the == 2 case returns 0 + // anyway so we may as well test against 3 instead and return the constant + if (inputLength >= 3) { + return (freq * snr * (inputLength - 2)) / (inputLength - 1); + } else { + return 0; + } + } + /** * Recursively traverse the tree for words that match the input. Input consists of * a list of arrays. Each item in the list is one input character position. An input @@ -246,13 +253,14 @@ public class ExpandableDictionary extends Dictionary { * @param completion whether the traversal is now in completion mode - meaning that we've * exhausted the input and we're looking for all possible suffixes. * @param snr current weight of the word being formed - * @param inputIndex position in the input characters. This can be off from the depth in + * @param inputIndex position in the input characters. This can be off from the depth in * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type * "wouldve", it could be matching "would've", so the depth will be one more than the * inputIndex * @param callback the callback class for adding a word */ - protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, + // TODO: Share this routine with the native code for BinaryDictionary + protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, final int depth, boolean completion, int snr, int inputIndex, int skipPos, WordCallback callback) { final int count = roots.mLength; @@ -278,14 +286,15 @@ public class ExpandableDictionary extends Dictionary { if (completion) { word[depth] = c; if (terminal) { - if (!callback.addWord(word, 0, depth + 1, freq * snr, mDicTypeId, - DataType.UNIGRAM)) { - return; + final int finalFreq; + if (skipPos < 0) { + finalFreq = freq * snr; + } else { + finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength); } - // Add to frequency of next letters for predictive correction - if (mNextLettersFrequencies != null && depth >= inputIndex && skipPos < 0 - && mNextLettersFrequencies.length > word[inputIndex]) { - mNextLettersFrequencies[word[inputIndex]]++; + if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, + DataType.UNIGRAM)) { + return; } } if (children != null) { @@ -296,7 +305,7 @@ public class ExpandableDictionary extends Dictionary { // Skip the ' and continue deeper word[depth] = c; if (children != null) { - getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, + getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, skipPos, callback); } } else { @@ -313,10 +322,16 @@ public class ExpandableDictionary extends Dictionary { if (codeSize == inputIndex + 1) { if (terminal) { - if (INCLUDE_TYPED_WORD_IF_VALID + if (INCLUDE_TYPED_WORD_IF_VALID || !same(word, depth + 1, codes.getTypedWord())) { - int finalFreq = freq * snr * addedAttenuation; - if (skipPos < 0) finalFreq *= FULL_WORD_FREQ_MULTIPLIER; + final int finalFreq; + if (skipPos < 0) { + finalFreq = freq * snr * addedAttenuation + * FULL_WORD_SCORE_MULTIPLIER; + } else { + finalFreq = computeSkippedWordFinalFreq(freq, + snr * addedAttenuation, mInputLength); + } callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, DataType.UNIGRAM); } @@ -327,7 +342,7 @@ public class ExpandableDictionary extends Dictionary { skipPos, callback); } } else if (children != null) { - getWordsRec(children, codes, word, depth + 1, + getWordsRec(children, codes, word, depth + 1, false, snr * addedAttenuation, inputIndex + 1, skipPos, callback); } @@ -427,7 +442,7 @@ public class ExpandableDictionary extends Dictionary { @Override public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback, int[] nextLettersFrequencies) { + final WordCallback callback) { if (!reloadDictionaryIfRequired()) { runReverseLookUp(previousWord, callback); } @@ -516,7 +531,7 @@ public class ExpandableDictionary extends Dictionary { } } - static char toLowerCase(char c) { + private static char toLowerCase(char c) { char baseChar = c; if (c < BASE_CHARS.length) { baseChar = BASE_CHARS[c]; @@ -535,7 +550,7 @@ public class ExpandableDictionary extends Dictionary { * if c is not a combined character, or the base character if it * is combined. */ - static final char BASE_CHARS[] = { + private static final char BASE_CHARS[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java index a9f2c2c22..be5e015aa 100644 --- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java @@ -106,8 +106,8 @@ public class InputLanguageSelection extends PreferenceActivity { conf.locale = locale; res.updateConfiguration(conf, res.getDisplayMetrics()); - int mainDicResId = LatinIME.getMainDictionaryResourceId(res); - BinaryDictionary bd = BinaryDictionary.initDictionary(this, mainDicResId, Suggest.DIC_MAIN); + BinaryDictionary bd = BinaryDictionary.initDictionaryFromManager(this, Suggest.DIC_MAIN, + locale, Utils.getMainDictionaryResourceId(res)); // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of // 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index acbccf6e6..ae2315437 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,16 +16,21 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.compat.InputMethodSubtype; +import com.android.inputmethod.compat.CompatUtils; +import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.compat.InputConnectionCompatUtils; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodServiceCompatWrapper; +import com.android.inputmethod.compat.InputTypeCompatUtils; +import com.android.inputmethod.compat.VibratorCompatWrapper; +import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboardView; import com.android.inputmethod.latin.Utils.RingCharBuffer; -import com.android.inputmethod.voice.VoiceIMEConnector; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -41,9 +46,9 @@ import android.media.AudioManager; import android.net.ConnectivityManager; import android.os.Debug; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.SystemClock; -import android.os.Vibrator; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.text.InputType; @@ -62,13 +67,10 @@ import android.view.ViewParent; 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.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -// @@@ import android.view.inputmethod.InputMethodSubtype; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; @@ -82,13 +84,35 @@ import java.util.Locale; /** * Input method implementation for Qwerty'ish keyboard. */ -public class LatinIME extends InputMethodService implements KeyboardActionListener, - SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = "LatinIME"; +public class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener { + private static final String TAG = LatinIME.class.getSimpleName(); private static final boolean PERF_DEBUG = false; private static final boolean TRACE = false; private static boolean DEBUG = LatinImeLogger.sDBG; + /** + * The private IME option used to indicate that no microphone should be + * shown for a given text field. For instance, this is specified by the + * search dialog when the dialog is already showing a voice search button. + * + * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed. + */ + @SuppressWarnings("dep-ann") + public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm"; + + /** + * The private IME option used to indicate that no microphone should be + * shown for a given text field. For instance, this is specified by the + * search dialog when the dialog is already showing a voice search button. + */ + public static final String IME_OPTION_NO_MICROPHONE = "noMicrophoneKey"; + + /** + * The private IME option used to indicate that no settings key should be + * shown for a given text field. + */ + public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey"; + private static final int DELAY_UPDATE_SUGGESTIONS = 180; private static final int DELAY_UPDATE_OLD_SUGGESTIONS = 300; private static final int DELAY_UPDATE_SHIFT_STATE = 300; @@ -99,6 +123,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Key events coming any faster than this are long-presses. private static final int QUICK_PRESS = 200; + /** + * The name of the scheme used by the Package Manager to warn of a new package installation, + * replacement or removal. + */ + private static final String SCHEME_PACKAGE = "package"; + private int mSuggestionVisibility; private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE = R.string.prefs_suggestion_visibility_show_value; @@ -120,13 +150,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private AlertDialog mOptionsDialog; - private InputMethodManager mImm; + private InputMethodManagerCompatWrapper mImm; private Resources mResources; private SharedPreferences mPrefs; private String mInputMethodId; private KeyboardSwitcher mKeyboardSwitcher; private SubtypeSwitcher mSubtypeSwitcher; - private VoiceIMEConnector mVoiceConnector; + private VoiceProxy mVoiceProxy; private UserDictionary mUserDictionary; private UserBigramDictionary mUserBigramDictionary; @@ -139,6 +169,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mIsSettingsSuggestionStripOn; private boolean mApplicationSpecifiedCompletionOn; + private AccessibilityUtils mAccessibilityUtils; + private final StringBuilder mComposing = new StringBuilder(); private WordComposer mWord = new WordComposer(); private CharSequence mBestWord; @@ -146,7 +178,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mHasDictionary; private boolean mJustAddedAutoSpace; private boolean mAutoCorrectEnabled; - private boolean mReCorrectionEnabled; + private boolean mRecorrectionEnabled; private boolean mBigramSuggestionEnabled; private boolean mAutoCorrectOn; private boolean mVibrateOn; @@ -159,6 +191,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mConfigDelayBeforeFadeoutLanguageOnSpacebar; private int mConfigDurationOfFadeoutLanguageOnSpacebar; private float mConfigFinalFadeoutFactorOfLanguageOnSpacebar; + private long mConfigDoubleSpacesTurnIntoPeriodTimeout; private int mCorrectionMode; private int mCommittedLength; @@ -170,7 +203,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Indicates whether the suggestion strip is to be on in landscape private boolean mJustAccepted; - private boolean mJustReverted; private int mDeleteCount; private long mLastKeyTime; @@ -182,24 +214,37 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen /* package */ String mWordSeparators; private String mSentenceSeparators; private String mSuggestPuncs; - // TODO: Move this flag to VoiceIMEConnector + // TODO: Move this flag to VoiceProxy private boolean mConfigurationChanging; + // Object for reacting to adding/removing a dictionary pack. + private BroadcastReceiver mDictionaryPackInstallReceiver = + new DictionaryPackInstallBroadcastReceiver(this); + // Keeps track of most recently inserted text (multi-character key) for reverting private CharSequence mEnteredText; - private boolean mRefreshKeyboardRequired; private final ArrayList mWordHistory = new ArrayList(); - public abstract static class WordAlternatives { - protected CharSequence mChosenWord; + public class WordAlternatives { + private final CharSequence mChosenWord; + private final WordComposer mWordComposer; - public WordAlternatives() { - // Nothing + public WordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { + mChosenWord = chosenWord; + mWordComposer = wordComposer; } - public WordAlternatives(CharSequence chosenWord) { - mChosenWord = chosenWord; + public CharSequence getChosenWord() { + return mChosenWord; + } + + public CharSequence getOriginalWord() { + return mWordComposer.getTypedWord(); + } + + public SuggestedWords.Builder getAlternatives() { + return getTypedSuggestions(mWordComposer); } @Override @@ -207,35 +252,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mChosenWord.hashCode(); } - public abstract CharSequence getOriginalWord(); - - public CharSequence getChosenWord() { - return mChosenWord; - } - - public abstract SuggestedWords.Builder getAlternatives(); - } - - public class TypedWordAlternatives extends WordAlternatives { - private WordComposer word; - - public TypedWordAlternatives() { - // Nothing - } - - public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { - super(chosenWord); - word = wordComposer; - } - @Override - public CharSequence getOriginalWord() { - return word.getTypedWord(); - } - - @Override - public SuggestedWords.Builder getAlternatives() { - return getTypedSuggestions(word); + public boolean equals(Object o) { + return o instanceof CharSequence && TextUtils.equals(mChosenWord, (CharSequence)o); } } @@ -248,6 +267,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_VOICE_RESULTS = 3; private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 4; private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5; + private static final int MSG_SPACE_TYPED = 6; @Override public void handleMessage(Message msg) { @@ -264,7 +284,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switcher.updateShiftState(); break; case MSG_VOICE_RESULTS: - mVoiceConnector.handleVoiceResults(preferCapitalization() + mVoiceProxy.handleVoiceResults(preferCapitalization() || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked())); break; case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: @@ -324,7 +344,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); final LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); if (inputView != null) { - final LatinKeyboard keyboard = inputView.getLatinKeyboard(); + final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); // The language is never displayed when the delay is zero. if (mConfigDelayBeforeFadeoutLanguageOnSpacebar != 0) inputView.setSpacebarTextFadeFactor(localeChanged ? 1.0f @@ -336,6 +356,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } + + public void startDoubleSpacesTimer() { + removeMessages(MSG_SPACE_TYPED); + sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), + mConfigDoubleSpacesTurnIntoPeriodTimeout); + } + + public void cancelDoubleSpacesTimer() { + removeMessages(MSG_SPACE_TYPED); + } + + public boolean isAcceptingDoubleSpaces() { + return hasMessages(MSG_SPACE_TYPED); + } } @Override @@ -345,13 +379,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.init(this, prefs); SubtypeSwitcher.init(this, prefs); KeyboardSwitcher.init(this, prefs); + AccessibilityUtils.init(this, prefs); super.onCreate(); - mImm = ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)); - mInputMethodId = Utils.getInputMethodId(mImm, getApplicationInfo().packageName); + mImm = InputMethodManagerCompatWrapper.getInstance(this); + mInputMethodId = Utils.getInputMethodId(mImm, getPackageName()); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); + mAccessibilityUtils = AccessibilityUtils.getInstance(); final Resources res = getResources(); mResources = res; @@ -359,10 +395,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If the option should not be shown, do not read the recorrection preference // but always use the default setting defined in the resources. if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) { - mReCorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED, + mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED, res.getBoolean(R.bool.config_default_recorrection_enabled)); } else { - mReCorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); + mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); } mConfigEnableShowSubtypeSettings = res.getBoolean( @@ -375,6 +411,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen R.integer.config_duration_of_fadeout_language_on_spacebar); mConfigFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger( R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f; + mConfigDoubleSpacesTurnIntoPeriodTimeout = res.getInteger( + R.integer.config_double_spaces_turn_into_period_timeout); Utils.GCUtils.getInstance().reset(); boolean tryGC = true; @@ -390,30 +428,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mOrientation = res.getConfiguration().orientation; initSuggestPuncList(); - // register to receive ringer mode change and network state change. + // Register to receive ringer mode change and network state change. + // Also receive installation and removal of a dictionary pack. final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(mReceiver, filter); - mVoiceConnector = VoiceIMEConnector.init(this, prefs, mHandler); - prefs.registerOnSharedPreferenceChangeListener(this); - } + mVoiceProxy = VoiceProxy.init(this, prefs, mHandler); - /** - * Returns a main dictionary resource id - * @return main dictionary resource id - */ - public static int getMainDictionaryResourceId(Resources res) { - final String MAIN_DIC_NAME = "main"; - String packageName = LatinIME.class.getPackage().getName(); - return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName); + final IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme(SCHEME_PACKAGE); + registerReceiver(mDictionaryPackInstallReceiver, packageFilter); } private void initSuggest() { - updateAutoTextEnabled(); - String locale = mSubtypeSwitcher.getInputLocaleStr(); + final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); + final Locale keyboardLocale = new Locale(localeStr); - Locale savedLocale = mSubtypeSwitcher.changeSystemLocale(new Locale(locale)); + final Locale savedLocale = mSubtypeSwitcher.changeSystemLocale(keyboardLocale); if (mSuggest != null) { mSuggest.close(); } @@ -421,20 +455,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mQuickFixes = isQuickFixesEnabled(prefs); final Resources res = mResources; - int mainDicResId = getMainDictionaryResourceId(res); - mSuggest = new Suggest(this, mainDicResId); + int mainDicResId = Utils.getMainDictionaryResourceId(res); + mSuggest = new Suggest(this, mainDicResId, keyboardLocale); loadAndSetAutoCorrectionThreshold(prefs); + updateAutoTextEnabled(); - mUserDictionary = new UserDictionary(this, locale); + mUserDictionary = new UserDictionary(this, localeStr); mSuggest.setUserDictionary(mUserDictionary); mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS); mSuggest.setContactsDictionary(mContactsDictionary); - mAutoDictionary = new AutoDictionary(this, this, locale, Suggest.DIC_AUTO); + mAutoDictionary = new AutoDictionary(this, this, localeStr, Suggest.DIC_AUTO); mSuggest.setAutoDictionary(mAutoDictionary); - mUserBigramDictionary = new UserBigramDictionary(this, this, locale, Suggest.DIC_USER); + mUserBigramDictionary = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER); mSuggest.setUserBigramDictionary(mUserBigramDictionary); updateCorrectionMode(); @@ -444,6 +479,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher.changeSystemLocale(savedLocale); } + /* package private */ void resetSuggestMainDict() { + final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); + final Locale keyboardLocale = new Locale(localeStr); + int mainDicResId = Utils.getMainDictionaryResourceId(mResources); + mSuggest.resetMainDict(this, mainDicResId, keyboardLocale); + } + @Override public void onDestroy() { if (mSuggest != null) { @@ -451,7 +493,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggest = null; } unregisterReceiver(mReceiver); - mVoiceConnector.destroy(); + unregisterReceiver(mDictionaryPackInstallReceiver); + mVoiceProxy.destroy(); LatinImeLogger.commit(); LatinImeLogger.onDestroy(); super.onDestroy(); @@ -472,7 +515,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConfigurationChanging = true; super.onConfigurationChanged(conf); - mVoiceConnector.onConfigurationChanged(conf); + mVoiceProxy.onConfigurationChanged(conf); mConfigurationChanging = false; } @@ -489,7 +532,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (container.getPaddingRight() != 0) { HorizontalScrollView scrollView = (HorizontalScrollView) container.findViewById(R.id.candidates_scroll_view); - // @@@ scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER); + setOverScrollModeNever(scrollView); container.setGravity(Gravity.CENTER_HORIZONTAL); } mCandidateView = (CandidateView) container.findViewById(R.id.candidates); @@ -498,17 +541,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return container; } - private static boolean isPasswordVariation(int variation) { - return variation == InputType.TYPE_TEXT_VARIATION_PASSWORD - || variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; - // @@@ || variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD; - } - - private static boolean isEmailVariation(int variation) { - return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; - // @@@ || variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; - } - @Override public void onStartInputView(EditorInfo attribute, boolean restarting) { final KeyboardSwitcher switcher = mKeyboardSwitcher; @@ -524,20 +556,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher.updateParametersOnStartInputView(); - if (mRefreshKeyboardRequired) { - mRefreshKeyboardRequired = false; - onRefreshKeyboard(); - } - - TextEntryState.newSession(this); + TextEntryState.reset(); // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to // know now whether this is a password text field, because we need to know now whether we // want to enable the voice button. - mVoiceConnector.resetVoiceStates(isPasswordVariation( - attribute.inputType & InputType.TYPE_MASK_VARIATION)); + final VoiceProxy voiceIme = mVoiceProxy; + voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(attribute.inputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(attribute.inputType)); - final int mode = initializeInputAttributesAndGetMode(attribute.inputType); + initializeInputAttributes(attribute); inputView.closing(); mEnteredText = null; @@ -548,9 +576,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen loadSettings(attribute); if (mSubtypeSwitcher.isKeyboardMode()) { - switcher.loadKeyboard(mode, attribute.imeOptions, - mVoiceConnector.isVoiceButtonEnabled(), - mVoiceConnector.isVoiceButtonOnPrimary()); + switcher.loadKeyboard(attribute, + mSubtypeSwitcher.isShortcutImeEnabled() && voiceIme.isVoiceButtonEnabled(), + voiceIme.isVoiceButtonOnPrimary()); switcher.updateShiftState(); } @@ -561,18 +589,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen updateCorrectionMode(); + final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled(); + inputView.setPreviewEnabled(mPopupOn); inputView.setProximityCorrectionEnabled(true); + inputView.setAccessibilityEnabled(accessibilityEnabled); // If we just entered a text field, maybe it has some old text that requires correction - checkReCorrectionOnStart(); + checkRecorrectionOnStart(); inputView.setForeground(true); - mVoiceConnector.onStartInputView(inputView.getWindowToken()); + voiceIme.onStartInputView(inputView.getWindowToken()); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } - private int initializeInputAttributesAndGetMode(int inputType) { + private void initializeInputAttributes(EditorInfo attribute) { + if (attribute == null) + return; + final int inputType = attribute.inputType; final int variation = inputType & InputType.TYPE_MASK_VARIATION; mAutoSpace = false; mInputTypeNoAutoCorrect = false; @@ -580,73 +614,52 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mApplicationSpecifiedCompletionOn = false; mApplicationSpecifiedCompletions = null; - final int mode; - switch (inputType & InputType.TYPE_MASK_CLASS) { - case InputType.TYPE_CLASS_NUMBER: - case InputType.TYPE_CLASS_DATETIME: - mode = KeyboardId.MODE_NUMBER; - break; - case InputType.TYPE_CLASS_PHONE: - mode = KeyboardId.MODE_PHONE; - break; - case InputType.TYPE_CLASS_TEXT: - mIsSettingsSuggestionStripOn = true; - // Make sure that passwords are not displayed in candidate view - if (isPasswordVariation(variation)) { - mIsSettingsSuggestionStripOn = false; - } - if (isEmailVariation(variation) - || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { - mAutoSpace = false; - } else { - mAutoSpace = true; - } - if (isEmailVariation(variation)) { - mIsSettingsSuggestionStripOn = false; - mode = KeyboardId.MODE_EMAIL; - } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { - mIsSettingsSuggestionStripOn = false; - mode = KeyboardId.MODE_URL; - } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { - mode = KeyboardId.MODE_IM; - } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { - mIsSettingsSuggestionStripOn = false; - mode = KeyboardId.MODE_TEXT; - } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { - mode = KeyboardId.MODE_WEB; - // If it's a browser edit field and auto correct is not ON explicitly, then - // disable auto correction, but keep suggestions on. - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { - mInputTypeNoAutoCorrect = true; - } - } else { - mode = KeyboardId.MODE_TEXT; + if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) { + mIsSettingsSuggestionStripOn = true; + // Make sure that passwords are not displayed in candidate view + if (InputTypeCompatUtils.isPasswordInputType(inputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) { + mIsSettingsSuggestionStripOn = false; + } + if (InputTypeCompatUtils.isEmailVariation(variation) + || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { + mAutoSpace = false; + } else { + mAutoSpace = true; + } + if (InputTypeCompatUtils.isEmailVariation(variation)) { + mIsSettingsSuggestionStripOn = false; + } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { + mIsSettingsSuggestionStripOn = false; + } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { + mIsSettingsSuggestionStripOn = false; + } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { + // If it's a browser edit field and auto correct is not ON explicitly, then + // disable auto correction, but keep suggestions on. + if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { + mInputTypeNoAutoCorrect = true; } + } - // If NO_SUGGESTIONS is set, don't do prediction. - if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) { - mIsSettingsSuggestionStripOn = false; - mInputTypeNoAutoCorrect = true; - } - // If it's not multiline and the autoCorrect flag is not set, then don't correct - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 && - (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) { - mInputTypeNoAutoCorrect = true; - } - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { - mIsSettingsSuggestionStripOn = false; - mApplicationSpecifiedCompletionOn = isFullscreenMode(); - } - break; - default: - mode = KeyboardId.MODE_TEXT; - break; + // If NO_SUGGESTIONS is set, don't do prediction. + if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) { + mIsSettingsSuggestionStripOn = false; + mInputTypeNoAutoCorrect = true; + } + // If it's not multiline and the autoCorrect flag is not set, then don't correct + if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 + && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) { + mInputTypeNoAutoCorrect = true; + } + if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { + mIsSettingsSuggestionStripOn = false; + mApplicationSpecifiedCompletionOn = isFullscreenMode(); + } } - return mode; } - private void checkReCorrectionOnStart() { - if (!mReCorrectionEnabled) return; + private void checkRecorrectionOnStart() { + if (!mRecorrectionEnabled) return; final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; @@ -680,7 +693,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.commit(); mKeyboardSwitcher.onAutoCorrectionStateChanged(false); - mVoiceConnector.flushVoiceInputLogs(mConfigurationChanging); + mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging); KeyboardView inputView = mKeyboardSwitcher.getInputView(); if (inputView != null) inputView.closing(); @@ -701,7 +714,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onUpdateExtractedText(int token, ExtractedText text) { super.onUpdateExtractedText(token, text); - mVoiceConnector.showPunctuationHintIfNecessary(); + mVoiceProxy.showPunctuationHintIfNecessary(); } @Override @@ -714,19 +727,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (DEBUG) { Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd + + ", lss=" + mLastSelectionStart + + ", lse=" + mLastSelectionEnd + ", nss=" + newSelStart + ", nse=" + newSelEnd + ", cs=" + candidatesStart + ", ce=" + candidatesEnd); } - mVoiceConnector.setCursorAndSelection(newSelEnd, newSelStart); + mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart); // If the current selection in the text view changes, we should // clear whatever candidate text we have. - if ((((mComposing.length() > 0 && mHasValidSuggestions) - || mVoiceConnector.isVoiceInputHighlighted()) && (newSelStart != candidatesEnd - || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart)) { + final boolean selectionChanged = (newSelStart != candidatesEnd + || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart; + final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1; + if (((mComposing.length() > 0 && mHasValidSuggestions) + || mVoiceProxy.isVoiceInputHighlighted()) + && (selectionChanged || candidatesCleared)) { + if (candidatesCleared) { + // If the composing span has been cleared, save the typed word in the history for + // recorrection before we reset the candidate strip. Then, we'll be able to show + // suggestions for recorrection right away. + saveWordInHistory(mComposing); + } mComposing.setLength(0); mHasValidSuggestions = false; mHandler.postUpdateSuggestions(); @@ -735,17 +759,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (ic != null) { ic.finishComposingText(); } - mVoiceConnector.setVoiceInputHighlighted(false); + mVoiceProxy.setVoiceInputHighlighted(false); } else if (!mHasValidSuggestions && !mJustAccepted) { - switch (TextEntryState.getState()) { - case ACCEPTED_DEFAULT: - TextEntryState.reset(); - // $FALL-THROUGH$ - case SPACE_AFTER_PICKED: + if (TextEntryState.isAcceptedDefault() || TextEntryState.isSpaceAfterPicked()) { + if (TextEntryState.isAcceptedDefault()) + TextEntryState.reset(); mJustAddedAutoSpace = false; // The user moved the cursor. - break; - default: - break; } } mJustAccepted = false; @@ -755,18 +774,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastSelectionStart = newSelStart; mLastSelectionEnd = newSelEnd; - if (mReCorrectionEnabled && isShowingSuggestionsStrip()) { + if (mRecorrectionEnabled && isShowingSuggestionsStrip()) { // Don't look for corrections if the keyboard is not visible if (mKeyboardSwitcher.isInputViewShown()) { // Check if we should go in or out of correction mode. - if (isSuggestionsRequested() && !mJustReverted + if (isSuggestionsRequested() && (candidatesStart == candidatesEnd || newSelStart != oldSelStart - || TextEntryState.isCorrecting()) + || TextEntryState.isRecorrecting()) && (newSelStart < newSelEnd - 1 || !mHasValidSuggestions)) { if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) { mHandler.postUpdateOldSuggestions(); } else { - abortCorrection(false); + abortRecorrection(false); // Show the punctuation suggestions list if the current one is not // and if not showing "Touch again to save". if (mCandidateView != null && !isShowingPunctuationList() @@ -789,7 +808,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ @Override public void onExtractedTextClicked() { - if (mReCorrectionEnabled && isSuggestionsRequested()) return; + if (mRecorrectionEnabled && isSuggestionsRequested()) return; super.onExtractedTextClicked(); } @@ -805,7 +824,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ @Override public void onExtractedCursorMovement(int dx, int dy) { - if (mReCorrectionEnabled && isSuggestionsRequested()) return; + if (mRecorrectionEnabled && isSuggestionsRequested()) return; super.onExtractedCursorMovement(dx, dy); } @@ -820,20 +839,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mOptionsDialog.dismiss(); mOptionsDialog = null; } - mVoiceConnector.hideVoiceWindow(mConfigurationChanging); + mVoiceProxy.hideVoiceWindow(mConfigurationChanging); mWordHistory.clear(); super.hideWindow(); - TextEntryState.endSession(); } @Override public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) { if (DEBUG) { Log.i(TAG, "Received completions:"); - final int count = (applicationSpecifiedCompletions != null) - ? applicationSpecifiedCompletions.length : 0; - for (int i = 0; i < count; i++) { - Log.i(TAG, " #" + i + ": " + applicationSpecifiedCompletions[i]); + if (applicationSpecifiedCompletions != null) { + for (int i = 0; i < applicationSpecifiedCompletions.length; i++) { + Log.i(TAG, " #" + i + ": " + applicationSpecifiedCompletions[i]); + } } } if (mApplicationSpecifiedCompletionOn) { @@ -898,8 +916,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (DEBUG) { Log.d(TAG, "Touchable region " + x + ", " + y + ", " + width + ", " + height); } - //@@@outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; - //@@@outInsets.touchableRegion.set(x, y, width, height); + setTouchableRegionCompat(outInsets, x, y, width, height); } } @@ -964,7 +981,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mCommittedLength = mComposing.length(); TextEntryState.acceptedTyped(mComposing); - addToDictionaries(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED); + addToAutoAndUserBigramDictionaries(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED); } updateSuggestions(); } @@ -1012,7 +1029,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void doubleSpace() { - //if (!mAutoPunctuate) return; if (mCorrectionMode == Suggest.CORRECTION_NONE) return; final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; @@ -1020,13 +1036,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastThree != null && lastThree.length() == 3 && Character.isLetterOrDigit(lastThree.charAt(0)) && lastThree.charAt(1) == Keyboard.CODE_SPACE - && lastThree.charAt(2) == Keyboard.CODE_SPACE) { + && lastThree.charAt(2) == Keyboard.CODE_SPACE + && mHandler.isAcceptingDoubleSpaces()) { + mHandler.cancelDoubleSpacesTimer(); ic.beginBatchEdit(); ic.deleteSurroundingText(2, 0); ic.commitText(". ", 1); ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mJustAddedAutoSpace = true; + } else { + mHandler.startDoubleSpacesTimer(); } } @@ -1106,6 +1126,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mLastKeyTime = when; KeyboardSwitcher switcher = mKeyboardSwitcher; + final boolean accessibilityEnabled = switcher.isAccessibilityEnabled(); final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); switch (primaryCode) { case Keyboard.CODE_DELETE: @@ -1115,12 +1136,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case Keyboard.CODE_SHIFT: // Shift key is handled in onPress() when device has distinct multi-touch panel. - if (!distinctMultiTouch) + if (!distinctMultiTouch || accessibilityEnabled) switcher.toggleShift(); break; case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: // Symbol key is handled in onPress() when device has distinct multi-touch panel. - if (!distinctMultiTouch) + if (!distinctMultiTouch || accessibilityEnabled) switcher.changeKeyboardMode(); break; case Keyboard.CODE_CANCEL: @@ -1158,10 +1179,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isWordSeparator(primaryCode)) { handleSeparator(primaryCode); } else { - handleCharacter(primaryCode, keyCodes); + handleCharacter(primaryCode, keyCodes, x, y); } - // Cancel the just reverted state - mJustReverted = false; } switcher.onKey(primaryCode); // Reset after any single keystroke @@ -1170,10 +1189,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onTextInput(CharSequence text) { - mVoiceConnector.commitVoiceInput(); + mVoiceProxy.commitVoiceInput(); InputConnection ic = getCurrentInputConnection(); if (ic == null) return; - abortCorrection(false); + abortRecorrection(false); ic.beginBatchEdit(); commitTyped(ic); maybeRemovePreviousPeriod(text); @@ -1181,7 +1200,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY); - mJustReverted = false; mJustAddedAutoSpace = false; mEnteredText = text; } @@ -1193,13 +1211,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace() { - if (mVoiceConnector.logAndRevertVoiceInput()) return; + if (mVoiceProxy.logAndRevertVoiceInput()) return; final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ic.beginBatchEdit(); - mVoiceConnector.handleBackspace(); + mVoiceProxy.handleBackspace(); boolean deleteChar = false; if (mHasValidSuggestions) { @@ -1221,7 +1239,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateShiftKeyState(); TextEntryState.backspace(); - if (TextEntryState.getState() == TextEntryState.State.UNDO_COMMIT) { + if (TextEntryState.isUndoCommit()) { revertLastWord(deleteChar); ic.endBatchEdit(); return; @@ -1246,16 +1264,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - mJustReverted = false; ic.endBatchEdit(); } private void handleTab() { final int imeOptions = getCurrentInputEditorInfo().imeOptions; - final int navigationFlags = 0; - // @@@ final int navigationFlags = - // @@@ EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; - if ((imeOptions & navigationFlags) == 0) { + if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) + && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) { sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); return; } @@ -1264,33 +1279,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (ic == null) return; - /* @@@ // True if keyboard is in either chording shift or manual temporary upper case mode. final boolean isManualTemporaryUpperCase = mKeyboardSwitcher.isManualTemporaryUpperCase(); - if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0 + if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) && !isManualTemporaryUpperCase) { + EditorInfoCompatUtils.performEditorActionNext(ic); ic.performEditorAction(EditorInfo.IME_ACTION_NEXT); - } else if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0 + } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions) && isManualTemporaryUpperCase) { - ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS); + EditorInfoCompatUtils.performEditorActionPrevious(ic); } - */ } - private void abortCorrection(boolean force) { - if (force || TextEntryState.isCorrecting()) { - TextEntryState.onAbortCorrection(); + private void abortRecorrection(boolean force) { + if (force || TextEntryState.isRecorrecting()) { + TextEntryState.onAbortRecorrection(); setCandidatesViewShown(isCandidateStripVisible()); getCurrentInputConnection().finishComposingText(); clearSuggestions(); } } - private void handleCharacter(int primaryCode, int[] keyCodes) { - mVoiceConnector.handleCharacter(); + private void handleCharacter(int primaryCode, int[] keyCodes, int x, int y) { + mVoiceProxy.handleCharacter(); - if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isCorrecting()) { - abortCorrection(false); + if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isRecorrecting()) { + abortRecorrection(false); } int code = primaryCode; @@ -1300,6 +1314,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mComposing.setLength(0); saveWordInHistory(mBestWord); mWord.reset(); + clearSuggestions(); } } KeyboardSwitcher switcher = mKeyboardSwitcher; @@ -1327,7 +1342,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWord.setFirstCharCapitalized(true); } mComposing.append((char) code); - mWord.add(code, keyCodes); + mWord.add(code, keyCodes, x, y); InputConnection ic = getCurrentInputConnection(); if (ic != null) { // If it's the first letter, make note of auto-caps state @@ -1346,7 +1361,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleSeparator(int primaryCode) { - mVoiceConnector.handleSeparator(); + mVoiceProxy.handleSeparator(); // Should dismiss the "Touch again to save" message when handling separator if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) { @@ -1358,14 +1373,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); - abortCorrection(false); + abortRecorrection(false); } if (mHasValidSuggestions) { // 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 (mAutoCorrectOn && primaryCode != '\'' && !mJustReverted) { + if (mAutoCorrectOn && primaryCode != '\'') { pickedDefault = pickDefaultSuggestion(); // Picked the suggestion by the space key. We consider this // as "added an auto space". @@ -1384,14 +1399,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Handle the case of ". ." -> " .." with auto-space if necessary // before changing the TextEntryState. - if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED - && primaryCode == Keyboard.CODE_PERIOD) { + if (TextEntryState.isPunctuationAfterAccepted() && primaryCode == Keyboard.CODE_PERIOD) { reswapPeriodAndSpace(); } TextEntryState.typedCharacter((char) primaryCode, true); - if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED - && primaryCode != Keyboard.CODE_ENTER) { + if (TextEntryState.isPunctuationAfterAccepted() && primaryCode != Keyboard.CODE_ENTER) { swapPunctuationAndSpace(); } else if (isSuggestionsRequested() && primaryCode == Keyboard.CODE_SPACE) { doubleSpace(); @@ -1400,13 +1413,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen CharSequence typedWord = mWord.getTypedWord(); TextEntryState.backToAcceptedDefault(typedWord); if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) { - if (ic != null) { - /* @@@ - CorrectionInfo correctionInfo = new CorrectionInfo( - mLastSelectionEnd - typedWord.length(), typedWord, mBestWord); - ic.commitCorrection(correctionInfo); - */ - } + InputConnectionCompatUtils.commitCorrection( + ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord); if (mCandidateView != null) mCandidateView.onAutoCorrectionInverted(mBestWord); } @@ -1420,17 +1428,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void handleClose() { commitTyped(getCurrentInputConnection()); - mVoiceConnector.handleClose(); + mVoiceProxy.handleClose(); requestHideSelf(0); LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); if (inputView != null) inputView.closing(); - TextEntryState.endSession(); } private void saveWordInHistory(CharSequence result) { if (mWord.size() <= 1) { - mWord.reset(); return; } // Skip if result is null. It happens in some edge case. @@ -1440,7 +1446,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Make a copy of the CharSequence, since it is/could be a mutable CharSequence final String resultCopy = result.toString(); - TypedWordAlternatives entry = new TypedWordAlternatives(resultCopy, + WordAlternatives entry = new WordAlternatives(resultCopy, new WordComposer(mWord)); mWordHistory.add(entry); } @@ -1463,7 +1469,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean isCandidateStripVisible() { if (mCandidateView == null) return false; - if (mCandidateView.isShowingAddToDictionaryHint() || TextEntryState.isCorrecting()) + if (mCandidateView.isShowingAddToDictionaryHint() || TextEntryState.isRecorrecting()) return true; if (!isShowingSuggestionsStrip()) return false; @@ -1473,26 +1479,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } public void switchToKeyboardView() { - mHandler.post(new Runnable() { - @Override - public void run() { - if (DEBUG) { - Log.d(TAG, "Switch to keyboard view."); - } - View v = mKeyboardSwitcher.getInputView(); - if (v != null) { - // Confirms that the keyboard view doesn't have parent view. - ViewParent p = v.getParent(); - if (p != null && p instanceof ViewGroup) { - ((ViewGroup) p).removeView(v); - } - setInputView(v); - } - setCandidatesViewShown(isCandidateStripVisible()); - updateInputViewShown(); - mHandler.postUpdateSuggestions(); + if (DEBUG) { + Log.d(TAG, "Switch to keyboard view."); + } + View v = mKeyboardSwitcher.getInputView(); + if (v != null) { + // Confirms that the keyboard view doesn't have parent view. + ViewParent p = v.getParent(); + if (p != null && p instanceof ViewGroup) { + ((ViewGroup) p).removeView(v); } - }); + setInputView(v); + } + setCandidatesViewShown(isCandidateStripVisible()); + updateInputViewShown(); + mHandler.postUpdateSuggestions(); } public void clearSuggestions() { @@ -1500,7 +1501,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } public void setSuggestions(SuggestedWords words) { - if (mVoiceConnector.getAndResetIsShowingHint()) { + if (mVoiceProxy.getAndResetIsShowingHint()) { setCandidatesView(mCandidateViewContainer); } @@ -1514,11 +1515,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } public void updateSuggestions() { - mKeyboardSwitcher.setPreferredLetters(null); - // Check if we have a suggestion engine attached. if ((mSuggest == null || !isSuggestionsRequested()) - && !mVoiceConnector.isVoiceInputHighlighted()) { + && !mVoiceProxy.isVoiceInputHighlighted()) { return; } @@ -1534,36 +1533,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void showCorrections(WordAlternatives alternatives) { - mKeyboardSwitcher.setPreferredLetters(null); SuggestedWords.Builder builder = alternatives.getAlternatives(); builder.setTypedWordValid(false).setHasMinimalSuggestion(false); showSuggestions(builder.build(), alternatives.getOriginalWord()); } private void showSuggestions(WordComposer word) { - // TODO Maybe need better way of retrieving previous word + // TODO: May need a better way of retrieving previous word CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(), mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( mKeyboardSwitcher.getInputView(), word, prevWord); - int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies(); - mKeyboardSwitcher.setPreferredLetters(nextLettersFrequencies); - - boolean correctionAvailable = !mInputTypeNoAutoCorrect && !mJustReverted - && mSuggest.hasAutoCorrection(); + boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); final CharSequence typedWord = word.getTypedWord(); - // If we're in basic correct - final boolean typedWordValid = mSuggest.isValidWord(typedWord) || - (preferCapitalization() - && mSuggest.isValidWord(typedWord.toString().toLowerCase())); + // Here, we want to promote a whitelisted word if exists. + final boolean typedWordValid = AutoCorrection.isValidWordForAutoCorrection( + mSuggest.getUnigramDictionaries(), typedWord, preferCapitalization()); if (mCorrectionMode == Suggest.CORRECTION_FULL || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) { correctionAvailable |= typedWordValid; } // Don't auto-correct words with multiple capital letter correctionAvailable &= !word.isMostlyCaps(); - correctionAvailable &= !TextEntryState.isCorrecting(); + correctionAvailable &= !TextEntryState.isRecorrecting(); // Basically, we update the suggestion strip only when suggestion count > 1. However, // there is an exception: We update the suggestion strip whenever typed word's length @@ -1610,7 +1603,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mJustAccepted = true; pickSuggestion(mBestWord); // Add the word to the auto dictionary if it's not a known word - addToDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED); + addToAutoAndUserBigramDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED); return true; } @@ -1619,9 +1612,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void pickSuggestionManually(int index, CharSequence suggestion) { SuggestedWords suggestions = mCandidateView.getSuggestions(); - mVoiceConnector.flushAndLogAllTextModificationCounters(index, suggestion, mWordSeparators); + mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion, mWordSeparators); - final boolean correcting = TextEntryState.isCorrecting(); + final boolean recorrecting = TextEntryState.isRecorrecting(); InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); @@ -1663,24 +1656,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen pickSuggestion(suggestion); // Add the word to the auto dictionary if it's not a known word if (index == 0) { - addToDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED); + addToAutoAndUserBigramDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED); } else { - addToBigramDictionary(suggestion, 1); + addToOnlyBigramDictionary(suggestion, 1); } LatinImeLogger.logOnManualSuggestion(mComposing.toString(), suggestion.toString(), index, suggestions.mWords); TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion); // Follow it with a space - if (mAutoSpace && !correcting) { + if (mAutoSpace && !recorrecting) { sendSpace(); mJustAddedAutoSpace = true; } - final boolean showingAddToDictionaryHint = index == 0 && mCorrectionMode > 0 - && !mSuggest.isValidWord(suggestion) - && !mSuggest.isValidWord(suggestion.toString().toLowerCase()); + // We should show the hint if the user pressed the first entry AND either: + // - There is no dictionary (we know that because we tried to load it => null != mSuggest + // AND mHasDictionary is false) + // - There is a dictionary and the word is not in it + // Please note that if mSuggest is null, it means that everything is off: suggestion + // and correction, so we shouldn't try to show the hint + // We used to look at mCorrectionMode here, but showing the hint should have nothing + // to do with the autocorrection setting. + final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null + // If there is no dictionary the hint should be shown. + && (!mHasDictionary + // If "suggestion" is not in the dictionary, the hint should be shown. + || !AutoCorrection.isValidWord( + mSuggest.getUnigramDictionaries(), suggestion, true)); - if (!correcting) { + if (!recorrecting) { // Fool the state watcher so that a subsequent backspace will not do a revert, unless // we just did a correction, in which case we need to stay in // TextEntryState.State.PICKED_SUGGESTION state. @@ -1712,13 +1716,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; InputConnection ic = getCurrentInputConnection(); if (ic != null) { - mVoiceConnector.rememberReplacedWord(suggestion, mWordSeparators); + mVoiceProxy.rememberReplacedWord(suggestion, mWordSeparators); ic.commitText(suggestion, 1); } saveWordInHistory(suggestion); mHasValidSuggestions = false; mCommittedLength = suggestion.length(); - switcher.setPreferredLetters(null); } /** @@ -1731,31 +1734,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If we didn't find a match, search for result in typed word history WordComposer foundWord = null; WordAlternatives alternatives = null; + // Search old suggestions to suggest re-corrected suggestions. for (WordAlternatives entry : mWordHistory) { if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) { - if (entry instanceof TypedWordAlternatives) { - foundWord = ((TypedWordAlternatives) entry).word; - } + foundWord = entry.mWordComposer; alternatives = entry; break; } } - // If we didn't find a match, at least suggest corrections. + // If we didn't find a match, at least suggest corrections as re-corrected suggestions. if (foundWord == null - && (mSuggest.isValidWord(touching.mWord) - || mSuggest.isValidWord(touching.mWord.toString().toLowerCase()))) { + && (AutoCorrection.isValidWord( + mSuggest.getUnigramDictionaries(), touching.mWord, true))) { foundWord = new WordComposer(); for (int i = 0; i < touching.mWord.length(); i++) { foundWord.add(touching.mWord.charAt(i), new int[] { touching.mWord.charAt(i) - }); + }, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } foundWord.setFirstCharCapitalized(Character.isUpperCase(touching.mWord.charAt(0))); } // Found a match, show suggestions if (foundWord != null || alternatives != null) { if (alternatives == null) { - alternatives = new TypedWordAlternatives(touching.mWord, foundWord); + alternatives = new WordAlternatives(touching.mWord, foundWord); } showCorrections(alternatives); if (foundWord != null) { @@ -1769,7 +1771,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void setOldSuggestions() { - mVoiceConnector.setShowingVoiceSuggestions(false); + mVoiceProxy.setShowingVoiceSuggestions(false); if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) { return; } @@ -1783,21 +1785,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (touching != null && touching.mWord.length() > 1) { ic.beginBatchEdit(); - if (!mVoiceConnector.applyVoiceAlternatives(touching) + if (!mVoiceProxy.applyVoiceAlternatives(touching) && !applyTypedAlternatives(touching)) { - abortCorrection(true); + abortRecorrection(true); } else { - TextEntryState.selectedForCorrection(); + TextEntryState.selectedForRecorrection(); EditingUtils.underlineWord(ic, touching); } ic.endBatchEdit(); } else { - abortCorrection(true); + abortRecorrection(true); setPunctuationSuggestions(); // Show the punctuation suggestions list } } else { - abortCorrection(true); + abortRecorrection(true); } } @@ -1806,21 +1808,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setCandidatesViewShown(isCandidateStripVisible()); } - private void addToDictionaries(CharSequence suggestion, int frequencyDelta) { + private void addToAutoAndUserBigramDictionaries(CharSequence suggestion, int frequencyDelta) { checkAddToDictionary(suggestion, frequencyDelta, false); } - private void addToBigramDictionary(CharSequence suggestion, int frequencyDelta) { + private void addToOnlyBigramDictionary(CharSequence suggestion, int frequencyDelta) { checkAddToDictionary(suggestion, frequencyDelta, true); } /** * Adds to the UserBigramDictionary and/or AutoDictionary - * @param addToBigramDictionary true if it should be added to bigram dictionary if possible + * @param selectedANotTypedWord true if it should be added to bigram dictionary if possible */ private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta, - boolean addToBigramDictionary) { + boolean selectedANotTypedWord) { if (suggestion == null || suggestion.length() < 1) return; + // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be // adding words in situations where the user or application really didn't // want corrections enabled or learned. @@ -1828,9 +1831,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) { return; } - if (!addToBigramDictionary && mAutoDictionary.isValidWord(suggestion) - || (!mSuggest.isValidWord(suggestion.toString()) - && !mSuggest.isValidWord(suggestion.toString().toLowerCase()))) { + + final boolean selectedATypedWordAndItsInAutoDic = + !selectedANotTypedWord && mAutoDictionary.isValidWord(suggestion); + final boolean isValidWord = AutoCorrection.isValidWord( + mSuggest.getUnigramDictionaries(), suggestion, true); + final boolean needsToAddToAutoDictionary = selectedATypedWordAndItsInAutoDic + || !isValidWord; + if (needsToAddToAutoDictionary) { mAutoDictionary.addWord(suggestion.toString(), frequencyDelta); } @@ -1870,7 +1878,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int length = mComposing.length(); if (!mHasValidSuggestions && length > 0) { final InputConnection ic = getCurrentInputConnection(); - mJustReverted = true; final CharSequence punctuation = ic.getTextBeforeCursor(1, 0); if (deleteChar) ic.deleteSurroundingText(1, 0); int toDelete = mCommittedLength; @@ -1898,7 +1905,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestions(); } else { sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); - mJustReverted = false; } } @@ -1936,28 +1942,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher.toggleLanguage(reset, next); } // Reload keyboard because the current language has been changed. - KeyboardSwitcher switcher = mKeyboardSwitcher; - final EditorInfo attribute = getCurrentInputEditorInfo(); - final int mode = initializeInputAttributesAndGetMode((attribute != null) - ? attribute.inputType : 0); - final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; - switcher.loadKeyboard(mode, imeOptions, mVoiceConnector.isVoiceButtonEnabled(), - mVoiceConnector.isVoiceButtonOnPrimary()); + mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), + mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(), + mVoiceProxy.isVoiceButtonOnPrimary()); initSuggest(); - switcher.updateShiftState(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - mSubtypeSwitcher.onSharedPreferenceChanged(sharedPreferences, key); - if (Settings.PREF_SELECTED_LANGUAGES.equals(key)) { - mRefreshKeyboardRequired = true; - } else if (Settings.PREF_RECORRECTION_ENABLED.equals(key)) { - mReCorrectionEnabled = sharedPreferences.getBoolean( - Settings.PREF_RECORRECTION_ENABLED, - mResources.getBoolean(R.bool.config_default_recorrection_enabled)); - } + mKeyboardSwitcher.updateShiftState(); } @Override @@ -1967,7 +1956,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public void onPress(int primaryCode) { + public void onPress(int primaryCode, boolean withSliding) { if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) { vibrate(); playKeyClick(primaryCode); @@ -1975,25 +1964,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) { - switcher.onPressShift(); + switcher.onPressShift(withSliding); } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { switcher.onPressSymbol(); } else { switcher.onOtherKeyPressed(); } + mAccessibilityUtils.onPress(primaryCode, switcher); } @Override - public void onRelease(int primaryCode) { + public void onRelease(int primaryCode, boolean withSliding) { KeyboardSwitcher switcher = mKeyboardSwitcher; // Reset any drag flags in the keyboard switcher.keyReleased(); final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) { - switcher.onReleaseShift(); + switcher.onReleaseShift(withSliding); } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { switcher.onReleaseSymbol(); } + mAccessibilityUtils.onRelease(primaryCode, switcher); } @@ -2073,6 +2064,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void updateCorrectionMode() { + // TODO: cleanup messy flags mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false; mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes) && !mInputTypeNoAutoCorrect && mHasDictionary; @@ -2124,10 +2116,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void loadSettings(EditorInfo attribute) { // Get the settings preferences final SharedPreferences prefs = mPrefs; - Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); - // @@@ mVibrateOn = vibrator != null && vibrator.hasVibrator() - mVibrateOn = vibrator != null - && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false); + final boolean hasVibrator = VibratorCompatWrapper.getInstance(this).hasVibrator(); + mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false); mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, mResources.getBoolean(R.bool.config_default_sound_enabled)); @@ -2139,7 +2129,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs); loadAndSetAutoCorrectionThreshold(prefs); - mVoiceConnector.loadSettings(attribute, prefs); + mVoiceProxy.loadSettings(attribute, prefs); updateCorrectionMode(); updateAutoTextEnabled(); @@ -2250,13 +2240,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen di.dismiss(); switch (position) { case 0: - Intent intent = new Intent( - android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + Intent intent = CompatUtils.getInputLanguageSelectionIntent( + mInputMethodId, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(android.provider.Settings.EXTRA_INPUT_METHOD_ID, - mInputMethodId); startActivity(intent); break; case 1: @@ -2293,6 +2280,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void showOptionsMenuInternal(CharSequence title, CharSequence[] items, DialogInterface.OnClickListener listener) { + final IBinder windowToken = mKeyboardSwitcher.getInputView().getWindowToken(); + if (windowToken == null) return; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(true); builder.setIcon(R.drawable.ic_dialog_keyboard); @@ -2303,7 +2292,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mOptionsDialog.setCanceledOnTouchOutside(true); Window window = mOptionsDialog.getWindow(); WindowManager.LayoutParams lp = window.getAttributes(); - lp.token = mKeyboardSwitcher.getInputView().getWindowToken(); + lp.token = windowToken; lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; window.setAttributes(lp); window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); @@ -2347,8 +2336,4 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i]; System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total)); } - - public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) { - SubtypeSwitcher.getInstance().updateSubtype(subtype); - } } diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java new file mode 100644 index 000000000..90726b0d8 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; + +import java.util.Locale; + +class PrivateBinaryDictionaryGetter { + private PrivateBinaryDictionaryGetter() {} + public static AssetFileAddress getDictionaryFile(Locale locale, Context context) { + return null; + } +} diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 59c68f208..488ab09c3 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -16,17 +16,17 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.voice.VoiceIMEConnector; -import com.android.inputmethod.voice.VoiceInputLogger; +import com.android.inputmethod.compat.CompatUtils; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.deprecated.VoiceProxy; +import com.android.inputmethod.compat.VibratorCompatWrapper; import android.app.AlertDialog; import android.app.Dialog; import android.app.backup.BackupManager; import android.content.DialogInterface; -import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.os.Vibrator; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; @@ -82,7 +82,7 @@ public class Settings extends PreferenceActivity private AlertDialog mDialog; - private VoiceInputLogger mLogger; + private VoiceProxy.VoiceLoggerWrapper mVoiceLogger; private boolean mOkClicked = false; private String mVoiceModeOff; @@ -111,7 +111,7 @@ public class Settings extends PreferenceActivity mVoiceModeOff = getString(R.string.voice_mode_off); mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff) .equals(mVoiceModeOff)); - mLogger = VoiceInputLogger.getLogger(this); + mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(this); mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS); @@ -134,10 +134,7 @@ public class Settings extends PreferenceActivity generalSettings.removePreference(mVoicePreference); } - Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); - if (vibrator == null - // @@@ || !vibrator.hasVibrator() - ) { + if (!VibratorCompatWrapper.getInstance(this).hasVibrator()) { generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); } @@ -186,7 +183,7 @@ public class Settings extends PreferenceActivity ((PreferenceGroup) findPreference(PREF_PREDICTION_SETTINGS_KEY)) .removePreference(mQuickFixes); } - if (!VoiceIMEConnector.VOICE_INSTALLED + if (!VoiceProxy.VOICE_INSTALLED || !SpeechRecognizer.isRecognitionAvailable(this)) { getPreferenceScreen().removePreference(mVoicePreference); } else { @@ -224,16 +221,9 @@ public class Settings extends PreferenceActivity @Override public boolean onPreferenceClick(Preference pref) { if (pref == mInputLanguageSelection) { - final String action; - if (android.os.Build.VERSION.SDK_INT - >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) { - // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS - // TODO: Can this be a constant instead of literal String constant? - action = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS"; - } else { - action = "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"; - } - startActivity(new Intent(action)); + startActivity(CompatUtils.getInputLanguageSelectionIntent( + Utils.getInputMethodId(InputMethodManagerCompatWrapper.getInstance(this), + getApplicationInfo().packageName), 0)); return true; } return false; @@ -279,10 +269,10 @@ public class Settings extends PreferenceActivity public void onClick(DialogInterface dialog, int whichButton) { if (whichButton == DialogInterface.BUTTON_NEGATIVE) { mVoicePreference.setValue(mVoiceModeOff); - mLogger.settingsWarningDialogCancel(); + mVoiceLogger.settingsWarningDialogCancel(); } else if (whichButton == DialogInterface.BUTTON_POSITIVE) { mOkClicked = true; - mLogger.settingsWarningDialogOk(); + mVoiceLogger.settingsWarningDialogOk(); } updateVoicePreference(); } @@ -313,7 +303,7 @@ public class Settings extends PreferenceActivity AlertDialog dialog = builder.create(); mDialog = dialog; dialog.setOnDismissListener(this); - mLogger.settingsWarningDialogShown(); + mVoiceLogger.settingsWarningDialogShown(); return dialog; default: Log.e(TAG, "unknown dialog " + id); @@ -323,7 +313,7 @@ public class Settings extends PreferenceActivity @Override public void onDismiss(DialogInterface dialog) { - mLogger.settingsWarningDialogDismissed(); + mVoiceLogger.settingsWarningDialogDismissed(); if (!mOkClicked) { // This assumes that onPreferenceClick gets called first, and this if the user // agreed after the warning, we set the mOkClicked value to true. @@ -333,10 +323,6 @@ public class Settings extends PreferenceActivity private void updateVoicePreference() { boolean isChecked = !mVoicePreference.getValue().equals(mVoiceModeOff); - if (isChecked) { - mLogger.voiceInputSettingEnabled(); - } else { - mLogger.voiceInputSettingDisabled(); - } + mVoiceLogger.voiceInputSettingEnabled(isChecked); } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index f45c2b7f8..6610a65ef 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -16,13 +16,12 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.compat.InputMethodSubtype; +import com.android.inputmethod.compat.InputMethodInfoCompatWrapper; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; +import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.LatinKeyboard; -import com.android.inputmethod.keyboard.LatinKeyboardView; -import com.android.inputmethod.voice.SettingsUtil; -import com.android.inputmethod.voice.VoiceIMEConnector; -import com.android.inputmethod.voice.VoiceInput; import android.content.Context; import android.content.Intent; @@ -33,11 +32,10 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.AsyncTask; import android.os.IBinder; import android.text.TextUtils; import android.util.Log; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; import java.util.ArrayList; import java.util.Arrays; @@ -47,7 +45,7 @@ import java.util.Map; public class SubtypeSwitcher { private static boolean DBG = LatinImeLogger.sDBG; - private static final String TAG = "SubtypeSwitcher"; + private static final String TAG = SubtypeSwitcher.class.getSimpleName(); private static final char LOCALE_SEPARATER = '_'; private static final String KEYBOARD_MODE = "keyboard"; @@ -60,26 +58,27 @@ public class SubtypeSwitcher { private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); private /* final */ LatinIME mService; private /* final */ SharedPreferences mPrefs; - private /* final */ InputMethodManager mImm; + private /* final */ InputMethodManagerCompatWrapper mImm; private /* final */ Resources mResources; private /* final */ ConnectivityManager mConnectivityManager; private /* final */ boolean mConfigUseSpacebarLanguageSwitcher; - private final ArrayList mEnabledKeyboardSubtypesOfCurrentInputMethod = - new ArrayList(); + private final ArrayList + mEnabledKeyboardSubtypesOfCurrentInputMethod = + new ArrayList(); private final ArrayList mEnabledLanguagesOfCurrentInputMethod = new ArrayList(); /*-----------------------------------------------------------*/ // Variants which should be changed only by reload functions. private boolean mNeedsToDisplayLanguage; private boolean mIsSystemLanguageSameAsInputLanguage; - private InputMethodInfo mShortcutInputMethodInfo; - private InputMethodSubtype mShortcutSubtype; - private List mAllEnabledSubtypesOfCurrentInputMethod; + private InputMethodInfoCompatWrapper mShortcutInputMethodInfo; + private InputMethodSubtypeCompatWrapper mShortcutSubtype; + private List mAllEnabledSubtypesOfCurrentInputMethod; + private InputMethodSubtypeCompatWrapper mCurrentSubtype; private Locale mSystemLocale; private Locale mInputLocale; private String mInputLocaleStr; - private String mMode; - private VoiceInput mVoiceInput; + private VoiceProxy.VoiceInputWrapper mVoiceInputWrapper; /*-----------------------------------------------------------*/ private boolean mIsNetworkConnected; @@ -103,7 +102,7 @@ public class SubtypeSwitcher { mService = service; mPrefs = prefs; mResources = service.getResources(); - mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE); + mImm = InputMethodManagerCompatWrapper.getInstance(service); mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mEnabledKeyboardSubtypesOfCurrentInputMethod.clear(); @@ -111,11 +110,10 @@ public class SubtypeSwitcher { mSystemLocale = null; mInputLocale = null; mInputLocaleStr = null; - // Mode is initialized to KEYBOARD_MODE, in case that LatinIME can't obtain currentSubtype - mMode = KEYBOARD_MODE; + mCurrentSubtype = null; mAllEnabledSubtypesOfCurrentInputMethod = null; // TODO: Voice input should be created here - mVoiceInput = null; + mVoiceInputWrapper = null; mConfigUseSpacebarLanguageSwitcher = mResources.getBoolean( R.bool.config_use_spacebar_language_switcher); if (mConfigUseSpacebarLanguageSwitcher) @@ -129,7 +127,7 @@ public class SubtypeSwitcher { // Only configuration changed event is allowed to call this because this is heavy. private void updateAllParameters() { mSystemLocale = mResources.getConfiguration().locale; - // @@@ updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(mImm.getCurrentInputMethodSubtype()); updateParametersOnStartInputView(); } @@ -146,19 +144,20 @@ public class SubtypeSwitcher { // Reload enabledSubtypes from the framework. private void updateEnabledSubtypes() { + final String currentMode = getCurrentSubtypeMode(); boolean foundCurrentSubtypeBecameDisabled = true; - // @@@ mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList( - // null, true); + mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList( + null, true); mEnabledLanguagesOfCurrentInputMethod.clear(); mEnabledKeyboardSubtypesOfCurrentInputMethod.clear(); - for (InputMethodSubtype ims: mAllEnabledSubtypesOfCurrentInputMethod) { + for (InputMethodSubtypeCompatWrapper ims: mAllEnabledSubtypesOfCurrentInputMethod) { final String locale = ims.getLocale(); final String mode = ims.getMode(); mLocaleSplitter.setString(locale); if (mLocaleSplitter.hasNext()) { mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next()); } - if (locale.equals(mInputLocaleStr) && mode.equals(mMode)) { + if (locale.equals(mInputLocaleStr) && mode.equals(currentMode)) { foundCurrentSubtypeBecameDisabled = false; } if (KEYBOARD_MODE.equals(ims.getMode())) { @@ -169,10 +168,10 @@ public class SubtypeSwitcher { && mIsSystemLanguageSameAsInputLanguage); if (foundCurrentSubtypeBecameDisabled) { if (DBG) { - Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + mMode); + Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + currentMode); Log.w(TAG, "Last subtype was disabled. Update to the current one."); } - // @@@ updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(mImm.getCurrentInputMethodSubtype()); } } @@ -185,11 +184,10 @@ public class SubtypeSwitcher { + ", " + mShortcutSubtype.getMode()))); } // TODO: Update an icon for shortcut IME - /* - Map> shortcuts = + final Map> shortcuts = mImm.getShortcutInputMethodsAndSubtypes(); - for (InputMethodInfo imi: shortcuts.keySet()) { - List subtypes = shortcuts.get(imi); + for (InputMethodInfoCompatWrapper imi : shortcuts.keySet()) { + List subtypes = shortcuts.get(imi); // TODO: Returns the first found IMI for now. Should handle all shortcuts as // appropriate. mShortcutInputMethodInfo = imi; @@ -197,17 +195,24 @@ public class SubtypeSwitcher { // as appropriate. mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; break; - } - */ + } + if (DBG) { + Log.d(TAG, "Update shortcut IME to : " + + (mShortcutInputMethodInfo == null + ? "" : mShortcutInputMethodInfo.getId()) + ", " + + (mShortcutSubtype == null ? "" : (mShortcutSubtype.getLocale() + + ", " + mShortcutSubtype.getMode()))); + } } // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. - public void updateSubtype(InputMethodSubtype newSubtype) { + public void updateSubtype(InputMethodSubtypeCompatWrapper newSubtype) { final String newLocale; final String newMode; - if (newSubtype == null) { + final String oldMode = getCurrentSubtypeMode(); + if (newSubtype == null || newSubtype.getOriginalObject() == null) { // Normally, newSubtype shouldn't be null. But just in case newSubtype was null, - // fallback to the default locale and mode. + // fallback to the default locale. Log.w(TAG, "Couldn't get the current subtype."); newLocale = "en_US"; newMode = KEYBOARD_MODE; @@ -217,7 +222,7 @@ public class SubtypeSwitcher { } if (DBG) { Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode - + ", from: " + mInputLocaleStr + ", " + mMode); + + ", from: " + mInputLocaleStr + ", " + oldMode); } boolean languageChanged = false; if (!newLocale.equals(mInputLocaleStr)) { @@ -227,42 +232,41 @@ public class SubtypeSwitcher { updateInputLocale(newLocale); } boolean modeChanged = false; - String oldMode = mMode; - if (!newMode.equals(mMode)) { - if (mMode != null) { + if (!newMode.equals(oldMode)) { + if (oldMode != null) { modeChanged = true; } - mMode = newMode; } + mCurrentSubtype = newSubtype; // If the old mode is voice input, we need to reset or cancel its status. // We cancel its status when we change mode, while we reset otherwise. if (isKeyboardMode()) { if (modeChanged) { - if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) { - mVoiceInput.cancel(); + if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) { + mVoiceInputWrapper.cancel(); } } if (modeChanged || languageChanged) { updateShortcutIME(); mService.onRefreshKeyboard(); } - } else if (isVoiceMode() && mVoiceInput != null) { + } else if (isVoiceMode() && mVoiceInputWrapper != null) { if (VOICE_MODE.equals(oldMode)) { - mVoiceInput.reset(); + mVoiceInputWrapper.reset(); } // If needsToShowWarningDialog is true, voice input need to show warning before // show recognition view. if (languageChanged || modeChanged - || VoiceIMEConnector.getInstance().needsToShowWarningDialog()) { + || VoiceProxy.getInstance().needsToShowWarningDialog()) { triggerVoiceIME(); } } else { - Log.w(TAG, "Unknown subtype mode: " + mMode); - if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) { + Log.w(TAG, "Unknown subtype mode: " + newMode); + if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) { // We need to reset the voice input to release the resources and to reset its status // as it is not the current input mode. - mVoiceInput.reset(); + mVoiceInputWrapper.reset(); } } } @@ -303,15 +307,34 @@ public class SubtypeSwitcher { if (token == null || mShortcutInputMethodInfo == null) { return; } - // @@@ mImm.setInputMethodAndSubtype(token, mShortcutInfo.getId(), mShortcutSubtype); + final String imiId = mShortcutInputMethodInfo.getId(); + final InputMethodSubtypeCompatWrapper subtype = mShortcutSubtype; + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + mImm.setInputMethodAndSubtype(token, imiId, subtype); + return null; + } + + @Override + protected void onPostExecute(Void result) { + // Calls in this method need to be done in the same thread as the thread which + // called switchToShortcutIME(). + + // Notify an event that the current subtype was changed. This event will be + // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented + // when the API level is 10 or previous. + mService.notifyOnCurrentInputMethodSubtypeChanged(subtype); + } + }.execute(); } public Drawable getShortcutIcon() { return getSubtypeIcon(mShortcutInputMethodInfo, mShortcutSubtype); } - private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) { - /* + private Drawable getSubtypeIcon( + InputMethodInfoCompatWrapper imi, InputMethodSubtypeCompatWrapper subtype) { final PackageManager pm = mService.getPackageManager(); if (imi != null) { final String imiPackageName = imi.getPackageName(); @@ -333,8 +356,7 @@ public class SubtypeSwitcher { Log.w(TAG, "IME can't be found: " + imiPackageName); } } - } - */ + } return null; } @@ -346,11 +368,28 @@ public class SubtypeSwitcher { return false; } - public boolean isShortcutAvailable() { + public boolean isShortcutImeEnabled() { if (mShortcutInputMethodInfo == null) return false; - if (mShortcutSubtype != null && contains(mShortcutSubtype.getExtraValue().split(","), - SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY)) { + if (mShortcutSubtype == null) + return true; + final boolean allowsImplicitlySelectedSubtypes = true; + for (final InputMethodSubtypeCompatWrapper enabledSubtype : + mImm.getEnabledInputMethodSubtypeList( + mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) { + if (enabledSubtype.equals(mShortcutSubtype)) + return true; + } + return false; + } + + public boolean isShortcutImeReady() { + if (mShortcutInputMethodInfo == null) + return false; + if (mShortcutSubtype == null) + return true; + if (contains(mShortcutSubtype.getExtraValue().split(","), + SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY)) { return mIsNetworkConnected; } return true; @@ -361,12 +400,10 @@ public class SubtypeSwitcher { ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); mIsNetworkConnected = !noConnection; - final LatinKeyboardView inputView = KeyboardSwitcher.getInstance().getInputView(); - if (inputView != null) { - final LatinKeyboard keyboard = inputView.getLatinKeyboard(); - if (keyboard != null) { - keyboard.updateShortcutKey(isShortcutAvailable(), inputView); - } + final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); + final LatinKeyboard keyboard = switcher.getLatinKeyboard(); + if (keyboard != null) { + keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getInputView()); } } @@ -462,14 +499,6 @@ public class SubtypeSwitcher { } } - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (mConfigUseSpacebarLanguageSwitcher) { - if (Settings.PREF_SELECTED_LANGUAGES.equals(key)) { - mLanguageSwitcher.loadLocales(sharedPreferences); - } - } - } - /** * Change system locale for this application * @param newLocale @@ -484,7 +513,7 @@ public class SubtypeSwitcher { } public boolean isKeyboardMode() { - return KEYBOARD_MODE.equals(mMode); + return KEYBOARD_MODE.equals(getCurrentSubtypeMode()); } @@ -492,9 +521,9 @@ public class SubtypeSwitcher { // Voice Input functions // /////////////////////////// - public boolean setVoiceInput(VoiceInput vi) { - if (mVoiceInput == null && vi != null) { - mVoiceInput = vi; + public boolean setVoiceInputWrapper(VoiceProxy.VoiceInputWrapper vi) { + if (mVoiceInputWrapper == null && vi != null) { + mVoiceInputWrapper = vi; if (isVoiceMode()) { if (DBG) { Log.d(TAG, "Set and call voice input.: " + getInputLocaleStr()); @@ -507,12 +536,12 @@ public class SubtypeSwitcher { } public boolean isVoiceMode() { - return VOICE_MODE.equals(mMode); + return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode()); } private void triggerVoiceIME() { if (!mService.isInputViewShown()) return; - VoiceIMEConnector.getInstance().startListening(false, + VoiceProxy.getInstance().startListening(false, KeyboardSwitcher.getInstance().getInputView().getWindowToken()); } @@ -573,19 +602,29 @@ public class SubtypeSwitcher { } } - // A list of locales which are supported by default for voice input, unless we get a - // different list from Gservices. - private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES = - "en " + - "en_US " + - "en_GB " + - "en_AU " + - "en_CA " + - "en_IE " + - "en_IN " + - "en_NZ " + - "en_SG " + - "en_ZA "; + ///////////////////////////// + // Other utility functions // + ///////////////////////////// + + public String getCurrentSubtypeExtraValue() { + // If null, return what an empty ExtraValue would return : the empty string. + return null != mCurrentSubtype ? mCurrentSubtype.getExtraValue() : ""; + } + + public boolean currentSubtypeContainsExtraValueKey(String key) { + // If null, return what an empty ExtraValue would return : false. + return null != mCurrentSubtype ? mCurrentSubtype.containsExtraValueKey(key) : false; + } + + public String getCurrentSubtypeExtraValueOf(String key) { + // If null, return what an empty ExtraValue would return : null. + return null != mCurrentSubtype ? mCurrentSubtype.getExtraValueOf(key) : null; + } + + public String getCurrentSubtypeMode() { + return null != mCurrentSubtype ? mCurrentSubtype.getMode() : KEYBOARD_MODE; + } + public boolean isVoiceSupported(String locale) { // Get the current list of supported locales and check the current locale against that @@ -593,10 +632,8 @@ public class SubtypeSwitcher { // input. Because this method is called by onStartInputView, this should mean that as // long as the locale doesn't change while the user is keeping the IME open, the // value should never be stale. - String supportedLocalesString = SettingsUtil.getSettingsString( - mService.getContentResolver(), - SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES, - DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES); + String supportedLocalesString = VoiceProxy.getSupportedLocalesString( + mService.getContentResolver()); List voiceInputSupportedLocales = Arrays.asList( supportedLocalesString.split("\\s+")); return voiceInputSupportedLocales.contains(locale); diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index c9e57d0a5..0cc9d4198 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -25,6 +25,11 @@ import android.view.View; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; /** * This class loads a dictionary and provides a list of suggestions for a given sequence of @@ -43,7 +48,7 @@ public class Suggest implements Dictionary.WordCallback { /** * Words that appear in both bigram and unigram data gets multiplier ranging from - * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the frequency score from + * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the score from * bigram data. */ public static final double BIGRAM_MULTIPLIER_MIN = 1.2; @@ -63,19 +68,23 @@ public class Suggest implements Dictionary.WordCallback { // If you add a type of dictionary, increment DIC_TYPE_LAST_ID public static final int DIC_TYPE_LAST_ID = 4; + public static final String DICT_KEY_MAIN = "main"; + public static final String DICT_KEY_CONTACTS = "contacts"; + public static final String DICT_KEY_AUTO = "auto"; + public static final String DICT_KEY_USER = "user"; + public static final String DICT_KEY_USER_BIGRAM = "user_bigram"; + public static final String DICT_KEY_WHITELIST ="whitelist"; + static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000; private static final boolean DBG = LatinImeLogger.sDBG; + private AutoCorrection mAutoCorrection; + private BinaryDictionary mMainDict; - - private Dictionary mUserDictionary; - - private Dictionary mAutoDictionary; - - private Dictionary mContactsDictionary; - - private Dictionary mUserBigramDictionary; + private WhitelistDictionary mWhiteListDictionary; + private final Map mUnigramDictionaries = new HashMap(); + private final Map mBigramDictionaries = new HashMap(); private int mPrefMaxSuggestions = 12; @@ -84,20 +93,13 @@ public class Suggest implements Dictionary.WordCallback { private boolean mQuickFixesEnabled; private double mAutoCorrectionThreshold; - private int[] mPriorities = new int[mPrefMaxSuggestions]; - private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS]; + private int[] mScores = new int[mPrefMaxSuggestions]; + private int[] mBigramScores = new int[PREF_MAX_BIGRAMS]; - // Handle predictive correction for only the first 1280 characters for performance reasons - // If we support scripts that need latin characters beyond that, we should probably use some - // kind of a sparse array or language specific list with a mapping lookup table. - // 1280 is the size of the BASE_CHARS array in ExpandableDictionary, which is a basic set of - // latin characters. - private int[] mNextLettersFrequencies = new int[1280]; private ArrayList mSuggestions = new ArrayList(); ArrayList mBigramSuggestions = new ArrayList(); private ArrayList mStringPool = new ArrayList(); - private boolean mHasAutoCorrection; - private String mLowerOriginalWord; + private CharSequence mTypedWord; // TODO: Remove these member variables by passing more context to addWord() callback method private boolean mIsFirstCharCapitalized; @@ -105,15 +107,42 @@ public class Suggest implements Dictionary.WordCallback { private int mCorrectionMode = CORRECTION_BASIC; - public Suggest(Context context, int dictionaryResId) { - mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN); + public Suggest(Context context, int dictionaryResId, Locale locale) { + init(context, BinaryDictionary.initDictionaryFromManager(context, DIC_MAIN, locale, + dictionaryResId)); + } + + /* package for test */ Suggest(File dictionary, long startOffset, long length, + BinaryDictionary.Flag[] flagArray) { + init(null, BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN, + flagArray)); + } + + private void init(Context context, BinaryDictionary mainDict) { + if (mainDict != null) { + mMainDict = mainDict; + mUnigramDictionaries.put(DICT_KEY_MAIN, mainDict); + mBigramDictionaries.put(DICT_KEY_MAIN, mainDict); + } + mWhiteListDictionary = WhitelistDictionary.init(context); + if (mWhiteListDictionary != null) { + mUnigramDictionaries.put(DICT_KEY_WHITELIST, mWhiteListDictionary); + } + mAutoCorrection = new AutoCorrection(); initPool(); } - // For unit test - /* package */ Suggest(File dictionary, long startOffset, long length) { - mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN); - initPool(); + public void resetMainDict(Context context, int dictionaryResId, Locale locale) { + final BinaryDictionary newMainDict = BinaryDictionary.initDictionaryFromManager(context, + DIC_MAIN, locale, dictionaryResId); + mMainDict = newMainDict; + if (null == newMainDict) { + mUnigramDictionaries.remove(DICT_KEY_MAIN); + mBigramDictionaries.remove(DICT_KEY_MAIN); + } else { + mUnigramDictionaries.put(DICT_KEY_MAIN, newMainDict); + mBigramDictionaries.put(DICT_KEY_MAIN, newMainDict); + } } private void initPool() { @@ -139,6 +168,10 @@ public class Suggest implements Dictionary.WordCallback { return mMainDict != null && mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD; } + public Map getUnigramDictionaries() { + return mUnigramDictionaries; + } + public int getApproxMaxWordLength() { return APPROX_MAX_WORD_LENGTH; } @@ -148,22 +181,28 @@ public class Suggest implements Dictionary.WordCallback { * before the main dictionary, if set. */ public void setUserDictionary(Dictionary userDictionary) { - mUserDictionary = userDictionary; + if (userDictionary != null) + mUnigramDictionaries.put(DICT_KEY_USER, userDictionary); } /** * Sets an optional contacts dictionary resource to be loaded. */ - public void setContactsDictionary(Dictionary userDictionary) { - mContactsDictionary = userDictionary; + public void setContactsDictionary(Dictionary contactsDictionary) { + if (contactsDictionary != null) { + mUnigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary); + mBigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary); + } } public void setAutoDictionary(Dictionary autoDictionary) { - mAutoDictionary = autoDictionary; + if (autoDictionary != null) + mUnigramDictionaries.put(DICT_KEY_AUTO, autoDictionary); } public void setUserBigramDictionary(Dictionary userBigramDictionary) { - mUserBigramDictionary = userBigramDictionary; + if (userBigramDictionary != null) + mBigramDictionaries.put(DICT_KEY_USER_BIGRAM, userBigramDictionary); } public void setAutoCorrectionThreshold(double threshold) { @@ -185,8 +224,8 @@ public class Suggest implements Dictionary.WordCallback { throw new IllegalArgumentException("maxSuggestions must be between 1 and 100"); } mPrefMaxSuggestions = maxSuggestions; - mPriorities = new int[mPrefMaxSuggestions]; - mBigramPriorities = new int[PREF_MAX_BIGRAMS]; + mScores = new int[mPrefMaxSuggestions]; + mBigramScores = new int[PREF_MAX_BIGRAMS]; collectGarbage(mSuggestions, mPrefMaxSuggestions); while (mStringPool.size() < mPrefMaxSuggestions) { StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); @@ -207,35 +246,50 @@ public class Suggest implements Dictionary.WordCallback { return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build(); } + private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) { + if (TextUtils.isEmpty(word) || !(all || first)) return word; + final int wordLength = word.length(); + final int poolSize = mStringPool.size(); + final StringBuilder sb = + poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1) + : new StringBuilder(getApproxMaxWordLength()); + sb.setLength(0); + if (all) { + sb.append(word.toString().toUpperCase()); + } else if (first) { + sb.append(Character.toUpperCase(word.charAt(0))); + if (wordLength > 1) { + sb.append(word.subSequence(1, wordLength)); + } + } + return sb; + } + // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer, CharSequence prevWordForBigram) { LatinImeLogger.onStartSuggestion(prevWordForBigram); - mHasAutoCorrection = false; + mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = wordComposer.isAllUpperCase(); collectGarbage(mSuggestions, mPrefMaxSuggestions); - Arrays.fill(mPriorities, 0); - Arrays.fill(mNextLettersFrequencies, 0); + Arrays.fill(mScores, 0); // Save a lowercase version of the original word CharSequence typedWord = wordComposer.getTypedWord(); if (typedWord != null) { final String typedWordString = typedWord.toString(); typedWord = typedWordString; - mLowerOriginalWord = typedWordString.toLowerCase(); // Treating USER_TYPED as UNIGRAM suggestion for logging now. LatinImeLogger.onAddSuggestedWord(typedWordString, Suggest.DIC_USER_TYPED, Dictionary.DataType.UNIGRAM); - } else { - mLowerOriginalWord = ""; } + mTypedWord = typedWord; - double normalizedScore = Integer.MIN_VALUE; if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM || mCorrectionMode == CORRECTION_BASIC)) { // At first character typed, search only the bigrams - Arrays.fill(mBigramPriorities, 0); + Arrays.fill(mBigramScores, 0); collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS); if (!TextUtils.isEmpty(prevWordForBigram)) { @@ -243,17 +297,8 @@ public class Suggest implements Dictionary.WordCallback { if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) { prevWordForBigram = lowerPrevWord; } - if (mUserBigramDictionary != null) { - mUserBigramDictionary.getBigrams(wordComposer, prevWordForBigram, this, - mNextLettersFrequencies); - } - if (mContactsDictionary != null) { - mContactsDictionary.getBigrams(wordComposer, prevWordForBigram, this, - mNextLettersFrequencies); - } - if (mMainDict != null) { - mMainDict.getBigrams(wordComposer, prevWordForBigram, this, - mNextLettersFrequencies); + for (final Dictionary dictionary : mBigramDictionaries.values()) { + dictionary.getBigrams(wordComposer, prevWordForBigram, this); } char currentChar = wordComposer.getTypedWord().charAt(0); char currentCharUpper = Character.toUpperCase(currentChar); @@ -276,120 +321,85 @@ public class Suggest implements Dictionary.WordCallback { } else if (wordComposer.size() > 1) { // At second character typed, search the unigrams (scores being affected by bigrams) - if (mUserDictionary != null || mContactsDictionary != null) { - if (mUserDictionary != null) { - mUserDictionary.getWords(wordComposer, this, mNextLettersFrequencies); - } - if (mContactsDictionary != null) { - mContactsDictionary.getWords(wordComposer, this, mNextLettersFrequencies); - } - - if (mSuggestions.size() > 0 && isValidWord(typedWord) - && (mCorrectionMode == CORRECTION_FULL - || mCorrectionMode == CORRECTION_FULL_BIGRAM)) { - if (DBG) { - Log.d(TAG, "Auto corrected by CORRECTION_FULL."); - } - mHasAutoCorrection = true; - } - } - if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies); - if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM) - && mSuggestions.size() > 0 && mPriorities.length > 0) { - // TODO: when the normalized score of the first suggestion is nearly equals to - // the normalized score of the second suggestion, behave less aggressive. - normalizedScore = Utils.calcNormalizedScore( - typedWord, mSuggestions.get(0), mPriorities[0]); - if (DBG) { - Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + "," - + mPriorities[0] + ", " + normalizedScore - + "(" + mAutoCorrectionThreshold + ")"); - } - if (normalizedScore >= mAutoCorrectionThreshold) { - if (DBG) { - Log.d(TAG, "Auto corrected by S-threthhold."); - } - mHasAutoCorrection = true; - } + for (final String key : mUnigramDictionaries.keySet()) { + // Skip AutoDictionary and WhitelistDictionary to lookup + if (key.equals(DICT_KEY_AUTO) || key.equals(DICT_KEY_WHITELIST)) + continue; + final Dictionary dictionary = mUnigramDictionaries.get(key); + dictionary.getWords(wordComposer, this); } } + CharSequence autoText = null; + final String typedWordString = typedWord == null ? null : typedWord.toString(); if (typedWord != null) { - mSuggestions.add(0, typedWord.toString()); - } - if (mQuickFixesEnabled) { - int i = 0; - int max = 6; - // Don't autotext the suggestions from the dictionaries - if (mCorrectionMode == CORRECTION_BASIC) max = 1; - while (i < mSuggestions.size() && i < max) { - String suggestedWord = mSuggestions.get(i).toString().toLowerCase(); - CharSequence autoText = - AutoText.get(suggestedWord, 0, suggestedWord.length(), view); + // Apply quick fix only for the typed word. + if (mQuickFixesEnabled) { + final String lowerCaseTypedWord = typedWordString.toLowerCase(); + CharSequence tempAutoText = capitalizeWord( + mIsAllUpperCase, mIsFirstCharCapitalized, AutoText.get( + lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view)); + // TODO: cleanup canAdd // Is there an AutoText (also known as Quick Fixes) correction? - boolean canAdd = autoText != null; // Capitalize as needed - final int autoTextLength = autoText != null ? autoText.length() : 0; - if (autoTextLength > 0 && (mIsAllUpperCase || mIsFirstCharCapitalized)) { - int poolSize = mStringPool.size(); - StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove( - poolSize - 1) : new StringBuilder(getApproxMaxWordLength()); - sb.setLength(0); - if (mIsAllUpperCase) { - sb.append(autoText.toString().toUpperCase()); - } else if (mIsFirstCharCapitalized) { - sb.append(Character.toUpperCase(autoText.charAt(0))); - if (autoTextLength > 1) { - sb.append(autoText.subSequence(1, autoTextLength)); - } - } - autoText = sb.toString(); - } + boolean canAdd = tempAutoText != null; // Is that correction already the current prediction (or original word)? - canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i)); + canAdd &= !TextUtils.equals(tempAutoText, typedWord); // Is that correction already the next predicted word? - if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) { - canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1)); + if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) { + canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0)); } if (canAdd) { if (DBG) { Log.d(TAG, "Auto corrected by AUTOTEXT."); } - mHasAutoCorrection = true; - mSuggestions.add(i + 1, autoText); - i++; + autoText = tempAutoText; } - i++; } } + + CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized, + mWhiteListDictionary.getWhiteListedWord(typedWordString)); + + mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer, + mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode, + autoText, whitelistedWord); + + if (autoText != null) { + mSuggestions.add(0, autoText); + } + + if (whitelistedWord != null) { + mSuggestions.add(0, whitelistedWord); + } + + if (typedWord != null) { + mSuggestions.add(0, typedWordString); + } removeDupes(); + if (DBG) { - ArrayList frequencyInfoList = + double normalizedScore = mAutoCorrection.getNormalizedScore(); + ArrayList scoreInfoList = new ArrayList(); - frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); - final int priorityLength = mPriorities.length; - for (int i = 0; i < priorityLength; ++i) { + scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); + for (int i = 0; i < mScores.length; ++i) { if (normalizedScore > 0) { - final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" + - normalizedScore + ")"; - frequencyInfoList.add( - new SuggestedWords.SuggestedWordInfo(priorityThreshold, false)); + final String scoreThreshold = String.format("%d (%4.2f)", mScores[i], + normalizedScore); + scoreInfoList.add( + new SuggestedWords.SuggestedWordInfo(scoreThreshold, false)); normalizedScore = 0.0; } else { - final String priority = Integer.toString(mPriorities[i]); - frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false)); + final String score = Integer.toString(mScores[i]); + scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false)); } } - for (int i = priorityLength; i < mSuggestions.size(); ++i) { - frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); + for (int i = mScores.length; i < mSuggestions.size(); ++i) { + scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); } - return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList); - } else { - return new SuggestedWords.Builder().addWords(mSuggestions, null); + return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList); } - } - - public int[] getNextLettersFrequencies() { - return mNextLettersFrequencies; + return new SuggestedWords.Builder().addWords(mSuggestions, null); } private void removeDupes() { @@ -420,45 +430,43 @@ public class Suggest implements Dictionary.WordCallback { } public boolean hasAutoCorrection() { - return mHasAutoCorrection; - } - - private static boolean compareCaseInsensitive(final String lowerOriginalWord, - final char[] word, final int offset, final int length) { - final int originalLength = lowerOriginalWord.length(); - if (originalLength == length && Character.isUpperCase(word[offset])) { - for (int i = 0; i < originalLength; i++) { - if (lowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) { - return false; - } - } - return true; - } - return false; + return mAutoCorrection.hasAutoCorrection(); } @Override - public boolean addWord(final char[] word, final int offset, final int length, int freq, + public boolean addWord(final char[] word, final int offset, final int length, int score, final int dicTypeId, final Dictionary.DataType dataType) { Dictionary.DataType dataTypeForLog = dataType; - ArrayList suggestions; - int[] priorities; - int prefMaxSuggestions; + final ArrayList suggestions; + final int[] sortedScores; + final int prefMaxSuggestions; if(dataType == Dictionary.DataType.BIGRAM) { suggestions = mBigramSuggestions; - priorities = mBigramPriorities; + sortedScores = mBigramScores; prefMaxSuggestions = PREF_MAX_BIGRAMS; } else { suggestions = mSuggestions; - priorities = mPriorities; + sortedScores = mScores; prefMaxSuggestions = mPrefMaxSuggestions; } int pos = 0; // Check if it's the same word, only caps are different - if (compareCaseInsensitive(mLowerOriginalWord, word, offset, length)) { - pos = 0; + if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) { + // TODO: remove this surrounding if clause and move this logic to + // getSuggestedWordBuilder. + if (suggestions.size() > 0) { + final String currentHighestWord = suggestions.get(0).toString(); + // If the current highest word is also equal to typed word, we need to compare + // frequency to determine the insertion position. This does not ensure strictly + // correct ordering, but ensures the top score is on top which is enough for + // removing duplicates correctly. + if (Utils.equalsIgnoreCase(currentHighestWord, word, offset, length) + && score <= sortedScores[0]) { + pos = 1; + } + } } else { if (dataType == Dictionary.DataType.UNIGRAM) { // Check if the word was already added before (by bigram data) @@ -466,24 +474,24 @@ public class Suggest implements Dictionary.WordCallback { if(bigramSuggestion >= 0) { dataTypeForLog = Dictionary.DataType.BIGRAM; // turn freq from bigram into multiplier specified above - double multiplier = (((double) mBigramPriorities[bigramSuggestion]) + double multiplier = (((double) mBigramScores[bigramSuggestion]) / MAXIMUM_BIGRAM_FREQUENCY) * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN) + BIGRAM_MULTIPLIER_MIN; /* Log.d(TAG,"bigram num: " + bigramSuggestion + " wordB: " + mBigramSuggestions.get(bigramSuggestion).toString() - + " currentPriority: " + freq + " bigramPriority: " - + mBigramPriorities[bigramSuggestion] + + " currentScore: " + score + " bigramScore: " + + mBigramScores[bigramSuggestion] + " multiplier: " + multiplier); */ - freq = (int)Math.round((freq * multiplier)); + score = (int)Math.round((score * multiplier)); } } - // Check the last one's priority and bail - if (priorities[prefMaxSuggestions - 1] >= freq) return true; + // Check the last one's score and bail + if (sortedScores[prefMaxSuggestions - 1] >= score) return true; while (pos < prefMaxSuggestions) { - if (priorities[pos] < freq - || (priorities[pos] == freq && length < suggestions.get(pos).length())) { + if (sortedScores[pos] < score + || (sortedScores[pos] == score && length < suggestions.get(pos).length())) { break; } pos++; @@ -493,8 +501,8 @@ public class Suggest implements Dictionary.WordCallback { return true; } - System.arraycopy(priorities, pos, priorities, pos + 1, prefMaxSuggestions - pos - 1); - priorities[pos] = freq; + System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1); + sortedScores[pos] = score; int poolSize = mStringPool.size(); StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1) : new StringBuilder(getApproxMaxWordLength()); @@ -541,16 +549,6 @@ public class Suggest implements Dictionary.WordCallback { return -1; } - public boolean isValidWord(final CharSequence word) { - if (word == null || word.length() == 0 || mMainDict == null) { - return false; - } - return mMainDict.isValidWord(word) - || (mUserDictionary != null && mUserDictionary.isValidWord(word)) - || (mAutoDictionary != null && mAutoDictionary.isValidWord(word)) - || (mContactsDictionary != null && mContactsDictionary.isValidWord(word)); - } - private void collectGarbage(ArrayList suggestions, int prefMaxSuggestions) { int poolSize = mStringPool.size(); int garbageSize = suggestions.size(); @@ -569,25 +567,12 @@ public class Suggest implements Dictionary.WordCallback { } public void close() { - if (mMainDict != null) { - mMainDict.close(); - mMainDict = null; - } - if (mUserDictionary != null) { - mUserDictionary.close(); - mUserDictionary = null; - } - if (mUserBigramDictionary != null) { - mUserBigramDictionary.close(); - mUserBigramDictionary = null; - } - if (mContactsDictionary != null) { - mContactsDictionary.close(); - mContactsDictionary = null; - } - if (mAutoDictionary != null) { - mAutoDictionary.close(); - mAutoDictionary = null; + final Set dictionaries = new HashSet(); + dictionaries.addAll(mUnigramDictionaries.values()); + dictionaries.addAll(mBigramDictionaries.values()); + for (final Dictionary dictionary : dictionaries) { + dictionary.close(); } + mMainDict = null; } } diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index f774ce3a5..fe7aac7c2 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -24,23 +24,20 @@ import java.util.HashSet; import java.util.List; public class SuggestedWords { - public static final SuggestedWords EMPTY = new SuggestedWords(null, false, false, false, null); + public static final SuggestedWords EMPTY = new SuggestedWords(null, false, false, null); public final List mWords; - public final boolean mIsApplicationSpecifiedCompletions; public final boolean mTypedWordValid; public final boolean mHasMinimalSuggestion; public final List mSuggestedWordInfoList; - private SuggestedWords(List words, boolean isApplicationSpecifiedCompletions, - boolean typedWordValid, boolean hasMinamlSuggestion, - List suggestedWordInfoList) { + private SuggestedWords(List words, boolean typedWordValid, + boolean hasMinamlSuggestion, List suggestedWordInfoList) { if (words != null) { mWords = words; } else { mWords = Collections.emptyList(); } - mIsApplicationSpecifiedCompletions = isApplicationSpecifiedCompletions; mTypedWordValid = typedWordValid; mHasMinimalSuggestion = hasMinamlSuggestion; mSuggestedWordInfoList = suggestedWordInfoList; @@ -64,7 +61,6 @@ public class SuggestedWords { public static class Builder { private List mWords = new ArrayList(); - private boolean mIsCompletions; private boolean mTypedWordValid; private boolean mHasMinimalSuggestion; private List mSuggestedWordInfoList = @@ -109,7 +105,6 @@ public class SuggestedWords { public Builder setApplicationSpecifiedCompletions(CompletionInfo[] infos) { for (CompletionInfo info : infos) addWord(info.getText()); - mIsCompletions = true; return this; } @@ -141,15 +136,14 @@ public class SuggestedWords { alreadySeen.add(prevWord); } } - mIsCompletions = false; mTypedWordValid = false; mHasMinimalSuggestion = false; return this; } public SuggestedWords build() { - return new SuggestedWords(mWords, mIsCompletions, mTypedWordValid, - mHasMinimalSuggestion, mSuggestedWordInfoList); + return new SuggestedWords(mWords, mTypedWordValid, mHasMinimalSuggestion, + mSuggestedWordInfoList); } public int size() { diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index f571f26d5..63196430b 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -16,117 +16,39 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.Key; - -import android.content.Context; -import android.text.format.DateFormat; import android.util.Log; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Calendar; - public class TextEntryState { - - private static final boolean DBG = false; + private static final String TAG = TextEntryState.class.getSimpleName(); + private static final boolean DEBUG = false; - private static final String TAG = "TextEntryState"; + private static final int UNKNOWN = 0; + private static final int START = 1; + private static final int IN_WORD = 2; + private static final int ACCEPTED_DEFAULT = 3; + private static final int PICKED_SUGGESTION = 4; + private static final int PUNCTUATION_AFTER_WORD = 5; + private static final int PUNCTUATION_AFTER_ACCEPTED = 6; + private static final int SPACE_AFTER_ACCEPTED = 7; + private static final int SPACE_AFTER_PICKED = 8; + private static final int UNDO_COMMIT = 9; + private static final int RECORRECTING = 10; + private static final int PICKED_RECORRECTION = 11; - private static boolean LOGGING = false; + private static int sState = UNKNOWN; + private static int sPreviousState = UNKNOWN; - private static int sBackspaceCount = 0; - - private static int sAutoSuggestCount = 0; - - private static int sAutoSuggestUndoneCount = 0; - - private static int sManualSuggestCount = 0; - - private static int sWordNotInDictionaryCount = 0; - - private static int sSessionCount = 0; - - private static int sTypedChars; - - private static int sActualChars; - - public enum State { - UNKNOWN, - START, - IN_WORD, - ACCEPTED_DEFAULT, - PICKED_SUGGESTION, - PUNCTUATION_AFTER_WORD, - PUNCTUATION_AFTER_ACCEPTED, - SPACE_AFTER_ACCEPTED, - SPACE_AFTER_PICKED, - UNDO_COMMIT, - CORRECTING, - PICKED_CORRECTION, + private static void setState(final int newState) { + sPreviousState = sState; + sState = newState; } - private static State sState = State.UNKNOWN; - - private static FileOutputStream sKeyLocationFile; - private static FileOutputStream sUserActionFile; - - public static void newSession(Context context) { - sSessionCount++; - sAutoSuggestCount = 0; - sBackspaceCount = 0; - sAutoSuggestUndoneCount = 0; - sManualSuggestCount = 0; - sWordNotInDictionaryCount = 0; - sTypedChars = 0; - sActualChars = 0; - sState = State.START; - - if (LOGGING) { - try { - sKeyLocationFile = context.openFileOutput("key.txt", Context.MODE_APPEND); - sUserActionFile = context.openFileOutput("action.txt", Context.MODE_APPEND); - } catch (IOException ioe) { - Log.e("TextEntryState", "Couldn't open file for output: " + ioe); - } - } - } - - public static void endSession() { - if (sKeyLocationFile == null) { - return; - } - try { - sKeyLocationFile.close(); - // Write to log file - // Write timestamp, settings, - String out = DateFormat.format("MM:dd hh:mm:ss", Calendar.getInstance().getTime()) - .toString() - + " BS: " + sBackspaceCount - + " auto: " + sAutoSuggestCount - + " manual: " + sManualSuggestCount - + " typed: " + sWordNotInDictionaryCount - + " undone: " + sAutoSuggestUndoneCount - + " saved: " + ((float) (sActualChars - sTypedChars) / sActualChars) - + "\n"; - sUserActionFile.write(out.getBytes()); - sUserActionFile.close(); - sKeyLocationFile = null; - sUserActionFile = null; - } catch (IOException ioe) { - // ignore - } - } - public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord) { if (typedWord == null) return; - if (!typedWord.equals(actualWord)) { - sAutoSuggestCount++; - } - sTypedChars += typedWord.length(); - sActualChars += actualWord.length(); - sState = State.ACCEPTED_DEFAULT; + setState(ACCEPTED_DEFAULT); LatinImeLogger.logOnAutoSuggestion(typedWord.toString(), actualWord.toString()); - displayState(); + if (DEBUG) + displayState("acceptedDefault", "typedWord", typedWord, "actualWord", actualWord); } // State.ACCEPTED_DEFAULT will be changed to other sub-states @@ -138,151 +60,167 @@ public class TextEntryState { case SPACE_AFTER_ACCEPTED: case PUNCTUATION_AFTER_ACCEPTED: case IN_WORD: - sState = State.ACCEPTED_DEFAULT; + setState(ACCEPTED_DEFAULT); break; default: break; } - displayState(); + if (DEBUG) displayState("backToAcceptedDefault", "typedWord", typedWord); } - public static void acceptedTyped(@SuppressWarnings("unused") CharSequence typedWord) { - sWordNotInDictionaryCount++; - sState = State.PICKED_SUGGESTION; - displayState(); + public static void acceptedTyped(CharSequence typedWord) { + setState(PICKED_SUGGESTION); + if (DEBUG) displayState("acceptedTyped", "typedWord", typedWord); } public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) { - sManualSuggestCount++; - State oldState = sState; - if (typedWord.equals(actualWord)) { - acceptedTyped(typedWord); - } - if (oldState == State.CORRECTING || oldState == State.PICKED_CORRECTION) { - sState = State.PICKED_CORRECTION; + if (sState == RECORRECTING || sState == PICKED_RECORRECTION) { + setState(PICKED_RECORRECTION); } else { - sState = State.PICKED_SUGGESTION; + setState(PICKED_SUGGESTION); } - displayState(); + if (DEBUG) + displayState("acceptedSuggestion", "typedWord", typedWord, "actualWord", actualWord); } - public static void selectedForCorrection() { - sState = State.CORRECTING; - displayState(); + public static void selectedForRecorrection() { + setState(RECORRECTING); + if (DEBUG) displayState("selectedForRecorrection"); } - public static void onAbortCorrection() { - if (isCorrecting()) { - sState = State.START; + public static void onAbortRecorrection() { + if (sState == RECORRECTING || sState == PICKED_RECORRECTION) { + setState(START); } - displayState(); + if (DEBUG) displayState("onAbortRecorrection"); } public static void typedCharacter(char c, boolean isSeparator) { - boolean isSpace = c == ' '; + final boolean isSpace = (c == ' '); switch (sState) { - case IN_WORD: - if (isSpace || isSeparator) { - sState = State.START; - } else { - // State hasn't changed. - } - break; - case ACCEPTED_DEFAULT: - case SPACE_AFTER_PICKED: - if (isSpace) { - sState = State.SPACE_AFTER_ACCEPTED; - } else if (isSeparator) { - sState = State.PUNCTUATION_AFTER_ACCEPTED; - } else { - sState = State.IN_WORD; - } - break; - case PICKED_SUGGESTION: - case PICKED_CORRECTION: - if (isSpace) { - sState = State.SPACE_AFTER_PICKED; - } else if (isSeparator) { - // Swap - sState = State.PUNCTUATION_AFTER_ACCEPTED; - } else { - sState = State.IN_WORD; - } - break; - case START: - case UNKNOWN: - case SPACE_AFTER_ACCEPTED: - case PUNCTUATION_AFTER_ACCEPTED: - case PUNCTUATION_AFTER_WORD: - if (!isSpace && !isSeparator) { - sState = State.IN_WORD; - } else { - sState = State.START; - } - break; - case UNDO_COMMIT: - if (isSpace || isSeparator) { - sState = State.ACCEPTED_DEFAULT; - } else { - sState = State.IN_WORD; - } - break; - case CORRECTING: - sState = State.START; - break; + case IN_WORD: + if (isSpace || isSeparator) { + setState(START); + } else { + // State hasn't changed. + } + break; + case ACCEPTED_DEFAULT: + case SPACE_AFTER_PICKED: + case PUNCTUATION_AFTER_ACCEPTED: + if (isSpace) { + setState(SPACE_AFTER_ACCEPTED); + } else if (isSeparator) { + // Swap + setState(PUNCTUATION_AFTER_ACCEPTED); + } else { + setState(IN_WORD); + } + break; + case PICKED_SUGGESTION: + case PICKED_RECORRECTION: + if (isSpace) { + setState(SPACE_AFTER_PICKED); + } else if (isSeparator) { + // Swap + setState(PUNCTUATION_AFTER_ACCEPTED); + } else { + setState(IN_WORD); + } + break; + case START: + case UNKNOWN: + case SPACE_AFTER_ACCEPTED: + case PUNCTUATION_AFTER_WORD: + if (!isSpace && !isSeparator) { + setState(IN_WORD); + } else { + setState(START); + } + break; + case UNDO_COMMIT: + if (isSpace || isSeparator) { + setState(ACCEPTED_DEFAULT); + } else { + setState(IN_WORD); + } + break; + case RECORRECTING: + setState(START); + break; } - displayState(); + if (DEBUG) displayState("typedCharacter", "char", c, "isSeparator", isSeparator); } public static void backspace() { - if (sState == State.ACCEPTED_DEFAULT) { - sState = State.UNDO_COMMIT; - sAutoSuggestUndoneCount++; + if (sState == ACCEPTED_DEFAULT) { + setState(UNDO_COMMIT); LatinImeLogger.logOnAutoSuggestionCanceled(); - } else if (sState == State.UNDO_COMMIT) { - sState = State.IN_WORD; + } else if (sState == UNDO_COMMIT) { + setState(IN_WORD); } - sBackspaceCount++; - displayState(); + if (DEBUG) displayState("backspace"); } public static void reset() { - sState = State.START; - displayState(); + setState(START); + if (DEBUG) displayState("reset"); } - public static State getState() { - if (DBG) { - Log.d(TAG, "Returning state = " + sState); - } - return sState; + public static boolean isAcceptedDefault() { + return sState == ACCEPTED_DEFAULT; } - public static boolean isCorrecting() { - return sState == State.CORRECTING || sState == State.PICKED_CORRECTION; + public static boolean isSpaceAfterPicked() { + return sState == SPACE_AFTER_PICKED; } - public static void keyPressedAt(Key key, int x, int y) { - if (LOGGING && sKeyLocationFile != null && key.mCode >= 32) { - String out = - "KEY: " + (char) key.mCode - + " X: " + x - + " Y: " + y - + " MX: " + (key.mX + key.mWidth / 2) - + " MY: " + (key.mY + key.mHeight / 2) - + "\n"; - try { - sKeyLocationFile.write(out.getBytes()); - } catch (IOException ioe) { - // TODO: May run out of space - } + public static boolean isUndoCommit() { + return sState == UNDO_COMMIT; + } + + public static boolean isPunctuationAfterAccepted() { + return sState == PUNCTUATION_AFTER_ACCEPTED; + } + + public static boolean isRecorrecting() { + return sState == RECORRECTING || sState == PICKED_RECORRECTION; + } + + public static String getState() { + return stateName(sState); + } + + private static String stateName(int state) { + switch (state) { + case START: return "START"; + case IN_WORD: return "IN_WORD"; + case ACCEPTED_DEFAULT: return "ACCEPTED_DEFAULT"; + case PICKED_SUGGESTION: return "PICKED_SUGGESTION"; + case PUNCTUATION_AFTER_WORD: return "PUNCTUATION_AFTER_WORD"; + case PUNCTUATION_AFTER_ACCEPTED: return "PUNCTUATION_AFTER_ACCEPTED"; + case SPACE_AFTER_ACCEPTED: return "SPACE_AFTER_ACCEPTED"; + case SPACE_AFTER_PICKED: return "SPACE_AFTER_PICKED"; + case UNDO_COMMIT: return "UNDO_COMMIT"; + case RECORRECTING: return "RECORRECTING"; + case PICKED_RECORRECTION: return "PICKED_RECORRECTION"; + default: return "UNKNOWN"; } } - private static void displayState() { - if (DBG) { - Log.d(TAG, "State = " + sState); + private static void displayState(String title, Object ... args) { + final StringBuilder sb = new StringBuilder(title); + sb.append(':'); + for (int i = 0; i < args.length; i += 2) { + sb.append(' '); + sb.append(args[i]); + sb.append('='); + sb.append(args[i+1].toString()); } + sb.append(" state="); + sb.append(stateName(sState)); + sb.append(" previous="); + sb.append(stateName(sPreviousState)); + Log.d(TAG, sb.toString()); } } - diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index 56ee5b9e7..c06bd736e 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -126,9 +126,8 @@ public class UserDictionary extends ExpandableDictionary { } @Override - public synchronized void getWords(final WordComposer codes, final WordCallback callback, - int[] nextLettersFrequencies) { - super.getWords(codes, callback, nextLettersFrequencies); + public synchronized void getWords(final WordComposer codes, final WordCallback callback) { + super.getWords(codes, callback); } @Override @@ -138,7 +137,7 @@ public class UserDictionary extends ExpandableDictionary { private void addWords(Cursor cursor) { clearDictionary(); - + if (cursor == null) return; final int maxWordLength = getMaxWordLength(); if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index d33d962c0..35b2b123c 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -16,15 +16,21 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.compat.InputMethodInfoCompatWrapper; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputTypeCompatUtils; +import com.android.inputmethod.keyboard.KeyboardId; + +import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.os.AsyncTask; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; +import android.text.InputType; import android.text.format.DateUtils; import android.util.Log; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.EditorInfo; import java.io.BufferedReader; import java.io.File; @@ -41,6 +47,10 @@ public class Utils { private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; private static boolean DBG = LatinImeLogger.sDBG; + private Utils() { + // Intentional empty constructor for utility class. + } + /** * Cancel an {@link AsyncTask}. * @@ -55,7 +65,7 @@ public class Utils { } public static class GCUtils { - private static final String TAG = "GCUtils"; + private static final String GC_TAG = GCUtils.class.getSimpleName(); public static final int GC_TRY_COUNT = 2; // GC_TRY_LOOP_MAX is used for the hard limit of GC wait, // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT. @@ -84,7 +94,7 @@ public class Utils { Thread.sleep(GC_INTERVAL); return true; } catch (InterruptedException e) { - Log.e(TAG, "Sleep was interrupted."); + Log.e(GC_TAG, "Sleep was interrupted."); LatinImeLogger.logOnException(metaData, t); return false; } @@ -92,16 +102,15 @@ public class Utils { } } - public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm) { - return imm.getEnabledInputMethodList().size() > 1; - // @@@ return imm.getEnabledInputMethodList().size() > 1 + public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) { + return imm.getEnabledInputMethodList().size() > 1 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled // input method subtype (The current IME should be LatinIME.) - // || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; + || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; } - public static String getInputMethodId(InputMethodManager imm, String packageName) { - for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) { + public static String getInputMethodId(InputMethodManagerCompatWrapper imm, String packageName) { + for (final InputMethodInfoCompatWrapper imi : imm.getEnabledInputMethodList()) { if (imi.getPackageName().equals(packageName)) return imi.getId(); } @@ -262,20 +271,42 @@ public class Utils { return dp[sl][tl]; } + // Get the current stack trace + public static String getStackTrace() { + StringBuilder sb = new StringBuilder(); + try { + throw new RuntimeException(); + } catch (RuntimeException e) { + StackTraceElement[] frames = e.getStackTrace(); + // Start at 1 because the first frame is here and we don't care about it + for (int j = 1; j < frames.length; ++j) sb.append(frames[j].toString() + "\n"); + } + return sb.toString(); + } + // In dictionary.cpp, getSuggestion() method, // suggestion scores are computed using the below formula. - // original score (called 'frequency') + // original score // := pow(mTypedLetterMultiplier (this is defined 2), // (the number of matched characters between typed word and suggested word)) // * (individual word's score which defined in the unigram dictionary, // and this score is defined in range [0, 255].) - // * (when before.length() == after.length(), - // mFullWordMultiplier (this is defined 2)) - // So, maximum original score is pow(2, before.length()) * 255 * 2 - // So, we can normalize original score by dividing this value. + // Then, the following processing is applied. + // - If the dictionary word is matched up to the point of the user entry + // (full match up to min(before.length(), after.length()) + // => Then multiply by FULL_MATCHED_WORDS_PROMOTION_RATE (this is defined 1.2) + // - If the word is a true full match except for differences in accents or + // capitalization, then treat it as if the score was 255. + // - If before.length() == after.length() + // => multiply by mFullWordMultiplier (this is defined 2)) + // So, maximum original score is pow(2, min(before.length(), after.length())) * 255 * 2 * 1.2 + // For historical reasons we ignore the 1.2 modifier (because the measure for a good + // autocorrection threshold was done at a time when it didn't exist). This doesn't change + // the result. + // So, we can normalize original score by dividing pow(2, min(b.l(),a.l())) * 255 * 2. private static final int MAX_INITIAL_SCORE = 255; private static final int TYPED_LETTER_MULTIPLIER = 2; - private static final int FULL_WORD_MULTIPLYER = 2; + private static final int FULL_WORD_MULTIPLIER = 2; public static double calcNormalizedScore(CharSequence before, CharSequence after, int score) { final int beforeLength = before.length(); final int afterLength = after.length(); @@ -285,7 +316,7 @@ public class Utils { // correction. final double maximumScore = MAX_INITIAL_SCORE * Math.pow(TYPED_LETTER_MULTIPLIER, Math.min(beforeLength, afterLength)) - * FULL_WORD_MULTIPLYER; + * FULL_WORD_MULTIPLIER; // add a weight based on edit distance. // distance <= max(afterLength, beforeLength) == afterLength, // so, 0 <= distance / afterLength <= 1 @@ -294,7 +325,7 @@ public class Utils { } public static class UsabilityStudyLogUtils { - private static final String TAG = "UsabilityStudyLogUtils"; + private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); private static final String FILENAME = "log.txt"; private static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); @@ -331,7 +362,7 @@ public class Utils { try { mWriter = getPrintWriter(mDirectory, FILENAME, false); } catch (IOException e) { - Log.e(TAG, "Can't create log file."); + Log.e(USABILITY_TAG, "Can't create log file."); } } } @@ -368,7 +399,7 @@ public class Utils { final String printString = String.format("%s\t%d\t%s\n", mDateFormat.format(mDate), currentTime, log); if (LatinImeLogger.sDBG) { - Log.d(TAG, "Write: " + log); + Log.d(USABILITY_TAG, "Write: " + log); } mWriter.print(printString); } @@ -389,10 +420,10 @@ public class Utils { sb.append(line); } } catch (IOException e) { - Log.e(TAG, "Can't read log file."); + Log.e(USABILITY_TAG, "Can't read log file."); } finally { if (LatinImeLogger.sDBG) { - Log.d(TAG, "output all logs\n" + sb.toString()); + Log.d(USABILITY_TAG, "output all logs\n" + sb.toString()); } mIms.getCurrentInputConnection().commitText(sb.toString(), 0); try { @@ -411,7 +442,7 @@ public class Utils { public void run() { if (mFile != null && mFile.exists()) { if (LatinImeLogger.sDBG) { - Log.d(TAG, "Delete log file."); + Log.d(USABILITY_TAG, "Delete log file."); } mFile.delete(); mWriter.close(); @@ -440,4 +471,134 @@ public class Utils { return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); } } + + public static int getKeyboardMode(EditorInfo attribute) { + if (attribute == null) + return KeyboardId.MODE_TEXT; + + final int inputType = attribute.inputType; + final int variation = inputType & InputType.TYPE_MASK_VARIATION; + + switch (inputType & InputType.TYPE_MASK_CLASS) { + case InputType.TYPE_CLASS_NUMBER: + case InputType.TYPE_CLASS_DATETIME: + return KeyboardId.MODE_NUMBER; + case InputType.TYPE_CLASS_PHONE: + return KeyboardId.MODE_PHONE; + case InputType.TYPE_CLASS_TEXT: + if (InputTypeCompatUtils.isEmailVariation(variation)) { + return KeyboardId.MODE_EMAIL; + } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { + return KeyboardId.MODE_URL; + } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { + return KeyboardId.MODE_IM; + } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { + return KeyboardId.MODE_TEXT; + } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { + return KeyboardId.MODE_WEB; + } else { + return KeyboardId.MODE_TEXT; + } + default: + return KeyboardId.MODE_TEXT; + } + } + + public static boolean containsInCsv(String key, String csv) { + if (csv == null) + return false; + for (String option : csv.split(",")) { + if (option.equals(key)) + return true; + } + return false; + } + + public static boolean inPrivateImeOptions(String packageName, String key, + EditorInfo attribute) { + if (attribute == null) + return false; + return containsInCsv(packageName != null ? packageName + "." + key : key, + attribute.privateImeOptions); + } + + /** + * Returns a main dictionary resource id + * @return main dictionary resource id + */ + public static int getMainDictionaryResourceId(Resources res) { + final String MAIN_DIC_NAME = "main"; + String packageName = LatinIME.class.getPackage().getName(); + return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName); + } + + public static void loadNativeLibrary() { + try { + System.loadLibrary("jni_latinime"); + } catch (UnsatisfiedLinkError ule) { + Log.e(TAG, "Could not load native library jni_latinime"); + } + } + + /** + * Returns true if a and b are equal ignoring the case of the character. + * @param a first character to check + * @param b second character to check + * @return {@code true} if a and b are equal, {@code false} otherwise. + */ + public static boolean equalsIgnoreCase(char a, char b) { + // Some language, such as Turkish, need testing both cases. + return a == b + || Character.toLowerCase(a) == Character.toLowerCase(b) + || Character.toUpperCase(a) == Character.toUpperCase(b); + } + + /** + * Returns true if a and b are equal ignoring the case of the characters, including if they are + * both null. + * @param a first CharSequence to check + * @param b second CharSequence to check + * @return {@code true} if a and b are equal, {@code false} otherwise. + */ + public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) { + if (a == b) + return true; // including both a and b are null. + if (a == null || b == null) + return false; + final int length = a.length(); + if (length != b.length()) + return false; + for (int i = 0; i < length; i++) { + if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) + return false; + } + return true; + } + + /** + * Returns true if a and b are equal ignoring the case of the characters, including if a is null + * and b is zero length. + * @param a CharSequence to check + * @param b character array to check + * @param offset start offset of array b + * @param length length of characters in array b + * @return {@code true} if a and b are equal, {@code false} otherwise. + * @throws IndexOutOfBoundsException + * if {@code offset < 0 || length < 0 || offset + length > data.length}. + * @throws NullPointerException if {@code b == null}. + */ + public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) { + if (offset < 0 || length < 0 || length > b.length - offset) + throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset + + " length=" + length); + if (a == null) + return length == 0; // including a is null and b is zero length. + if (a.length() != length) + return false; + for (int i = 0; i < length; i++) { + if (!equalsIgnoreCase(a.charAt(i), b[offset + i])) + return false; + } + return true; + } } diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java new file mode 100644 index 000000000..2389d4e3c --- /dev/null +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import java.util.HashMap; + +public class WhitelistDictionary extends Dictionary { + + private static final boolean DBG = LatinImeLogger.sDBG; + private static final String TAG = WhitelistDictionary.class.getSimpleName(); + + private final HashMap> mWhitelistWords = + new HashMap>(); + + private static final WhitelistDictionary sInstance = new WhitelistDictionary(); + + private WhitelistDictionary() { + } + + public static WhitelistDictionary init(Context context) { + synchronized (sInstance) { + if (context != null) { + sInstance.initWordlist( + context.getResources().getStringArray(R.array.wordlist_whitelist)); + } else { + sInstance.mWhitelistWords.clear(); + } + } + return sInstance; + } + + private void initWordlist(String[] wordlist) { + mWhitelistWords.clear(); + final int N = wordlist.length; + if (N % 3 != 0) { + if (DBG) { + Log.d(TAG, "The number of the whitelist is invalid."); + } + return; + } + try { + for (int i = 0; i < N; i += 3) { + final int score = Integer.valueOf(wordlist[i]); + final String before = wordlist[i + 1]; + final String after = wordlist[i + 2]; + if (before != null && after != null) { + mWhitelistWords.put( + before.toLowerCase(), new Pair(score, after)); + } + } + } catch (NumberFormatException e) { + if (DBG) { + Log.d(TAG, "The score of the word is invalid."); + } + } + } + + public String getWhiteListedWord(String before) { + if (before == null) return null; + final String lowerCaseBefore = before.toLowerCase(); + if(mWhitelistWords.containsKey(lowerCaseBefore)) { + if (DBG) { + Log.d(TAG, "--- found whiteListedWord: " + lowerCaseBefore); + } + return mWhitelistWords.get(lowerCaseBefore).second; + } + return null; + } + + // Not used for WhitelistDictionary. We use getWhitelistedWord() in Suggest.java instead + @Override + public void getWords(WordComposer composer, WordCallback callback) { + } + + @Override + public boolean isValidWord(CharSequence word) { + if (TextUtils.isEmpty(word)) return false; + return !TextUtils.isEmpty(getWhiteListedWord(word.toString())); + } +} diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 2e415b771..02583895b 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,22 +16,32 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.KeyDetector; + import java.util.ArrayList; /** * A place to store the currently composing word with information such as adjacent key codes as well */ public class WordComposer { + + public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; + public static final int NOT_A_COORDINATE = -1; + /** * The list of unicode values for each keystroke (including surrounding keys) */ private final ArrayList mCodes; - + + private int mTypedLength; + private final int[] mXCoordinates; + private final int[] mYCoordinates; + /** * The word chosen from the candidate list, until it is committed. */ private String mPreferredWord; - + private final StringBuilder mTypedWord; private int mCapsCount; @@ -44,17 +54,24 @@ public class WordComposer { private boolean mIsFirstCharCapitalized; public WordComposer() { - mCodes = new ArrayList(12); - mTypedWord = new StringBuilder(20); + final int N = BinaryDictionary.MAX_WORD_LENGTH; + mCodes = new ArrayList(N); + mTypedWord = new StringBuilder(N); + mTypedLength = 0; + mXCoordinates = new int[N]; + mYCoordinates = new int[N]; } - WordComposer(WordComposer copy) { - mCodes = new ArrayList(copy.mCodes); - mPreferredWord = copy.mPreferredWord; - mTypedWord = new StringBuilder(copy.mTypedWord); - mCapsCount = copy.mCapsCount; - mAutoCapitalized = copy.mAutoCapitalized; - mIsFirstCharCapitalized = copy.mIsFirstCharCapitalized; + WordComposer(WordComposer source) { + mCodes = new ArrayList(source.mCodes); + mPreferredWord = source.mPreferredWord; + mTypedWord = new StringBuilder(source.mTypedWord); + mCapsCount = source.mCapsCount; + mAutoCapitalized = source.mAutoCapitalized; + mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; + mTypedLength = source.mTypedLength; + mXCoordinates = source.mXCoordinates; + mYCoordinates = source.mYCoordinates; } /** @@ -62,6 +79,7 @@ public class WordComposer { */ public void reset() { mCodes.clear(); + mTypedLength = 0; mIsFirstCharCapitalized = false; mPreferredWord = null; mTypedWord.setLength(0); @@ -85,15 +103,28 @@ public class WordComposer { return mCodes.get(index); } + public int[] getXCoordinates() { + return mXCoordinates; + } + + public int[] getYCoordinates() { + return mYCoordinates; + } + /** * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of * the array containing unicode for adjacent keys, sorted by reducing probability/proximity. * @param codes the array of unicode values */ - public void add(int primaryCode, int[] codes) { + public void add(int primaryCode, int[] codes, int x, int y) { mTypedWord.append((char) primaryCode); correctPrimaryJuxtapos(primaryCode, codes); mCodes.add(codes); + if (mTypedLength < BinaryDictionary.MAX_WORD_LENGTH) { + mXCoordinates[mTypedLength] = x; + mYCoordinates[mTypedLength] = y; + } + ++mTypedLength; if (Character.isUpperCase((char) primaryCode)) mCapsCount++; } @@ -124,6 +155,9 @@ public class WordComposer { mTypedWord.deleteCharAt(lastPos); if (Character.isUpperCase(last)) mCapsCount--; } + if (mTypedLength > 0) { + --mTypedLength; + } } /** diff --git a/native/Android.mk b/native/Android.mk index 2a570356f..1d32deab4 100644 --- a/native/Android.mk +++ b/native/Android.mk @@ -3,11 +3,19 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES += $(LOCAL_PATH)/src $(JNI_H_INCLUDE) +LOCAL_CFLAGS += -Werror -Wall + +# To suppress compiler warnings for unused variables/functions used for debug features etc. +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function + LOCAL_SRC_FILES := \ + jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \ jni/com_android_inputmethod_latin_BinaryDictionary.cpp \ + jni/onload.cpp \ src/bigram_dictionary.cpp \ src/char_utils.cpp \ src/dictionary.cpp \ + src/proximity_info.cpp \ src/unigram_dictionary.cpp #FLAG_DBG := true diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp new file mode 100644 index 000000000..3db89edf1 --- /dev/null +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -0,0 +1,90 @@ +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "LatinIME: jni: ProximityInfo" + +#include "com_android_inputmethod_keyboard_ProximityInfo.h" +#include "jni.h" +#include "proximity_info.h" + +#include +#include +#include + +// ---------------------------------------------------------------------------- + +namespace latinime { + +// +// helper function to throw an exception +// +static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { + if (jclass cls = env->FindClass(ex)) { + char msg[1000]; + snprintf(msg, sizeof(msg), fmt, data); + env->ThrowNew(cls, msg); + env->DeleteLocalRef(cls); + } +} + +static jint latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, + jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth, + jint gridHeight, jintArray proximityCharsArray) { + jint* proximityChars = env->GetIntArrayElements(proximityCharsArray, NULL); + ProximityInfo *proximityInfo = new ProximityInfo(maxProximityCharsSize, displayWidth, + displayHeight, gridWidth, gridHeight, (const uint32_t *)proximityChars); + env->ReleaseIntArrayElements(proximityCharsArray, proximityChars, 0); + return (jint)proximityInfo; +} + +static void latinime_Keyboard_release(JNIEnv *env, jobject object, jint proximityInfo) { + ProximityInfo *pi = (ProximityInfo*)proximityInfo; + if (!pi) return; + delete pi; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod sKeyboardMethods[] = { + {"setProximityInfoNative", "(IIIII[I)I", (void*)latinime_Keyboard_setProximityInfo}, + {"releaseProximityInfoNative", "(I)V", (void*)latinime_Keyboard_release} +}; + +static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, + int numMethods) { + jclass clazz; + + clazz = env->FindClass(className); + if (clazz == NULL) { + LOGE("Native registration unable to find class '%s'", className); + return JNI_FALSE; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s'", className); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +int register_ProximityInfo(JNIEnv *env) { + const char* const kClassPathName = "com/android/inputmethod/keyboard/ProximityInfo"; + return registerNativeMethods(env, kClassPathName, sKeyboardMethods, + sizeof(sKeyboardMethods) / sizeof(sKeyboardMethods[0])); +} + +}; // namespace latinime diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h new file mode 100644 index 000000000..bdeeb8f37 --- /dev/null +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h @@ -0,0 +1,27 @@ +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef _COM_ANDROID_INPUTMETHOD_KEYBOARD_PROXIMITYINFO_H +#define _COM_ANDROID_INPUTMETHOD_KEYBOARD_PROXIMITYINFO_H + +#include "jni.h" + +namespace latinime { +int register_ProximityInfo(JNIEnv *env); +} + +#endif // _COM_ANDROID_INPUTMETHOD_KEYBOARD_PROXIMITYINFO_H diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 25580f4b1..555a522eb 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -15,10 +15,12 @@ ** limitations under the License. */ -#define LOG_TAG "LatinIME: jni" +#define LOG_TAG "LatinIME: jni: BinaryDictionary" +#include "com_android_inputmethod_latin_BinaryDictionary.h" #include "dictionary.h" #include "jni.h" +#include "proximity_info.h" #include #include @@ -35,7 +37,7 @@ // ---------------------------------------------------------------------------- -using namespace latinime; +namespace latinime { // // helper function to throw an exception @@ -43,7 +45,7 @@ using namespace latinime; static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { if (jclass cls = env->FindClass(ex)) { char msg[1000]; - sprintf(msg, fmt, data); + snprintf(msg, sizeof(msg), fmt, data); env->ThrowNew(cls, msg); env->DeleteLocalRef(cls); } @@ -123,26 +125,29 @@ static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object, } static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict, - jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray, - jintArray nextLettersArray, jint nextLettersSize) { + jint proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray, + jintArray inputArray, jint arraySize, jint flags, + jcharArray outputArray, jintArray frequencyArray) { Dictionary *dictionary = (Dictionary*)dict; if (!dictionary) return 0; + ProximityInfo *pInfo = (ProximityInfo*)proximityInfo; + if (!pInfo) return 0; + + int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, NULL); + int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, NULL); int *frequencies = env->GetIntArrayElements(frequencyArray, NULL); int *inputCodes = env->GetIntArrayElements(inputArray, NULL); jchar *outputChars = env->GetCharArrayElements(outputArray, NULL); - int *nextLetters = nextLettersArray != NULL ? env->GetIntArrayElements(nextLettersArray, NULL) - : NULL; - int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, - frequencies, nextLetters, nextLettersSize); + int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes, + arraySize, flags, (unsigned short*) outputChars, frequencies); env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); + env->ReleaseIntArrayElements(xCoordinatesArray, xCoordinates, 0); + env->ReleaseIntArrayElements(yCoordinatesArray, yCoordinates, 0); env->ReleaseCharArrayElements(outputArray, outputChars, 0); - if (nextLetters) { - env->ReleaseIntArrayElements(nextLettersArray, nextLetters, 0); - } return count; } @@ -206,10 +211,10 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jint di // ---------------------------------------------------------------------------- -static JNINativeMethod gMethods[] = { +static JNINativeMethod sMethods[] = { {"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open}, {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close}, - {"getSuggestionsNative", "(I[II[C[I[II)I", (void*)latinime_BinaryDictionary_getSuggestions}, + {"getSuggestionsNative", "(II[I[I[III[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions}, {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}, {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams} }; @@ -231,33 +236,10 @@ static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMe return JNI_TRUE; } -static int registerNatives(JNIEnv *env) { +int register_BinaryDictionary(JNIEnv *env) { const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary"; - return registerNativeMethods(env, kClassPathName, gMethods, - sizeof(gMethods) / sizeof(gMethods[0])); + return registerNativeMethods(env, kClassPathName, sMethods, + sizeof(sMethods) / sizeof(sMethods[0])); } -/* - * Returns the JNI version on success, -1 on failure. - */ -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - JNIEnv* env = NULL; - jint result = -1; - - if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { - LOGE("ERROR: GetEnv failed"); - goto bail; - } - assert(env != NULL); - - if (!registerNatives(env)) { - LOGE("ERROR: BinaryDictionary native registration failed"); - goto bail; - } - - /* success -- return valid version number */ - result = JNI_VERSION_1_4; - -bail: - return result; -} +}; // namespace latinime diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.h b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h new file mode 100644 index 000000000..f7cd81fa7 --- /dev/null +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h @@ -0,0 +1,27 @@ +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_BINARYDICTIONARY_H +#define _COM_ANDROID_INPUTMETHOD_LATIN_BINARYDICTIONARY_H + +#include "jni.h" + +namespace latinime { +int register_BinaryDictionary(JNIEnv *env); +} + +#endif // _COM_ANDROID_INPUTMETHOD_LATIN_BINARYDICTIONARY_H diff --git a/native/jni/onload.cpp b/native/jni/onload.cpp new file mode 100644 index 000000000..f02c9a052 --- /dev/null +++ b/native/jni/onload.cpp @@ -0,0 +1,62 @@ +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "LatinIME: jni" + +#include "com_android_inputmethod_keyboard_ProximityInfo.h" +#include "com_android_inputmethod_latin_BinaryDictionary.h" +#include "jni.h" +#include "proximity_info.h" + +#include +#include +#include + +// ---------------------------------------------------------------------------- + +using namespace latinime; + + +/* + * Returns the JNI version on success, -1 on failure. + */ +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed"); + goto bail; + } + assert(env != NULL); + + if (!register_BinaryDictionary(env)) { + LOGE("ERROR: BinaryDictionary native registration failed"); + goto bail; + } + + if (!register_ProximityInfo(env)) { + LOGE("ERROR: ProximityInfo native registration failed"); + goto bail; + } + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + +bail: + return result; +} diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp index 5ec310f07..36761b88d 100644 --- a/native/src/bigram_dictionary.cpp +++ b/native/src/bigram_dictionary.cpp @@ -30,8 +30,10 @@ BigramDictionary::BigramDictionary(const unsigned char *dict, int maxWordLength, : DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_ALTERNATIVES(maxAlternatives), IS_LATEST_DICT_VERSION(isLatestDictVersion), HAS_BIGRAM(hasBigram), mParentDictionary(parentDictionary) { - if (DEBUG_DICT) LOGI("BigramDictionary - constructor"); - if (DEBUG_DICT) LOGI("Has Bigram : %d", hasBigram); + if (DEBUG_DICT) { + LOGI("BigramDictionary - constructor"); + LOGI("Has Bigram : %d", hasBigram); + } } BigramDictionary::~BigramDictionary() { @@ -54,7 +56,9 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ } insertAt++; } - if (DEBUG_DICT) LOGI("Bigram: InsertAt -> %d maxBigrams: %d", insertAt, mMaxBigrams); + if (DEBUG_DICT) { + LOGI("Bigram: InsertAt -> %d maxBigrams: %d", insertAt, mMaxBigrams); + } if (insertAt < mMaxBigrams) { memmove((char*) mBigramFreq + (insertAt + 1) * sizeof(mBigramFreq[0]), (char*) mBigramFreq + insertAt * sizeof(mBigramFreq[0]), @@ -68,7 +72,9 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ *dest++ = *word++; } *dest = 0; // NULL terminate - if (DEBUG_DICT) LOGI("Bigram: Added word at %d", insertAt); + if (DEBUG_DICT) { + LOGI("Bigram: Added word at %d", insertAt); + } return true; } return false; @@ -107,7 +113,9 @@ int BigramDictionary::getBigrams(unsigned short *prevWord, int prevWordLength, i if (HAS_BIGRAM && IS_LATEST_DICT_VERSION) { int pos = mParentDictionary->isValidWordRec( DICTIONARY_HEADER_SIZE, prevWord, 0, prevWordLength); - if (DEBUG_DICT) LOGI("Pos -> %d", pos); + if (DEBUG_DICT) { + LOGI("Pos -> %d", pos); + } if (pos < 0) { return 0; } @@ -151,7 +159,9 @@ void BigramDictionary::searchForTerminalNode(int addressLookingFor, int frequenc } pos = followDownBranchAddress; // pos start at count int count = DICT[pos] & 0xFF; - if (DEBUG_DICT) LOGI("count - %d",count); + if (DEBUG_DICT) { + LOGI("count - %d",count); + } pos++; for (int i = 0; i < count; i++) { // pos at data @@ -225,7 +235,9 @@ void BigramDictionary::searchForTerminalNode(int addressLookingFor, int frequenc } depth++; if (followDownBranchAddress == 0) { - if (DEBUG_DICT) LOGI("ERROR!!! Cannot find bigram!!"); + if (DEBUG_DICT) { + LOGI("ERROR!!! Cannot find bigram!!"); + } break; } } diff --git a/native/src/debug.h b/native/src/debug.h new file mode 100644 index 000000000..ae629b222 --- /dev/null +++ b/native/src/debug.h @@ -0,0 +1,69 @@ +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef LATINIME_DEBUG_H +#define LATINIME_DEBUG_H + +#include "defines.h" + +static inline unsigned char* convertToUnibyteString(unsigned short* input, unsigned char* output, + const unsigned int length) { + int i = 0; + for (; i <= length && input[i] != 0; ++i) + output[i] = input[i] & 0xFF; + output[i] = 0; + return output; +} +static inline unsigned char* convertToUnibyteStringAndReplaceLastChar(unsigned short* input, + unsigned char* output, const unsigned int length, unsigned char c) { + int i = 0; + for (; i <= length && input[i] != 0; ++i) + output[i] = input[i] & 0xFF; + output[i-1] = c; + output[i] = 0; + return output; +} +static inline void LOGI_S16(unsigned short* string, const unsigned int length) { + unsigned char tmp_buffer[length]; + convertToUnibyteString(string, tmp_buffer, length); + LOGI(">> %s", tmp_buffer); + // The log facility is throwing out log that comes too fast. The following + // is a dirty way of slowing down processing so that we can see all log. + // TODO : refactor this in a blocking log or something. + // usleep(10); +} +static inline void LOGI_S16_PLUS(unsigned short* string, const unsigned int length, + unsigned char c) { + unsigned char tmp_buffer[length+1]; + convertToUnibyteStringAndReplaceLastChar(string, tmp_buffer, length, c); + LOGI(">> %s", tmp_buffer); + // Likewise + // usleep(10); +} + +static inline void printDebug(const char* tag, int* codes, int codesSize, int MAX_PROXIMITY_CHARS) { + unsigned char *buf = (unsigned char*)malloc((1 + codesSize) * sizeof(*buf)); + + buf[codesSize] = 0; + while (--codesSize >= 0) + buf[codesSize] = (unsigned char)codes[codesSize * MAX_PROXIMITY_CHARS]; + LOGI("%s, WORD = %s", tag, buf); + + free(buf); +} + +#endif // LATINIME_DEBUG_H diff --git a/native/src/defines.h b/native/src/defines.h index c1eaf0df2..926120703 100644 --- a/native/src/defines.h +++ b/native/src/defines.h @@ -28,6 +28,7 @@ #define DEBUG_SHOW_FOUND_WORD DEBUG_DICT_FULL #define DEBUG_NODE DEBUG_DICT_FULL #define DEBUG_TRACE DEBUG_DICT_FULL +#define DEBUG_PROXIMITY_INFO true // Profiler #include @@ -76,13 +77,14 @@ static void prof_out(void) { } #else // FLAG_DBG -#define LOGE -#define LOGI +#define LOGE(fmt, ...) +#define LOGI(fmt, ...) #define DEBUG_DICT false #define DEBUG_DICT_FULL false #define DEBUG_SHOW_FOUND_WORD false #define DEBUG_NODE false #define DEBUG_TRACE false +#define DEBUG_PROXIMITY_INFO false #define PROF_BUF_SIZE 0 #define PROF_RESET @@ -100,6 +102,9 @@ static void prof_out(void) { #ifndef U_SHORT_MAX #define U_SHORT_MAX 1 << 16 #endif +#ifndef S_INT_MAX +#define S_INT_MAX 2147483647 // ((1 << 31) - 1) +#endif // Define this to use mmap() for dictionary loading. Undefine to use malloc() instead of mmap(). // We measured and compared performance of both, and found mmap() is fairly good in terms of @@ -124,33 +129,40 @@ static void prof_out(void) { #define DICTIONARY_HEADER_SIZE 2 #define NOT_VALID_WORD -99 +#define KEYCODE_SPACE ' ' + #define SUGGEST_WORDS_WITH_MISSING_CHARACTER true #define SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER true #define SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER true #define SUGGEST_WORDS_WITH_TRANSPOSED_CHARACTERS true +#define SUGGEST_WORDS_WITH_SPACE_PROXIMITY true // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent. -#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 75 +#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 70 #define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 80 #define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120 -// This is used as a bare multiplier (not subject to /100) -#define FULL_MATCH_ACCENTS_OR_CAPITALIZATION_DIFFER_MULTIPLIER 2 - // This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java // This is only used for the size of array. Not to be used in c functions. #define MAX_WORD_LENGTH_INTERNAL 48 #define MAX_DEPTH_MULTIPLIER 3 +// TODO: Reduce this constant if possible; check the maximum number of umlauts in the same German +// word in the dictionary +#define DEFAULT_MAX_UMLAUT_SEARCH_DEPTH 5 + // Minimum suggest depth for one word for all cases except for missing space suggestions. #define MIN_SUGGEST_DEPTH 1 #define MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION 3 #define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3 +// The size of next letters frequency array. Zero will disable the feature. +#define NEXT_LETTERS_SIZE 0 + #define min(a,b) ((a)<(b)?(a):(b)) #endif // LATINIME_DEFINES_H diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp index fe3375706..d69cb2a53 100644 --- a/native/src/dictionary.cpp +++ b/native/src/dictionary.cpp @@ -23,6 +23,7 @@ namespace latinime { +// TODO: Change the type of all keyCodes to uint32_t Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives) @@ -53,8 +54,7 @@ bool Dictionary::hasBigram() { } // TODO: use uint16_t instead of unsigned short -bool Dictionary::isValidWord(unsigned short *word, int length) -{ +bool Dictionary::isValidWord(unsigned short *word, int length) { if (IS_LATEST_DICT_VERSION) { return (isValidWordRec(DICTIONARY_HEADER_SIZE, word, 0, length) != NOT_VALID_WORD); } else { diff --git a/native/src/dictionary.h b/native/src/dictionary.h index cef1cf9eb..13b2a2816 100644 --- a/native/src/dictionary.h +++ b/native/src/dictionary.h @@ -19,6 +19,7 @@ #include "bigram_dictionary.h" #include "defines.h" +#include "proximity_info.h" #include "unigram_dictionary.h" namespace latinime { @@ -27,10 +28,10 @@ class Dictionary { public: Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler, int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives); - int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, - int *nextLetters, int nextLettersSize) { - return mUnigramDictionary->getSuggestions(codes, codesSize, outWords, frequencies, - nextLetters, nextLettersSize); + int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates, + int *codes, int codesSize, int flags, unsigned short *outWords, int *frequencies) { + return mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, + codesSize, flags, outWords, frequencies); } // TODO: Call mBigramDictionary instead of mUnigramDictionary @@ -40,6 +41,7 @@ public: return mBigramDictionary->getBigrams(word, length, codes, codesSize, outWords, frequencies, maxWordLength, maxBigrams, maxAlternatives); } + bool isValidWord(unsigned short *word, int length); int isValidWordRec(int pos, unsigned short *word, int offset, int length); void *getDict() { return (void *)mDict; } diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp new file mode 100644 index 000000000..102123c3c --- /dev/null +++ b/native/src/proximity_info.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define LOG_TAG "LatinIME: proximity_info.cpp" + +#include "proximity_info.h" + +namespace latinime { +ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, + const int keyboardHeight, const int gridWidth, const int gridHeight, + const uint32_t *proximityCharsArray) + : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), + KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), + CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), + CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight) { + const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; + mProximityCharsArray = new uint32_t[len]; + if (DEBUG_PROXIMITY_INFO) { + LOGI("Create proximity info array %d", len); + } + memcpy(mProximityCharsArray, proximityCharsArray, len * sizeof(mProximityCharsArray[0])); +} + +ProximityInfo::~ProximityInfo() { + delete[] mProximityCharsArray; +} + +inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const { + return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH)) + * MAX_PROXIMITY_CHARS_SIZE; +} + +bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { + const int startIndex = getStartIndexFromCoordinates(x, y); + if (DEBUG_PROXIMITY_INFO) { + LOGI("hasSpaceProximity: index %d", startIndex); + } + for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { + if (DEBUG_PROXIMITY_INFO) { + LOGI("Index: %d", mProximityCharsArray[startIndex + i]); + } + if (mProximityCharsArray[startIndex + i] == KEYCODE_SPACE) { + return true; + } + } + return false; +} +} // namespace latinime diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h new file mode 100644 index 000000000..c2062e8c5 --- /dev/null +++ b/native/src/proximity_info.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_PROXIMITY_INFO_H +#define LATINIME_PROXIMITY_INFO_H + +#include + +#include "defines.h" + +namespace latinime { + +class ProximityInfo { +public: + ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, + const int keybaordHeight, const int gridWidth, const int gridHeight, + const uint32_t *proximityCharsArray); + ~ProximityInfo(); + bool hasSpaceProximity(const int x, const int y) const; +private: + int getStartIndexFromCoordinates(const int x, const int y) const; + const int MAX_PROXIMITY_CHARS_SIZE; + const int KEYBOARD_WIDTH; + const int KEYBOARD_HEIGHT; + const int GRID_WIDTH; + const int GRID_HEIGHT; + const int CELL_WIDTH; + const int CELL_HEIGHT; + uint32_t *mProximityCharsArray; +}; +}; // namespace latinime +#endif // LATINIME_PROXIMITY_INFO_H diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index dfbe8228e..c18829014 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -29,20 +29,146 @@ namespace latinime { +const UnigramDictionary::digraph_t UnigramDictionary::GERMAN_UMLAUT_DIGRAPHS[] = + { { 'a', 'e' }, + { 'o', 'e' }, + { 'u', 'e' } }; + UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion) - : DICT(dict), MAX_WORD_LENGTH(maxWordLength),MAX_WORDS(maxWords), + : DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords), MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion), TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier), - ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0) { - if (DEBUG_DICT) LOGI("UnigramDictionary - constructor"); + ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0), + BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(*mInputCodes)), + MAX_UMLAUT_SEARCH_DEPTH(DEFAULT_MAX_UMLAUT_SEARCH_DEPTH) { + if (DEBUG_DICT) { + LOGI("UnigramDictionary - constructor"); + } } UnigramDictionary::~UnigramDictionary() {} -int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords, - int *frequencies, int *nextLetters, int nextLettersSize) { +static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize, + const int MAX_PROXIMITY_CHARS) { + return sizeof(*codes) * MAX_PROXIMITY_CHARS * codesSize; +} + +bool UnigramDictionary::isDigraph(const int* codes, const int i, const int codesSize) const { + + // There can't be a digraph if we don't have at least 2 characters to examine + if (i + 2 > codesSize) return false; + + // Search for the first char of some digraph + int lastDigraphIndex = -1; + const int thisChar = codes[i * MAX_PROXIMITY_CHARS]; + for (lastDigraphIndex = sizeof(GERMAN_UMLAUT_DIGRAPHS) / sizeof(GERMAN_UMLAUT_DIGRAPHS[0]) - 1; + lastDigraphIndex >= 0; --lastDigraphIndex) { + if (thisChar == GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].first) break; + } + // No match: return early + if (lastDigraphIndex < 0) return false; + + // It's an interesting digraph if the second char matches too. + return GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].second == codes[(i + 1) * MAX_PROXIMITY_CHARS]; +} + +// Mostly the same arguments as the non-recursive version, except: +// codes is the original value. It points to the start of the work buffer, and gets passed as is. +// codesSize is the size of the user input (thus, it is the size of codesSrc). +// codesDest is the current point in the work buffer. +// codesSrc is the current point in the user-input, original, content-unmodified buffer. +// codesRemain is the remaining size in codesSrc. +void UnigramDictionary::getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo, + const int *xcoordinates, const int* ycoordinates, const int *codesBuffer, + const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain, + const int currentDepth, int* codesDest, unsigned short* outWords, int* frequencies) { + + if (currentDepth < MAX_UMLAUT_SEARCH_DEPTH) { + for (int i = 0; i < codesRemain; ++i) { + if (isDigraph(codesSrc, i, codesRemain)) { + // Found a digraph. We will try both spellings. eg. the word is "pruefen" + + // Copy the word up to the first char of the digraph, then continue processing + // on the remaining part of the word, skipping the second char of the digraph. + // In our example, copy "pru" and continue running on "fen" + // Make i the index of the second char of the digraph for simplicity. Forgetting + // to do that results in an infinite recursion so take care! + ++i; + memcpy(codesDest, codesSrc, i * BYTES_IN_ONE_CHAR); + getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, + codesBuffer, codesBufferSize, flags, + codesSrc + (i + 1) * MAX_PROXIMITY_CHARS, codesRemain - i - 1, + currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS, outWords, + frequencies); + + // Copy the second char of the digraph in place, then continue processing on + // the remaining part of the word. + // In our example, after "pru" in the buffer copy the "e", and continue on "fen" + memcpy(codesDest + i * MAX_PROXIMITY_CHARS, codesSrc + i * MAX_PROXIMITY_CHARS, + BYTES_IN_ONE_CHAR); + getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, + codesBuffer, codesBufferSize, flags, codesSrc + i * MAX_PROXIMITY_CHARS, + codesRemain - i, currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS, + outWords, frequencies); + return; + } + } + } + + // If we come here, we hit the end of the word: let's check it against the dictionary. + // In our example, we'll come here once for "prufen" and then once for "pruefen". + // If the word contains several digraphs, we'll come it for the product of them. + // eg. if the word is "ueberpruefen" we'll test, in order, against + // "uberprufen", "uberpruefen", "ueberprufen", "ueberpruefen". + const unsigned int remainingBytes = BYTES_IN_ONE_CHAR * codesRemain; + if (0 != remainingBytes) + memcpy(codesDest, codesSrc, remainingBytes); + + getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codesBuffer, + (codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies); +} + +int UnigramDictionary::getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates, + const int *ycoordinates, const int *codes, const int codesSize, const int flags, + unsigned short *outWords, int *frequencies) { + + if (REQUIRES_GERMAN_UMLAUT_PROCESSING & flags) + { // Incrementally tune the word and try all possibilities + int codesBuffer[getCodesBufferSize(codes, codesSize, MAX_PROXIMITY_CHARS)]; + getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer, + codesSize, flags, codes, codesSize, 0, codesBuffer, outWords, frequencies); + } else { // Normal processing + getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize, + outWords, frequencies); + } + + PROF_START(20); + // Get the word count + int suggestedWordsCount = 0; + while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) { + suggestedWordsCount++; + } + + if (DEBUG_DICT) { + LOGI("Returning %d words", suggestedWordsCount); + LOGI("Next letters: "); + for (int k = 0; k < NEXT_LETTERS_SIZE; k++) { + if (mNextLettersFrequency[k] > 0) { + LOGI("%c = %d,", k, mNextLettersFrequency[k]); + } + } + } + PROF_END(20); + PROF_CLOSE; + return suggestedWordsCount; +} + +void UnigramDictionary::getWordSuggestions(const ProximityInfo *proximityInfo, + const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, + unsigned short *outWords, int *frequencies) { + PROF_OPEN; PROF_START(0); initSuggestions(codes, codesSize, outWords, frequencies); @@ -52,14 +178,16 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short PROF_END(0); PROF_START(1); - getSuggestionCandidates(-1, -1, -1, nextLetters, nextLettersSize, MAX_DEPTH); + getSuggestionCandidates(-1, -1, -1, mNextLettersFrequency, NEXT_LETTERS_SIZE, MAX_DEPTH); PROF_END(1); PROF_START(2); // Suggestion with missing character if (SUGGEST_WORDS_WITH_MISSING_CHARACTER) { for (int i = 0; i < codesSize; ++i) { - if (DEBUG_DICT) LOGI("--- Suggest missing characters %d", i); + if (DEBUG_DICT) { + LOGI("--- Suggest missing characters %d", i); + } getSuggestionCandidates(i, -1, -1, NULL, 0, MAX_DEPTH); } } @@ -70,7 +198,9 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short if (SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER && mInputLength >= MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION) { for (int i = 0; i < codesSize; ++i) { - if (DEBUG_DICT) LOGI("--- Suggest excessive characters %d", i); + if (DEBUG_DICT) { + LOGI("--- Suggest excessive characters %d", i); + } getSuggestionCandidates(-1, i, -1, NULL, 0, MAX_DEPTH); } } @@ -81,7 +211,9 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short // Only suggest words that length is mInputLength if (SUGGEST_WORDS_WITH_TRANSPOSED_CHARACTERS) { for (int i = 0; i < codesSize; ++i) { - if (DEBUG_DICT) LOGI("--- Suggest transposed characters %d", i); + if (DEBUG_DICT) { + LOGI("--- Suggest transposed characters %d", i); + } getSuggestionCandidates(-1, -1, i, NULL, 0, mInputLength - 1); } } @@ -92,36 +224,40 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short if (SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER && mInputLength >= MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION) { for (int i = 1; i < codesSize; ++i) { - if (DEBUG_DICT) LOGI("--- Suggest missing space characters %d", i); + if (DEBUG_DICT) { + LOGI("--- Suggest missing space characters %d", i); + } getMissingSpaceWords(mInputLength, i); } } PROF_END(5); PROF_START(6); - // Get the word count - int suggestedWordsCount = 0; - while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) { - suggestedWordsCount++; - } - - if (DEBUG_DICT) { - LOGI("Returning %d words", suggestedWordsCount); - LOGI("Next letters: "); - for (int k = 0; k < nextLettersSize; k++) { - if (nextLetters[k] > 0) { - LOGI("%c = %d,", k, nextLetters[k]); + if (SUGGEST_WORDS_WITH_SPACE_PROXIMITY) { + // The first and last "mistyped spaces" are taken care of by excessive character handling + for (int i = 1; i < codesSize - 1; ++i) { + if (DEBUG_DICT) { + LOGI("--- Suggest words with proximity space %d", i); + } + const int x = xcoordinates[i]; + const int y = ycoordinates[i]; + if (DEBUG_PROXIMITY_INFO) { + LOGI("Input[%d] x = %d, y = %d, has space proximity = %d", + i, x, y, proximityInfo->hasSpaceProximity(x, y)); + } + if (proximityInfo->hasSpaceProximity(x, y)) { + getMistypedSpaceWords(mInputLength, i); } } } PROF_END(6); - PROF_CLOSE; - return suggestedWordsCount; } -void UnigramDictionary::initSuggestions(int *codes, int codesSize, unsigned short *outWords, - int *frequencies) { - if (DEBUG_DICT) LOGI("initSuggest"); +void UnigramDictionary::initSuggestions(const int *codes, const int codesSize, + unsigned short *outWords, int *frequencies) { + if (DEBUG_DICT) { + LOGI("initSuggest"); + } mFrequencies = frequencies; mOutputChars = outWords; mInputCodes = codes; @@ -145,7 +281,9 @@ bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) LOGI("Found word = %s, freq = %d", s, frequency); } if (length > MAX_WORD_LENGTH) { - if (DEBUG_DICT) LOGI("Exceeded max word length."); + if (DEBUG_DICT) { + LOGI("Exceeded max word length."); + } return false; } @@ -176,13 +314,15 @@ bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) *dest++ = *word++; } *dest = 0; // NULL terminate - if (DEBUG_DICT) LOGI("Added word at %d", insertAt); + if (DEBUG_DICT) { + LOGI("Added word at %d", insertAt); + } return true; } return false; } -unsigned short UnigramDictionary::toLowerCase(unsigned short c) { +unsigned short UnigramDictionary::toBaseLowerCase(unsigned short c) { if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) { c = BASE_CHARS[c]; } @@ -198,7 +338,7 @@ bool UnigramDictionary::sameAsTyped(unsigned short *word, int length) { if (length != mInputLength) { return false; } - int *inputCodes = mInputCodes; + const int *inputCodes = mInputCodes; while (length--) { if ((unsigned int) *inputCodes != (unsigned int) *word) { return false; @@ -238,7 +378,7 @@ void UnigramDictionary::getSuggestionCandidates(const int skipPos, if (mStackChildCount[depth] > 0) { --mStackChildCount[depth]; bool traverseAllNodes = mStackTraverseAll[depth]; - int snr = mStackNodeFreq[depth]; + int matchWeight = mStackNodeFreq[depth]; int inputIndex = mStackInputIndex[depth]; int diffs = mStackDiffs[depth]; int siblingPos = mStackSiblingPos[depth]; @@ -246,9 +386,10 @@ void UnigramDictionary::getSuggestionCandidates(const int skipPos, // depth will never be greater than maxDepth because in that case, // needsToTraverseChildrenNodes should be false const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth, - maxDepth, traverseAllNodes, snr, inputIndex, diffs, skipPos, excessivePos, - transposedPos, nextLetters, nextLettersSize, &childCount, &firstChildPos, - &traverseAllNodes, &snr, &inputIndex, &diffs, &siblingPos); + maxDepth, traverseAllNodes, matchWeight, inputIndex, diffs, skipPos, + excessivePos, transposedPos, nextLetters, nextLettersSize, &childCount, + &firstChildPos, &traverseAllNodes, &matchWeight, &inputIndex, &diffs, + &siblingPos); // Update next sibling pos mStackSiblingPos[depth] = siblingPos; if (needsToTraverseChildrenNodes) { @@ -256,7 +397,7 @@ void UnigramDictionary::getSuggestionCandidates(const int skipPos, ++depth; mStackChildCount[depth] = childCount; mStackTraverseAll[depth] = traverseAllNodes; - mStackNodeFreq[depth] = snr; + mStackNodeFreq[depth] = matchWeight; mStackInputIndex[depth] = inputIndex; mStackDiffs[depth] = diffs; mStackSiblingPos[depth] = firstChildPos; @@ -276,27 +417,35 @@ inline static void multiplyRate(const int rate, int *freq) { } } -bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) { - if (missingSpacePos <= 0 || missingSpacePos >= inputLength - || inputLength >= MAX_WORD_LENGTH) return false; - const int newWordLength = inputLength + 1; +bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, + const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos, + const int secondWordLength) { + if (inputLength >= MAX_WORD_LENGTH) return false; + if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos + || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength) + return false; + const int newWordLength = firstWordLength + secondWordLength + 1; // Allocating variable length array on stack unsigned short word[newWordLength]; - const int firstFreq = getBestWordFreq(0, missingSpacePos, mWord); - if (DEBUG_DICT) LOGI("First freq: %d", firstFreq); + const int firstFreq = getBestWordFreq(firstWordStartPos, firstWordLength, mWord); + if (DEBUG_DICT) { + LOGI("First freq: %d", firstFreq); + } if (firstFreq <= 0) return false; - for (int i = 0; i < missingSpacePos; ++i) { + for (int i = 0; i < firstWordLength; ++i) { word[i] = mWord[i]; } - const int secondFreq = getBestWordFreq(missingSpacePos, inputLength - missingSpacePos, mWord); - if (DEBUG_DICT) LOGI("Second freq: %d", secondFreq); + const int secondFreq = getBestWordFreq(secondWordStartPos, secondWordLength, mWord); + if (DEBUG_DICT) { + LOGI("Second freq: %d", secondFreq); + } if (secondFreq <= 0) return false; - word[missingSpacePos] = SPACE; - for (int i = (missingSpacePos + 1); i < newWordLength; ++i) { - word[i] = mWord[i - missingSpacePos - 1]; + word[firstWordLength] = SPACE; + for (int i = (firstWordLength + 1); i < newWordLength; ++i) { + word[i] = mWord[i - firstWordLength - 1]; } int pairFreq = ((firstFreq + secondFreq) / 2); @@ -306,6 +455,17 @@ bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int mi return true; } +bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) { + return getSplitTwoWordsSuggestion( + inputLength, 0, missingSpacePos, missingSpacePos, inputLength - missingSpacePos); +} + +bool UnigramDictionary::getMistypedSpaceWords(const int inputLength, const int spaceProximityPos) { + return getSplitTwoWordsSuggestion( + inputLength, 0, spaceProximityPos, spaceProximityPos + 1, + inputLength - spaceProximityPos - 1); +} + // Keep this for comparing spec to new getWords void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, const int skipPos, const int excessivePos, const int transposedPos,int *nextLetters, @@ -319,40 +479,52 @@ void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, } void UnigramDictionary::getWordsRec(const int childrenCount, const int pos, const int depth, - const int maxDepth, const bool traverseAllNodes, const int snr, const int inputIndex, - const int diffs, const int skipPos, const int excessivePos, const int transposedPos, - int *nextLetters, const int nextLettersSize) { + const int maxDepth, const bool traverseAllNodes, const int matchWeight, + const int inputIndex, const int diffs, const int skipPos, const int excessivePos, + const int transposedPos, int *nextLetters, const int nextLettersSize) { int siblingPos = pos; for (int i = 0; i < childrenCount; ++i) { int newCount; int newChildPosition; const int newDepth = depth + 1; bool newTraverseAllNodes; - int newSnr; + int newMatchRate; int newInputIndex; int newDiffs; int newSiblingPos; const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth, maxDepth, - traverseAllNodes, snr, inputIndex, diffs, skipPos, excessivePos, transposedPos, + traverseAllNodes, matchWeight, inputIndex, diffs, + skipPos, excessivePos, transposedPos, nextLetters, nextLettersSize, - &newCount, &newChildPosition, &newTraverseAllNodes, &newSnr, + &newCount, &newChildPosition, &newTraverseAllNodes, &newMatchRate, &newInputIndex, &newDiffs, &newSiblingPos); siblingPos = newSiblingPos; if (needsToTraverseChildrenNodes) { getWordsRec(newCount, newChildPosition, newDepth, maxDepth, newTraverseAllNodes, - newSnr, newInputIndex, newDiffs, skipPos, excessivePos, transposedPos, + newMatchRate, newInputIndex, newDiffs, skipPos, excessivePos, transposedPos, nextLetters, nextLettersSize); } } } +static const int TWO_31ST_DIV_255 = S_INT_MAX / 255; +static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) { + return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX); +} inline int UnigramDictionary::calculateFinalFreq(const int inputIndex, const int depth, - const int snr, const int skipPos, const int excessivePos, const int transposedPos, - const int freq, const bool sameLength) { + const int matchWeight, const int skipPos, const int excessivePos, const int transposedPos, + const int freq, const bool sameLength) const { // TODO: Demote by edit distance - int finalFreq = freq * snr; - if (skipPos >= 0) multiplyRate(WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE, &finalFreq); + int finalFreq = freq * matchWeight; + if (skipPos >= 0) { + if (mInputLength >= 3) { + multiplyRate(WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE * + (mInputLength - 2) / (mInputLength - 1), &finalFreq); + } else { + finalFreq = 0; + } + } if (transposedPos >= 0) multiplyRate( WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq); if (excessivePos >= 0) { @@ -363,24 +535,26 @@ inline int UnigramDictionary::calculateFinalFreq(const int inputIndex, const int } int lengthFreq = TYPED_LETTER_MULTIPLIER; for (int i = 0; i < depth; ++i) lengthFreq *= TYPED_LETTER_MULTIPLIER; - if (lengthFreq == snr) { + if (lengthFreq == matchWeight) { if (depth > 1) { - if (DEBUG_DICT) LOGI("Found full matched word."); + if (DEBUG_DICT) { + LOGI("Found full matched word."); + } multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq); } if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) { - finalFreq *= FULL_MATCH_ACCENTS_OR_CAPITALIZATION_DIFFER_MULTIPLIER; + finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq); } } - if (sameLength && skipPos < 0) finalFreq *= FULL_WORD_MULTIPLIER; + if (sameLength) finalFreq *= FULL_WORD_MULTIPLIER; return finalFreq; } inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsGreaterThanInputLength( - unsigned short *word, const int inputIndex, const int depth, const int snr, + unsigned short *word, const int inputIndex, const int depth, const int matchWeight, int *nextLetters, const int nextLettersSize, const int skipPos, const int excessivePos, const int transposedPos, const int freq) { - const int finalFreq = calculateFinalFreq(inputIndex, depth, snr, skipPos, excessivePos, + const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos, excessivePos, transposedPos, freq, false); if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq); if (depth >= mInputLength && skipPos < 0) { @@ -389,10 +563,10 @@ inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsGreaterThanInputLe } inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsSameAsInputLength( - unsigned short *word, const int inputIndex, const int depth, const int snr, + unsigned short *word, const int inputIndex, const int depth, const int matchWeight, const int skipPos, const int excessivePos, const int transposedPos, const int freq) { if (sameAsTyped(word, depth + 1)) return; - const int finalFreq = calculateFinalFreq(inputIndex, depth, snr, skipPos, + const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos, excessivePos, transposedPos, freq, true); // Proximity collection will promote a word of the same length as what user typed. if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq); @@ -400,18 +574,18 @@ inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsSameAsInputLength( inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c, const int inputIndex, const int skipPos, const int depth) { - const unsigned short userTypedChar = (mInputCodes + (inputIndex * MAX_PROXIMITY_CHARS))[0]; + const unsigned short userTypedChar = getInputCharsAt(inputIndex)[0]; // Skip the ' or other letter and continue deeper return (c == QUOTE && userTypedChar != QUOTE) || skipPos == depth; } inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex, - const int inputLength) { + const int inputLength) const { if (inputIndex < 0 || inputIndex >= inputLength) return false; const int currentChar = *getInputCharsAt(inputIndex); const int leftIndex = inputIndex - 1; if (leftIndex >= 0) { - int *leftChars = getInputCharsAt(leftIndex); + const int *leftChars = getInputCharsAt(leftIndex); int i = 0; while (leftChars[i] > 0 && i < MAX_PROXIMITY_CHARS) { if (leftChars[i++] == currentChar) return true; @@ -419,7 +593,7 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex } const int rightIndex = inputIndex + 1; if (rightIndex < inputLength) { - int *rightChars = getInputCharsAt(rightIndex); + const int *rightChars = getInputCharsAt(rightIndex); int i = 0; while (rightChars[i] > 0 && i < MAX_PROXIMITY_CHARS) { if (rightChars[i++] == currentChar) return true; @@ -428,32 +602,54 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex return false; } + +// In the following function, c is the current character of the dictionary word +// currently examined. +// currentChars is an array containing the keys close to the character the +// user actually typed at the same position. We want to see if c is in it: if so, +// then the word contains at that position a character close to what the user +// typed. +// What the user typed is actually the first character of the array. +// Notice : accented characters do not have a proximity list, so they are alone +// in their list. The non-accented version of the character should be considered +// "close", but not the other keys close to the non-accented version. inline UnigramDictionary::ProximityType UnigramDictionary::getMatchedProximityId( const int *currentChars, const unsigned short c, const int skipPos, const int excessivePos, const int transposedPos) { - const unsigned short lowerC = toLowerCase(c); - int j = 0; + const unsigned short baseLowerC = toBaseLowerCase(c); + + // The first char in the array is what user typed. If it matches right away, + // that means the user typed that same char for this pos. + if (currentChars[0] == baseLowerC || currentChars[0] == c) + return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR; + + // If one of those is true, we should not check for close characters at all. + if (skipPos >= 0 || excessivePos >= 0 || transposedPos >= 0) + return UNRELATED_CHAR; + + // If the non-accented, lowercased version of that first character matches c, + // then we have a non-accented version of the accented character the user + // typed. Treat it as a close char. + if (toBaseLowerCase(currentChars[0]) == baseLowerC) + return NEAR_PROXIMITY_CHAR; + + // Not an exact nor an accent-alike match: search the list of close keys + int j = 1; while (currentChars[j] > 0 && j < MAX_PROXIMITY_CHARS) { - const bool matched = (currentChars[j] == lowerC || currentChars[j] == c); - // If skipPos is defined, not to search proximity collections. - // First char is what user typed. - if (matched) { - if (j > 0) return NEAR_PROXIMITY_CHAR; - return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR; - } else if (skipPos >= 0 || excessivePos >= 0 || transposedPos >= 0) { - // Not to check proximity characters - return UNRELATED_CHAR; - } + const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c); + if (matched) return NEAR_PROXIMITY_CHAR; ++j; } + + // Was not included, signal this as an unrelated character. return UNRELATED_CHAR; } inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth, - const int maxDepth, const bool traverseAllNodes, int snr, int inputIndex, + const int maxDepth, const bool traverseAllNodes, int matchWeight, int inputIndex, const int diffs, const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition, - bool *newTraverseAllNodes, int *newSnr, int*newInputIndex, int *newDiffs, + bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs, int *nextSiblingPosition) { if (DEBUG_DICT) { int inputCount = 0; @@ -480,15 +676,16 @@ inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth mWord[depth] = c; if (traverseAllNodes && terminal) { onTerminalWhenUserTypedLengthIsGreaterThanInputLength(mWord, inputIndex, depth, - snr, nextLetters, nextLettersSize, skipPos, excessivePos, transposedPos, freq); + matchWeight, nextLetters, nextLettersSize, skipPos, excessivePos, transposedPos, + freq); } if (!needsToTraverseChildrenNodes) return false; *newTraverseAllNodes = traverseAllNodes; - *newSnr = snr; + *newMatchRate = matchWeight; *newDiffs = diffs; *newInputIndex = inputIndex; } else { - int *currentChars = mInputCodes + (inputIndex * MAX_PROXIMITY_CHARS); + const int *currentChars = getInputCharsAt(inputIndex); if (transposedPos >= 0) { if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS; @@ -502,18 +699,18 @@ inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth // If inputIndex is greater than mInputLength, that means there is no // proximity chars. So, we don't need to check proximity. if (SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { - snr = snr * TYPED_LETTER_MULTIPLIER; + matchWeight = matchWeight * TYPED_LETTER_MULTIPLIER; } bool isSameAsUserTypedLength = mInputLength == inputIndex + 1 || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2); if (isSameAsUserTypedLength && terminal) { - onTerminalWhenUserTypedLengthIsSameAsInputLength(mWord, inputIndex, depth, snr, + onTerminalWhenUserTypedLengthIsSameAsInputLength(mWord, inputIndex, depth, matchWeight, skipPos, excessivePos, transposedPos, freq); } if (!needsToTraverseChildrenNodes) return false; // Start traversing all nodes after the index exceeds the user typed length *newTraverseAllNodes = isSameAsUserTypedLength; - *newSnr = snr; + *newMatchRate = matchWeight; *newDiffs = diffs + ((NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0); *newInputIndex = inputIndex + 1; } @@ -591,20 +788,24 @@ inline bool UnigramDictionary::processCurrentNodeForExactMatch(const int firstCh const int startInputIndex, const int depth, unsigned short *word, int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos) { const int inputIndex = startInputIndex + depth; - const int *currentChars = mInputCodes + (inputIndex * MAX_PROXIMITY_CHARS); + const int *currentChars = getInputCharsAt(inputIndex); unsigned short c; *siblingPos = Dictionary::setDictionaryValues(DICT, IS_LATEST_DICT_VERSION, firstChildPos, &c, newChildPosition, newTerminal, newFreq); const unsigned int inputC = currentChars[0]; - if (DEBUG_DICT) assert(inputC <= U_SHORT_MAX); - const unsigned short lowerC = toLowerCase(c); - const bool matched = (inputC == lowerC || inputC == c); + if (DEBUG_DICT) { + assert(inputC <= U_SHORT_MAX); + } + const unsigned short baseLowerC = toBaseLowerCase(c); + const bool matched = (inputC == baseLowerC || inputC == c); const bool hasChild = *newChildPosition != 0; if (matched) { word[depth] = c; if (DEBUG_DICT && DEBUG_NODE) { LOGI("Node(%c, %c)<%d>, %d, %d", inputC, c, matched, hasChild, *newFreq); - if (*newTerminal) LOGI("Terminal %d", *newFreq); + if (*newTerminal) { + LOGI("Terminal %d", *newFreq); + } } if (hasChild) { *newCount = Dictionary::getCount(DICT, newChildPosition); diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h index 90c98149b..3d3007ce0 100644 --- a/native/src/unigram_dictionary.h +++ b/native/src/unigram_dictionary.h @@ -18,6 +18,7 @@ #define LATINIME_UNIGRAM_DICTIONARY_H #include "defines.h" +#include "proximity_info.h" namespace latinime { @@ -32,12 +33,22 @@ class UnigramDictionary { public: UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion); - int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, - int *nextLetters, int nextLettersSize); + int getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates, + const int *ycoordinates, const int *codes, const int codesSize, const int flags, + unsigned short *outWords, int *frequencies); ~UnigramDictionary(); private: - void initSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies); + void getWordSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates, + const int *ycoordinates, const int *codes, const int codesSize, + unsigned short *outWords, int *frequencies); + bool isDigraph(const int* codes, const int i, const int codesSize) const; + void getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo, + const int *xcoordinates, const int* ycoordinates, const int *codesBuffer, + const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain, + const int currentDepth, int* codesDest, unsigned short* outWords, int* frequencies); + void initSuggestions(const int *codes, const int codesSize, unsigned short *outWords, + int *frequencies); void getSuggestionCandidates(const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, const int nextLettersSize, const int maxDepth); @@ -48,19 +59,24 @@ private: int wideStrLen(unsigned short *str); bool sameAsTyped(unsigned short *word, int length); bool addWord(unsigned short *word, int length, int frequency); - unsigned short toLowerCase(unsigned short c); + unsigned short toBaseLowerCase(unsigned short c); void getWordsRec(const int childrenCount, const int pos, const int depth, const int maxDepth, const bool traverseAllNodes, const int snr, const int inputIndex, const int diffs, const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, const int nextLettersSize); + bool getSplitTwoWordsSuggestion(const int inputLength, + const int firstWordStartPos, const int firstWordLength, + const int secondWordStartPos, const int secondWordLength); bool getMissingSpaceWords(const int inputLength, const int missingSpacePos); + bool getMistypedSpaceWords(const int inputLength, const int spaceProximityPos); // Keep getWordsOld for comparing performance between getWords and getWordsOld void getWordsOld(const int initialPos, const int inputLength, const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, const int nextLettersSize); void registerNextLetter(unsigned short c, int *nextLetters, int nextLettersSize); int calculateFinalFreq(const int inputIndex, const int depth, const int snr, const int skipPos, - const int excessivePos, const int transposedPos, const int freq, const bool sameLength); + const int excessivePos, const int transposedPos, const int freq, + const bool sameLength) const; void onTerminalWhenUserTypedLengthIsGreaterThanInputLength(unsigned short *word, const int inputIndex, const int depth, const int snr, int *nextLetters, const int nextLettersSize, const int skipPos, const int excessivePos, @@ -84,8 +100,10 @@ private: bool processCurrentNodeForExactMatch(const int firstChildPos, const int startInputIndex, const int depth, unsigned short *word, int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos); - bool existsAdjacentProximityChars(const int inputIndex, const int inputLength); - int* getInputCharsAt(const int index) {return mInputCodes + (index * MAX_PROXIMITY_CHARS);} + bool existsAdjacentProximityChars(const int inputIndex, const int inputLength) const; + inline const int* getInputCharsAt(const int index) const { + return mInputCodes + (index * MAX_PROXIMITY_CHARS); + } const unsigned char *DICT; const int MAX_WORD_LENGTH; const int MAX_WORDS; @@ -94,10 +112,21 @@ private: const int TYPED_LETTER_MULTIPLIER; const int FULL_WORD_MULTIPLIER; const int ROOT_POS; + const unsigned int BYTES_IN_ONE_CHAR; + const int MAX_UMLAUT_SEARCH_DEPTH; + + // Flags for special processing + // Those *must* match the flags in BinaryDictionary.Flags.ALL_FLAGS in BinaryDictionary.java + // or something very bad (like, the apocalypse) will happen. + // Please update both at the same time. + enum { + REQUIRES_GERMAN_UMLAUT_PROCESSING = 0x1 + }; + static const struct digraph_t { int first; int second; } GERMAN_UMLAUT_DIGRAPHS[]; int *mFrequencies; unsigned short *mOutputChars; - int *mInputCodes; + const int *mInputCodes; int mInputLength; // MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH unsigned short mWord[MAX_WORD_LENGTH_INTERNAL]; @@ -109,6 +138,7 @@ private: int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL]; int mStackDiffs[MAX_WORD_LENGTH_INTERNAL]; int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL]; + int mNextLettersFrequency[NEXT_LETTERS_SIZE]; }; // ---------------------------------------------------------------------------- diff --git a/tests/Android.mk b/tests/Android.mk index 60e82d5c2..6845bfe10 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -7,6 +7,11 @@ LOCAL_CERTIFICATE := shared LOCAL_JAVA_LIBRARIES := android.test.runner +# Do not compress dictionary files to mmap dict data runtime +LOCAL_AAPT_FLAGS += -0 .dict +# Do not compress test data file +LOCAL_AAPT_FLAGS += -0 .txt + # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java index e1c3678fd..d128cb3fa 100644 --- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java +++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java @@ -31,7 +31,7 @@ public class SubtypeLocaleTests extends AndroidTestCase { private static final String PACKAGE = LatinIME.class.getPackage().getName(); private Resources mRes; - private List mKeyboardSubtypes; + private List mKeyboardSubtypes = new ArrayList(); @Override protected void setUp() throws Exception { @@ -60,11 +60,6 @@ public class SubtypeLocaleTests extends AndroidTestCase { assertTrue("Can not find keyboard subtype", mKeyboardSubtypes.size() > 0); } - // Copied from {@link java.junit.Assert#format(String, Object, Object)} - private static String format(String message, Object expected, Object actual) { - return message + " expected:<" + expected + "> but was:<" + actual + ">"; - } - private String getStringWithLocale(int resId, Locale locale) { final Locale savedLocale = Locale.getDefault(); try { @@ -76,6 +71,8 @@ public class SubtypeLocaleTests extends AndroidTestCase { } public void testSubtypeLocale() { + final StringBuilder messages = new StringBuilder(); + int failedCount = 0; for (final InputMethodSubtype subtype : mKeyboardSubtypes) { final String localeCode = subtype.getLocale(); final Locale locale = new Locale(localeCode); @@ -85,9 +82,13 @@ public class SubtypeLocaleTests extends AndroidTestCase { // The subtype name in its locale. For example 'English (US) Keyboard' or // 'Clavier Francais (Canada)'. (c=\u008d) final String subtypeName = getStringWithLocale(subtype.getNameResId(), locale); - assertTrue( - format("subtype display name of " + localeCode + ":", subtypeName, displayName), - subtypeName.contains(displayName)); + if (subtypeName.contains(displayName)) { + failedCount++; + messages.append(String.format( + "subtype name is '%s' and should contain locale '%s' name '%s'\n", + subtypeName, localeCode, displayName)); + } } + assertEquals(messages.toString(), 0, failedCount); } } diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java index bcc0d6cde..1d0a5b7eb 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java @@ -16,98 +16,114 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.LatinKeyboard; +import com.android.inputmethod.keyboard.ProximityKeyDetector; + import android.content.Context; import android.text.TextUtils; -import android.util.Log; -import com.android.inputmethod.latin.Suggest; -import com.android.inputmethod.latin.UserBigramDictionary; -import com.android.inputmethod.latin.WordComposer; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.Channels; +import java.io.File; import java.util.List; -import java.util.Locale; -import java.util.StringTokenizer; public class SuggestHelper { - private Suggest mSuggest; - private UserBigramDictionary mUserBigram; - private final String TAG; + protected final Suggest mSuggest; + private final LatinKeyboard mKeyboard; + private final KeyDetector mKeyDetector; - /** Uses main dictionary only **/ - public SuggestHelper(String tag, Context context, int resId) { - TAG = tag; - mSuggest = new Suggest(context, resId); + public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) { + // Use null as the locale for Suggest so as to force it to use the internal dictionary + // (and not try to find a dictionary provider for a specified locale) + mSuggest = new Suggest(context, dictionaryId, null); + mKeyboard = new LatinKeyboard(context, keyboardId); + mKeyDetector = new ProximityKeyDetector(); + init(); + } + + protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length, + KeyboardId keyboardId) { + mSuggest = new Suggest(dictionaryPath, startOffset, length, null); + mKeyboard = new LatinKeyboard(context, keyboardId); + mKeyDetector = new ProximityKeyDetector(); + init(); + } + + private void init() { mSuggest.setQuickFixesEnabled(false); - mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM); + mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL); + mKeyDetector.setKeyboard(mKeyboard, 0, 0); + mKeyDetector.setProximityCorrectionEnabled(true); + mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(mKeyboard)); } - /** Uses both main dictionary and user-bigram dictionary **/ - public SuggestHelper(String tag, Context context, int resId, int userBigramMax, - int userBigramDelete) { - this(tag, context, resId); - mUserBigram = new UserBigramDictionary(context, null, Locale.US.toString(), - Suggest.DIC_USER); - mUserBigram.setDatabaseMax(userBigramMax); - mUserBigram.setDatabaseDelete(userBigramDelete); - mSuggest.setUserBigramDictionary(mUserBigram); + public void setCorrectionMode(int correctionMode) { + mSuggest.setCorrectionMode(correctionMode); } - void changeUserBigramLocale(Context context, Locale locale) { - if (mUserBigram != null) { - flushUserBigrams(); - mUserBigram.close(); - mUserBigram = new UserBigramDictionary(context, null, locale.toString(), - Suggest.DIC_USER); - mSuggest.setUserBigramDictionary(mUserBigram); + public boolean hasMainDictionary() { + return mSuggest.hasMainDictionary(); + } + + private void addKeyInfo(WordComposer word, char c) { + final List keys = mKeyboard.getKeys(); + for (final Key key : keys) { + if (key.mCode == c) { + final int x = key.mX + key.mWidth / 2; + final int y = key.mY + key.mHeight / 2; + final int[] codes = mKeyDetector.newCodeArray(); + mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); + word.add(c, codes, x, y); + return; + } } + word.add(c, new int[] { c }, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } - private WordComposer createWordComposer(CharSequence s) { + protected WordComposer createWordComposer(CharSequence s) { WordComposer word = new WordComposer(); for (int i = 0; i < s.length(); i++) { final char c = s.charAt(i); - int[] codes; - // If it's not a lowercase letter, don't find adjacent letters - if (c < 'a' || c > 'z') { - codes = new int[] { c }; - } else { - codes = adjacents[c - 'a']; - } - word.add(c, codes); + addKeyInfo(word, c); } return word; } - private boolean isDefaultSuggestion(SuggestedWords suggestions, CharSequence word) { - // Check if either the word is what you typed or the first alternative - return suggestions.size() > 0 && - (/*TextUtils.equals(suggestions.get(0), word) || */ - (suggestions.size() > 1 && TextUtils.equals(suggestions.getWord(1), word))); + public boolean isValidWord(CharSequence typed) { + return AutoCorrection.isValidWordForAutoCorrection(mSuggest.getUnigramDictionaries(), + typed, false); } - boolean isDefaultSuggestion(CharSequence typed, CharSequence expected) { - WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); - return isDefaultSuggestion(suggestions, expected); + // TODO: This may be slow, but is OK for test so far. + public SuggestedWords getSuggestions(CharSequence typed) { + return mSuggest.getSuggestions(null, createWordComposer(typed), null); } - boolean isDefaultCorrection(CharSequence typed, CharSequence expected) { + public CharSequence getFirstSuggestion(CharSequence typed) { WordComposer word = createWordComposer(typed); SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); - return isDefaultSuggestion(suggestions, expected) && mSuggest.hasAutoCorrection(); + // Note that suggestions.getWord(0) is the word user typed. + return suggestions.size() > 1 ? suggestions.getWord(1) : null; } - boolean isASuggestion(CharSequence typed, CharSequence expected) { + public CharSequence getAutoCorrection(CharSequence typed) { WordComposer word = createWordComposer(typed); SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + // Note that suggestions.getWord(0) is the word user typed. + return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) + ? suggestions.getWord(1) : null; + } + + public int getSuggestIndex(CharSequence typed, CharSequence expected) { + WordComposer word = createWordComposer(typed); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + // Note that suggestions.getWord(0) is the word user typed. for (int i = 1; i < suggestions.size(); i++) { - if (TextUtils.equals(suggestions.getWord(i), expected)) return true; + if (TextUtils.equals(suggestions.getWord(i), expected)) + return i; } - return false; + return -1; } private void getBigramSuggestions(CharSequence previous, CharSequence typed) { @@ -117,109 +133,30 @@ public class SuggestHelper { } } - boolean isDefaultNextSuggestion(CharSequence previous, CharSequence typed, - CharSequence expected) { + public CharSequence getBigramFirstSuggestion(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); - return isDefaultSuggestion(suggestions, expected); + return suggestions.size() > 1 ? suggestions.getWord(1) : null; } - boolean isDefaultNextCorrection(CharSequence previous, CharSequence typed, - CharSequence expected) { + public CharSequence getBigramAutoCorrection(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); - return isDefaultSuggestion(suggestions, expected) && mSuggest.hasAutoCorrection(); + return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) + ? suggestions.getWord(1) : null; } - boolean isASuggestion(CharSequence previous, CharSequence typed, + public int searchBigramSuggestion(CharSequence previous, CharSequence typed, CharSequence expected) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); for (int i = 1; i < suggestions.size(); i++) { - if (TextUtils.equals(suggestions.getWord(i), expected)) return true; + if (TextUtils.equals(suggestions.getWord(i), expected)) + return i; } - return false; + return -1; } - - boolean isValid(CharSequence typed) { - return mSuggest.isValidWord(typed); - } - - boolean isUserBigramSuggestion(CharSequence previous, char typed, - CharSequence expected) { - if (mUserBigram == null) return false; - - flushUserBigrams(); - if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) { - WordComposer firstChar = createWordComposer(Character.toString(typed)); - mSuggest.getSuggestions(null, firstChar, previous); - boolean reloading = mUserBigram.reloadDictionaryIfRequired(); - if (reloading) mUserBigram.waitForDictionaryLoading(); - mUserBigram.getBigrams(firstChar, previous, mSuggest, null); - } - - List suggestions = mSuggest.mBigramSuggestions; - for (int i = 0; i < suggestions.size(); i++) { - if (TextUtils.equals(suggestions.get(i), expected)) return true; - } - - return false; - } - - void addToUserBigram(String sentence) { - StringTokenizer st = new StringTokenizer(sentence); - String previous = null; - while (st.hasMoreTokens()) { - String current = st.nextToken(); - if (previous != null) { - addToUserBigram(new String[] {previous, current}); - } - previous = current; - } - } - - void addToUserBigram(String[] pair) { - if (mUserBigram != null && pair.length == 2) { - mUserBigram.addBigrams(pair[0], pair[1]); - } - } - - void flushUserBigrams() { - if (mUserBigram != null) { - mUserBigram.flushPendingWrites(); - mUserBigram.waitUntilUpdateDBDone(); - } - } - - final int[][] adjacents = { - {'a','s','w','q',-1}, - {'b','h','v','n','g','j',-1}, - {'c','v','f','x','g',}, - {'d','f','r','e','s','x',-1}, - {'e','w','r','s','d',-1}, - {'f','g','d','c','t','r',-1}, - {'g','h','f','y','t','v',-1}, - {'h','j','u','g','b','y',-1}, - {'i','o','u','k',-1}, - {'j','k','i','h','u','n',-1}, - {'k','l','o','j','i','m',-1}, - {'l','k','o','p',-1}, - {'m','k','n','l',-1}, - {'n','m','j','k','b',-1}, - {'o','p','i','l',-1}, - {'p','o',-1}, - {'q','w',-1}, - {'r','t','e','f',-1}, - {'s','d','e','w','a','z',-1}, - {'t','y','r',-1}, - {'u','y','i','h','j',-1}, - {'v','b','g','c','h',-1}, - {'w','e','q',-1}, - {'x','c','d','z','f',-1}, - {'y','u','t','h','g',-1}, - {'z','s','x','a','d',-1}, - }; } diff --git a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java b/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java index c5913ab4f..99bcc615e 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2010,2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -15,70 +15,77 @@ */ package com.android.inputmethod.latin; - -import android.test.AndroidTestCase; -import android.util.Log; import com.android.inputmethod.latin.tests.R; -import java.io.InputStreamReader; -import java.io.InputStream; + +import android.content.res.AssetFileDescriptor; +import android.text.TextUtils; +import android.util.Slog; + import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.Locale; import java.util.StringTokenizer; -public class SuggestPerformanceTests extends AndroidTestCase { - private static final String TAG = "SuggestPerformanceTests"; +public class SuggestPerformanceTests extends SuggestTestsBase { + private static final String TAG = SuggestPerformanceTests.class.getSimpleName(); private String mTestText; - private SuggestHelper sh; + private SuggestHelper mHelper; @Override - protected void setUp() { - // TODO Figure out a way to directly using the dictionary rather than copying it over - - // For testing with real dictionary, TEMPORARILY COPY main dictionary into test directory. - // DO NOT SUBMIT real dictionary under test directory. - //int resId = R.raw.main; - - int resId = R.raw.test; - - sh = new SuggestHelper(TAG, getTestContext(), resId); - loadString(); + protected void setUp() throws Exception { + super.setUp(); + final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); + mHelper = new SuggestHelper( + getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), + createKeyboardId(Locale.US)); + loadString(R.raw.testtext); } - private void loadString() { + private void loadString(int testFileId) { + final String testFile = getTestContext().getResources().getResourceName(testFileId); + BufferedReader reader = null; try { - InputStream is = getTestContext().getResources().openRawResource(R.raw.testtext); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - sb.append(line + " "); - line = reader.readLine(); + reader = new BufferedReader( + new InputStreamReader(openTestRawResource(testFileId))); + final StringBuilder sb = new StringBuilder(); + String line; + Slog.i(TAG, "Reading test file " + testFile); + while ((line = reader.readLine()) != null) { + sb.append(line); + sb.append(" "); } mTestText = sb.toString(); } catch (Exception e) { + Slog.e(TAG, "Can not read " + testFile); e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception e) { + Slog.e(TAG, "Closing " + testFile + " failed"); + } + } } } /************************** Helper functions ************************/ - private int lookForSuggestion(String prevWord, String currentWord) { + private int lookForBigramSuggestion(String prevWord, String currentWord) { for (int i = 1; i < currentWord.length(); i++) { - if (i == 1) { - if (sh.isDefaultNextSuggestion(prevWord, currentWord.substring(0, i), - currentWord)) { - return i; - } - } else { - if (sh.isDefaultNextCorrection(prevWord, currentWord.substring(0, i), - currentWord)) { - return i; - } - } + final CharSequence prefix = currentWord.substring(0, i); + final CharSequence word = (i == 1) + ? mHelper.getBigramFirstSuggestion(prevWord, prefix) + : mHelper.getBigramAutoCorrection(prevWord, prefix); + if (TextUtils.equals(word, currentWord)) + return i; } return currentWord.length(); } private double runText(boolean withBigrams) { + mHelper.setCorrectionMode( + withBigrams ? Suggest.CORRECTION_FULL_BIGRAM : Suggest.CORRECTION_FULL); StringTokenizer st = new StringTokenizer(mTestText); String prevWord = null; int typeCount = 0; @@ -92,9 +99,9 @@ public class SuggestPerformanceTests extends AndroidTestCase { endCheck = true; } if (withBigrams && prevWord != null) { - typeCount += lookForSuggestion(prevWord, currentWord); + typeCount += lookForBigramSuggestion(prevWord, currentWord); } else { - typeCount += lookForSuggestion(null, currentWord); + typeCount += lookForBigramSuggestion(null, currentWord); } characterCount += currentWord.length(); if (!endCheck) prevWord = currentWord; @@ -103,14 +110,14 @@ public class SuggestPerformanceTests extends AndroidTestCase { double result = (double) (characterCount - typeCount) / characterCount * 100; if (withBigrams) { - Log.i(TAG, "with bigrams -> " + result + " % saved!"); + Slog.i(TAG, "with bigrams -> " + result + " % saved!"); } else { - Log.i(TAG, "without bigrams -> " + result + " % saved!"); + Slog.i(TAG, "without bigrams -> " + result + " % saved!"); } - Log.i(TAG, "\ttotal number of words: " + wordCount); - Log.i(TAG, "\ttotal number of characters: " + mTestText.length()); - Log.i(TAG, "\ttotal number of characters without space: " + characterCount); - Log.i(TAG, "\ttotal number of characters typed: " + typeCount); + Slog.i(TAG, "\ttotal number of words: " + wordCount); + Slog.i(TAG, "\ttotal number of characters: " + mTestText.length()); + Slog.i(TAG, "\ttotal number of characters without space: " + characterCount); + Slog.i(TAG, "\ttotal number of characters typed: " + typeCount); return result; } diff --git a/tests/src/com/android/inputmethod/latin/SuggestTests.java b/tests/src/com/android/inputmethod/latin/SuggestTests.java index c890394d0..6e9a12797 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2010,2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -16,18 +16,23 @@ package com.android.inputmethod.latin; -import android.test.AndroidTestCase; import com.android.inputmethod.latin.tests.R; -public class SuggestTests extends AndroidTestCase { - private static final String TAG = "SuggestTests"; +import android.content.res.AssetFileDescriptor; - private SuggestHelper sh; +import java.util.Locale; + +public class SuggestTests extends SuggestTestsBase { + private SuggestHelper mHelper; @Override - protected void setUp() { - int resId = R.raw.test; - sh = new SuggestHelper(TAG, getTestContext(), resId); + protected void setUp() throws Exception { + super.setUp(); + final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); + mHelper = new SuggestHelper( + getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), + createKeyboardId(Locale.US)); + mHelper.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM); } /************************** Tests ************************/ @@ -36,105 +41,105 @@ public class SuggestTests extends AndroidTestCase { * Tests for simple completions of one character. */ public void testCompletion1char() { - assertTrue(sh.isDefaultSuggestion("peopl", "people")); - assertTrue(sh.isDefaultSuggestion("abou", "about")); - assertTrue(sh.isDefaultSuggestion("thei", "their")); + suggested("people", mHelper.getFirstSuggestion("peopl")); + suggested("about", mHelper.getFirstSuggestion("abou")); + suggested("their", mHelper.getFirstSuggestion("thei")); } /** * Tests for simple completions of two characters. */ public void testCompletion2char() { - assertTrue(sh.isDefaultSuggestion("peop", "people")); - assertTrue(sh.isDefaultSuggestion("calli", "calling")); - assertTrue(sh.isDefaultSuggestion("busine", "business")); + suggested("people", mHelper.getFirstSuggestion("peop")); + suggested("calling", mHelper.getFirstSuggestion("calli")); + suggested("business", mHelper.getFirstSuggestion("busine")); } /** * Tests for proximity errors. */ public void testProximityPositive() { - assertTrue(sh.isDefaultSuggestion("peiple", "people")); - assertTrue(sh.isDefaultSuggestion("peoole", "people")); - assertTrue(sh.isDefaultSuggestion("pwpple", "people")); + suggested("typed peiple", "people", mHelper.getFirstSuggestion("peiple")); + suggested("typed peoole", "people", mHelper.getFirstSuggestion("peoole")); + suggested("typed pwpple", "people", mHelper.getFirstSuggestion("pwpple")); } /** - * Tests for proximity errors - negative, when the error key is not near. + * Tests for proximity errors - negative, when the error key is not close. */ public void testProximityNegative() { - assertFalse(sh.isDefaultSuggestion("arout", "about")); - assertFalse(sh.isDefaultSuggestion("ire", "are")); + notSuggested("about", mHelper.getFirstSuggestion("arout")); + notSuggested("are", mHelper.getFirstSuggestion("ire")); } /** * Tests for checking if apostrophes are added automatically. */ public void testApostropheInsertion() { - assertTrue(sh.isDefaultSuggestion("im", "I'm")); - assertTrue(sh.isDefaultSuggestion("dont", "don't")); + suggested("I'm", mHelper.getFirstSuggestion("im")); + suggested("don't", mHelper.getFirstSuggestion("dont")); } /** * Test to make sure apostrophed word is not suggested for an apostrophed word. */ public void testApostrophe() { - assertFalse(sh.isDefaultSuggestion("don't", "don't")); + notSuggested("don't", mHelper.getFirstSuggestion("don't")); } /** * Tests for suggestion of capitalized version of a word. */ public void testCapitalization() { - assertTrue(sh.isDefaultSuggestion("i'm", "I'm")); - assertTrue(sh.isDefaultSuggestion("sunday", "Sunday")); - assertTrue(sh.isDefaultSuggestion("sundat", "Sunday")); + suggested("I'm", mHelper.getFirstSuggestion("i'm")); + suggested("Sunday", mHelper.getFirstSuggestion("sunday")); + suggested("Sunday", mHelper.getFirstSuggestion("sundat")); } /** * Tests to see if more than one completion is provided for certain prefixes. */ public void testMultipleCompletions() { - assertTrue(sh.isASuggestion("com", "come")); - assertTrue(sh.isASuggestion("com", "company")); - assertTrue(sh.isASuggestion("th", "the")); - assertTrue(sh.isASuggestion("th", "that")); - assertTrue(sh.isASuggestion("th", "this")); - assertTrue(sh.isASuggestion("th", "they")); + isInSuggestions("com: come", mHelper.getSuggestIndex("com", "come")); + isInSuggestions("com: company", mHelper.getSuggestIndex("com", "company")); + isInSuggestions("th: the", mHelper.getSuggestIndex("th", "the")); + isInSuggestions("th: that", mHelper.getSuggestIndex("th", "that")); + isInSuggestions("th: this", mHelper.getSuggestIndex("th", "this")); + isInSuggestions("th: they", mHelper.getSuggestIndex("th", "they")); } /** * Does the suggestion engine recognize zero frequency words as valid words. */ public void testZeroFrequencyAccepted() { - assertTrue(sh.isValid("yikes")); - assertFalse(sh.isValid("yike")); + assertTrue("valid word yikes", mHelper.isValidWord("yikes")); + assertFalse("non valid word yike", mHelper.isValidWord("yike")); } /** * Tests to make sure that zero frequency words are not suggested as completions. */ public void testZeroFrequencySuggestionsNegative() { - assertFalse(sh.isASuggestion("yike", "yikes")); - assertFalse(sh.isASuggestion("what", "whatcha")); + assertTrue(mHelper.getSuggestIndex("yike", "yikes") < 0); + assertTrue(mHelper.getSuggestIndex("what", "whatcha") < 0); } /** - * Tests to ensure that words with large edit distances are not suggested, in some cases - * and not considered corrections, in some cases. + * Tests to ensure that words with large edit distances are not suggested, in some cases. + * Also such word is not considered auto correction, in some cases. */ public void testTooLargeEditDistance() { - assertFalse(sh.isASuggestion("sniyr", "about")); + assertTrue(mHelper.getSuggestIndex("sniyr", "about") < 0); // TODO: The following test fails. - // assertFalse(sh.isDefaultCorrection("rjw", "the")); + // notSuggested("the", mHelper.getAutoCorrection("rjw")); } /** - * Make sure sh.isValid is case-sensitive. + * Make sure mHelper.isValidWord is case-sensitive. */ public void testValidityCaseSensitivity() { - assertTrue(sh.isValid("Sunday")); - assertFalse(sh.isValid("sunday")); + assertTrue("valid word Sunday", mHelper.isValidWord("Sunday")); + assertFalse("non valid word sunday", mHelper.isValidWord("sunday")); } /** @@ -142,11 +147,11 @@ public class SuggestTests extends AndroidTestCase { */ public void testAccents() { // nio - assertTrue(sh.isDefaultCorrection("nino", "ni\u00F1o")); + suggested("ni\u00F1o", mHelper.getAutoCorrection("nino")); // nio - assertTrue(sh.isDefaultCorrection("nimo", "ni\u00F1o")); + suggested("ni\u00F1o", mHelper.getAutoCorrection("nimo")); // Mara - assertTrue(sh.isDefaultCorrection("maria", "Mar\u00EDa")); + suggested("Mar\u00EDa", mHelper.getAutoCorrection("maria")); } /** @@ -154,21 +159,29 @@ public class SuggestTests extends AndroidTestCase { * and don't show any when there aren't any */ public void testBigramsAtFirstChar() { - assertTrue(sh.isDefaultNextSuggestion("about", "p", "part")); - assertTrue(sh.isDefaultNextSuggestion("I'm", "a", "about")); - assertTrue(sh.isDefaultNextSuggestion("about", "b", "business")); - assertTrue(sh.isASuggestion("about", "b", "being")); - assertFalse(sh.isDefaultNextSuggestion("about", "p", "business")); + suggested("bigram: about p[art]", + "part", mHelper.getBigramFirstSuggestion("about", "p")); + suggested("bigram: I'm a[bout]", + "about", mHelper.getBigramFirstSuggestion("I'm", "a")); + suggested("bigram: about b[usiness]", + "business", mHelper.getBigramFirstSuggestion("about", "b")); + isInSuggestions("bigram: about b[eing]", + mHelper.searchBigramSuggestion("about", "b", "being")); + notSuggested("bigram: about p", + "business", mHelper.getBigramFirstSuggestion("about", "p")); } /** * Make sure bigrams score affects the original score */ public void testBigramsScoreEffect() { - assertTrue(sh.isDefaultCorrection("pa", "page")); - assertTrue(sh.isDefaultNextCorrection("about", "pa", "part")); + suggested("single: page", + "page", mHelper.getAutoCorrection("pa")); + suggested("bigram: about pa[rt]", + "part", mHelper.getBigramAutoCorrection("about", "pa")); // TODO: The following test fails. - // assertTrue(sh.isDefaultCorrection("sa", "said")); - assertTrue(sh.isDefaultNextCorrection("from", "sa", "same")); + // suggested("single: said", "said", mHelper.getAutoCorrection("sa")); + suggested("bigram: from sa[me]", + "same", mHelper.getBigramAutoCorrection("from", "sa")); } } diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java new file mode 100644 index 000000000..64f26743e --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardView; + +import android.content.res.AssetFileDescriptor; +import android.content.res.Configuration; +import android.test.AndroidTestCase; +import android.text.TextUtils; +import android.view.inputmethod.EditorInfo; + +import java.io.File; +import java.io.InputStream; +import java.util.Locale; + +public class SuggestTestsBase extends AndroidTestCase { + protected File mTestPackageFile; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestPackageFile = new File(getTestContext().getApplicationInfo().sourceDir); + } + + protected static KeyboardId createKeyboardId(Locale locale) { + return new KeyboardId(locale.toString() + " keyboard", + com.android.inputmethod.latin.R.xml.kbd_qwerty, KeyboardView.COLOR_SCHEME_WHITE, + locale, Configuration.ORIENTATION_LANDSCAPE, KeyboardId.MODE_TEXT, + new EditorInfo(), false, false, false, false); + } + + protected InputStream openTestRawResource(int resIdInTest) { + return getTestContext().getResources().openRawResource(resIdInTest); + } + + protected AssetFileDescriptor openTestRawResourceFd(int resIdInTest) { + return getTestContext().getResources().openRawResourceFd(resIdInTest); + } + + private static String format(String message, Object expected, Object actual) { + return message + " expected:<" + expected + "> but was:<" + actual + ">"; + } + + protected static void suggested(CharSequence expected, CharSequence actual) { + if (!TextUtils.equals(expected, actual)) + fail(format("assertEquals", expected, actual)); + } + + protected static void suggested(String message, CharSequence expected, CharSequence actual) { + if (!TextUtils.equals(expected, actual)) + fail(format(message, expected, actual)); + } + + protected static void notSuggested(CharSequence expected, CharSequence actual) { + if (TextUtils.equals(expected, actual)) + fail(format("assertNotEquals", expected, actual)); + } + + protected static void notSuggested(String message, CharSequence expected, CharSequence actual) { + if (TextUtils.equals(expected, actual)) + fail(format(message, expected, actual)); + } + + protected static void isInSuggestions(String message, int position) { + assertTrue(message, position >= 0); + } + + protected static void isNotInSuggestions(String message, int position) { + assertTrue(message, position < 0); + } +} diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java new file mode 100644 index 000000000..46e5a2454 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import com.android.inputmethod.keyboard.KeyboardId; + +import android.content.Context; +import android.text.TextUtils; + +import java.io.File; +import java.util.Locale; +import java.util.StringTokenizer; + +public class UserBigramSuggestHelper extends SuggestHelper { + private final Context mContext; + private UserBigramDictionary mUserBigram; + + public UserBigramSuggestHelper(Context context, File dictionaryPath, long startOffset, + long length, int userBigramMax, int userBigramDelete, KeyboardId keyboardId) { + super(context, dictionaryPath, startOffset, length, keyboardId); + mContext = context; + mUserBigram = new UserBigramDictionary(context, null, Locale.US.toString(), + Suggest.DIC_USER); + mUserBigram.setDatabaseMax(userBigramMax); + mUserBigram.setDatabaseDelete(userBigramDelete); + mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM); + mSuggest.setUserBigramDictionary(mUserBigram); + } + + public void changeUserBigramLocale(Locale locale) { + if (mUserBigram != null) { + flushUserBigrams(); + mUserBigram.close(); + mUserBigram = new UserBigramDictionary(mContext, null, locale.toString(), + Suggest.DIC_USER); + mSuggest.setUserBigramDictionary(mUserBigram); + } + } + + public int searchUserBigramSuggestion(CharSequence previous, char typed, + CharSequence expected) { + if (mUserBigram == null) return -1; + + flushUserBigrams(); + if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) { + WordComposer firstChar = createWordComposer(Character.toString(typed)); + mSuggest.getSuggestions(null, firstChar, previous); + boolean reloading = mUserBigram.reloadDictionaryIfRequired(); + if (reloading) mUserBigram.waitForDictionaryLoading(); + mUserBigram.getBigrams(firstChar, previous, mSuggest); + } + + for (int i = 0; i < mSuggest.mBigramSuggestions.size(); i++) { + final CharSequence word = mSuggest.mBigramSuggestions.get(i); + if (TextUtils.equals(word, expected)) + return i; + } + + return -1; + } + + public void addToUserBigram(String sentence) { + StringTokenizer st = new StringTokenizer(sentence); + String previous = null; + while (st.hasMoreTokens()) { + String current = st.nextToken(); + if (previous != null) { + addToUserBigram(new String[] {previous, current}); + } + previous = current; + } + } + + public void addToUserBigram(String[] pair) { + if (mUserBigram != null && pair.length == 2) { + mUserBigram.addBigrams(pair[0], pair[1]); + } + } + + public void flushUserBigrams() { + if (mUserBigram != null) { + mUserBigram.flushPendingWrites(); + mUserBigram.waitUntilUpdateDBDone(); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java new file mode 100644 index 000000000..9bd85385e --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010,2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; +import com.android.inputmethod.latin.tests.R; + +import android.content.res.AssetFileDescriptor; + +import java.util.Locale; + +public class UserBigramSuggestTests extends SuggestTestsBase { + private static final int SUGGESTION_STARTS = 6; + private static final int MAX_DATA = 20; + private static final int DELETE_DATA = 10; + + private UserBigramSuggestHelper mHelper; + + @Override + protected void setUp() throws Exception { + super.setUp(); + final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); + mHelper = new UserBigramSuggestHelper( + getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), + MAX_DATA, DELETE_DATA, createKeyboardId(Locale.US)); + } + + /************************** Tests ************************/ + + /** + * Test suggestion started at right time + */ + public void testUserBigram() { + for (int i = 0; i < SUGGESTION_STARTS; i++) mHelper.addToUserBigram(pair1); + for (int i = 0; i < (SUGGESTION_STARTS - 1); i++) mHelper.addToUserBigram(pair2); + + isInSuggestions("bigram", mHelper.searchUserBigramSuggestion("user", 'b', "bigram")); + isNotInSuggestions("platform", + mHelper.searchUserBigramSuggestion("android", 'p', "platform")); + } + + /** + * Test loading correct (locale) bigrams + */ + public void testOpenAndClose() { + for (int i = 0; i < SUGGESTION_STARTS; i++) mHelper.addToUserBigram(pair1); + isInSuggestions("bigram in default locale", + mHelper.searchUserBigramSuggestion("user", 'b', "bigram")); + + // change to fr_FR + mHelper.changeUserBigramLocale(Locale.FRANCE); + for (int i = 0; i < SUGGESTION_STARTS; i++) mHelper.addToUserBigram(pair3); + isInSuggestions("france in fr_FR", + mHelper.searchUserBigramSuggestion("locale", 'f', "france")); + isNotInSuggestions("bigram in fr_FR", + mHelper.searchUserBigramSuggestion("user", 'b', "bigram")); + + // change back to en_US + mHelper.changeUserBigramLocale(Locale.US); + isNotInSuggestions("france in en_US", + mHelper.searchUserBigramSuggestion("locale", 'f', "france")); + isInSuggestions("bigram in en_US", + mHelper.searchUserBigramSuggestion("user", 'b', "bigram")); + } + + /** + * Test data gets pruned when it is over maximum + */ + public void testPruningData() { + for (int i = 0; i < SUGGESTION_STARTS; i++) mHelper.addToUserBigram(sentence0); + mHelper.flushUserBigrams(); + isInSuggestions("world after several sentence 0", + mHelper.searchUserBigramSuggestion("Hello", 'w', "world")); + + mHelper.addToUserBigram(sentence1); + mHelper.addToUserBigram(sentence2); + isInSuggestions("world after sentence 1 and 2", + mHelper.searchUserBigramSuggestion("Hello", 'w', "world")); + + // pruning should happen + mHelper.addToUserBigram(sentence3); + mHelper.addToUserBigram(sentence4); + + // trying to reopen database to check pruning happened in database + mHelper.changeUserBigramLocale(Locale.US); + isNotInSuggestions("world after sentence 3 and 4", + mHelper.searchUserBigramSuggestion("Hello", 'w', "world")); + } + + private static final String[] pair1 = {"user", "bigram"}; + private static final String[] pair2 = {"android","platform"}; + private static final String[] pair3 = {"locale", "france"}; + private static final String sentence0 = "Hello world"; + private static final String sentence1 = "This is a test for user input based bigram"; + private static final String sentence2 = "It learns phrases that contain both dictionary and " + + "nondictionary words"; + private static final String sentence3 = "This should give better suggestions than the previous " + + "version"; + private static final String sentence4 = "Android stock keyboard is improving"; +} diff --git a/tests/src/com/android/inputmethod/latin/UserBigramTests.java b/tests/src/com/android/inputmethod/latin/UserBigramTests.java deleted file mode 100644 index af527b02d..000000000 --- a/tests/src/com/android/inputmethod/latin/UserBigramTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.latin; - -import android.test.AndroidTestCase; -import com.android.inputmethod.latin.tests.R; -import java.util.Locale; - -public class UserBigramTests extends AndroidTestCase { - private static final String TAG = "UserBigramTests"; - - private static final int SUGGESTION_STARTS = 6; - private static final int MAX_DATA = 20; - private static final int DELETE_DATA = 10; - - private SuggestHelper sh; - - @Override - protected void setUp() { - int resId = R.raw.test; - sh = new SuggestHelper(TAG, getTestContext(), resId, MAX_DATA, DELETE_DATA); - } - - /************************** Tests ************************/ - - /** - * Test suggestion started at right time - */ - public void testUserBigram() { - for (int i = 0; i < SUGGESTION_STARTS; i++) sh.addToUserBigram(pair1); - for (int i = 0; i < (SUGGESTION_STARTS - 1); i++) sh.addToUserBigram(pair2); - - assertTrue(sh.isUserBigramSuggestion("user", 'b', "bigram")); - assertFalse(sh.isUserBigramSuggestion("android", 'p', "platform")); - } - - /** - * Test loading correct (locale) bigrams - */ - public void testOpenAndClose() { - for (int i = 0; i < SUGGESTION_STARTS; i++) sh.addToUserBigram(pair1); - assertTrue(sh.isUserBigramSuggestion("user", 'b', "bigram")); - - // change to fr_FR - sh.changeUserBigramLocale(getTestContext(), Locale.FRANCE); - for (int i = 0; i < SUGGESTION_STARTS; i++) sh.addToUserBigram(pair3); - assertTrue(sh.isUserBigramSuggestion("locale", 'f', "france")); - assertFalse(sh.isUserBigramSuggestion("user", 'b', "bigram")); - - // change back to en_US - sh.changeUserBigramLocale(getTestContext(), Locale.US); - assertFalse(sh.isUserBigramSuggestion("locale", 'f', "france")); - assertTrue(sh.isUserBigramSuggestion("user", 'b', "bigram")); - } - - /** - * Test data gets pruned when it is over maximum - */ - public void testPruningData() { - for (int i = 0; i < SUGGESTION_STARTS; i++) sh.addToUserBigram(sentence0); - sh.flushUserBigrams(); - assertTrue(sh.isUserBigramSuggestion("Hello", 'w', "world")); - - sh.addToUserBigram(sentence1); - sh.addToUserBigram(sentence2); - assertTrue(sh.isUserBigramSuggestion("Hello", 'w', "world")); - - // pruning should happen - sh.addToUserBigram(sentence3); - sh.addToUserBigram(sentence4); - - // trying to reopen database to check pruning happened in database - sh.changeUserBigramLocale(getTestContext(), Locale.US); - assertFalse(sh.isUserBigramSuggestion("Hello", 'w', "world")); - } - - final String[] pair1 = new String[] {"user", "bigram"}; - final String[] pair2 = new String[] {"android","platform"}; - final String[] pair3 = new String[] {"locale", "france"}; - final String sentence0 = "Hello world"; - final String sentence1 = "This is a test for user input based bigram"; - final String sentence2 = "It learns phrases that contain both dictionary and nondictionary " - + "words"; - final String sentence3 = "This should give better suggestions than the previous version"; - final String sentence4 = "Android stock keyboard is improving"; -}