diff options
106 files changed, 1116 insertions, 792 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index b54406f4b..031d62e0c 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -110,6 +110,12 @@ </intent-filter> </receiver> + <receiver android:name=".personalization.DictionaryDecayBroadcastReciever"> + <intent-filter> + <action android:name="com.android.inputmethod.latin.personalization.DICT_DECAY" /> + </intent-filter> + </receiver> + <receiver android:name=".DictionaryPackInstallBroadcastReceiver"> <intent-filter> <action android:name="com.android.inputmethod.dictionarypack.aosp.UNKNOWN_CLIENT" /> diff --git a/java/res/color/key_text_color_ics.xml b/java/res/color/key_text_color_ics.xml new file mode 100644 index 000000000..c6f111ad2 --- /dev/null +++ b/java/res/color/key_text_color_ics.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Functional keys. --> + <item android:state_single="true" android:state_pressed="true" + android:color="@color/key_text_color_functional_ics" /> + <item android:state_single="true" + android:color="@color/key_text_color_functional_ics" /> + + <!-- Action keys. --> + <item android:state_active="true" android:state_pressed="true" + android:color="@color/key_text_color_normal_ics" /> + <item android:state_active="true" + android:color="@color/key_text_color_normal_ics" /> + + <!-- Toggle keys. Use checkable/checked state. --> + <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true" + android:color="@color/key_text_color_normal_ics" /> + <item android:state_checkable="true" android:state_pressed="true" + android:color="@color/key_text_color_normal_ics" /> + <item android:state_checkable="true" android:state_checked="true" + android:color="@color/key_text_color_normal_ics" /> + <item android:state_checkable="true" + android:color="@color/key_text_color_normal_ics" /> + + <!-- Empty background keys. --> + <item android:state_empty="true" + android:color="@color/key_text_color_normal_ics" /> + + <!-- Normal keys. --> + <item android:state_pressed="true" + android:color="@color/key_text_color_normal_ics" /> + <item android:color="@color/key_text_color_normal_ics" /> +</selector> diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index a3c95b290..f187a73a5 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Koppel \'n kopstuk om te hoor hoe wagwoordsleutels hardop gesĂȘ word."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige teks is %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen teks ingevoer nie"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> het outokorreksie"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Sleutelkode %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om te deaktiveer)"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 89e37b274..6504e64c7 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ášááááá áááᜠáźá á áá áČááá© ááá”áá” ášááź ááłáá« á°á«::"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ášá áá á
áá %s áá"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ááá á
áá á áááŁá"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> <xliff:g id="ORIGINAL">%2$s</xliff:g>á áá° <xliff:g id="CORRECTED">%3$s</xliff:g> á«áááá"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> á«á”-á áá á áá"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ášááá áźá”%d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ááá"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"á
á«á á áá·á (ááá°ááá áá«)"</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 9c3ff51c8..46bff1294 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ÙÙ
ÙÙÙ ŰȘÙŰ”ÙÙ ŰłÙ
ۧŰčŰ© ۱ۣ۳ ÙŰłÙ
ۧŰč Ù
ÙۧŰȘÙŰ ÙÙÙ
Ű© ۧÙÙ
۱Ù۱ Ù
ÙŰ·ÙÙŰ© ۚ۔ÙŰȘ ŰčۧÙÙ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ۧÙÙŰ” ۧÙŰۧÙÙ ÙÙ %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ÙÙ
ÙŰȘÙ
Ű„ŰŻŰźŰ§Ù ÙŰ”"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ÙŰȘŰ”ŰÙŰ <xliff:g id="ORIGINAL">%2$s</xliff:g> Ű„ÙÙ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ÙÙŰȘŰ”ŰÙŰ Ű§ÙŰȘÙÙۧۊÙ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"۱Ù
ŰČ Ű§ÙÙ
ÙŰȘŰ§Ű %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ۧÙŰčۧÙÙ"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ÙŰčÙ
Ù (ۧÙÙ۱ ÙÙŰȘŰčŰ·ÙÙ)"</string> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index c21b53490..0e6c8dd45 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ĐĐșĐ»ŃŃĐ”ŃĐ” ŃĐ»ŃŃалĐșĐž, за Ўа ŃŃĐ”ŃĐ” ĐșлаĐČĐžŃĐžŃĐ” за паŃĐŸĐ»Đ°Ńа ĐœĐ° ĐČĐžŃĐŸĐș глаŃ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"йДĐșŃŃĐžŃŃ ŃĐ”ĐșŃŃ Đ” %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ĐŃĐŒĐ° ĐČŃĐČĐ”ĐŽĐ”Đœ ŃĐ”ĐșŃŃ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"â<xliff:g id="KEY">%1$s</xliff:g>â ĐșĐŸŃОгОŃа â<xliff:g id="ORIGINAL">%2$s</xliff:g>â ĐœĐ° â<xliff:g id="CORRECTED">%3$s</xliff:g>â"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"â<xliff:g id="KEY">%1$s</xliff:g>â Đ” Ń Đ°ĐČŃĐŸĐŒĐ°ŃĐžŃĐœĐŸ ĐșĐŸŃОгОŃĐ°ĐœĐ”"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ĐĐŸĐŽ ĐœĐ° ĐșлаĐČĐžŃa %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"âShiftâ Đ” ĐČĐșĐ»ŃŃĐ”Đœ (ĐŽĐŸĐșĐŸŃĐœĐ”ŃĐ” за ЎДаĐșŃĐžĐČĐžŃĐ°ĐœĐ”)"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index d791452d4..043fbd980 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Connecta un auricular per escoltar les claus de la contrasenya en veu alta."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"El text actual Ă©s %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"No s\'ha introduĂŻt cap text"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL">%2$s</xliff:g> per <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tĂ© correcciĂł automĂ tica"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Clau de codi %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maj activat (pica per desactivar)"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 2cd159d0f..2d5c386a5 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Chcete-li slyĆĄet, kterĂ© klĂĄvesy jste pĆi zadĂĄvĂĄnĂ hesla stiskli, pĆipojte sluchĂĄtka."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"AktuĂĄlnĂ text je %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"NenĂ zadĂĄn ĆŸĂĄdnĂœ text"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"KlĂĄvesou <xliff:g id="KEY">%1$s</xliff:g> opravĂte <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"KlĂĄvese <xliff:g id="KEY">%1$s</xliff:g> je pĆiĆazena automatickĂĄ oprava"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"KĂłd klĂĄvesy %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"KlĂĄvesa Shift je zapnutĂĄ (vypnete ji klepnutĂm)."</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 6c9d9d786..0a53691b2 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Tilslut et headset for at hĂžre indtastningen blive lĂŠst hĂžjt ved angivelse af adgangskode."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"NuvĂŠrende tekst er %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Der er ingen indtastet tekst"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> retter <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> udfĂžrer automatisk rettelse"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastekode %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift-tast"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift er slĂ„et til (tryk for at deaktivere)"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 1a4e38542..1485cc60d 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"SchlieĂen Sie ein Headset an, um das Passwort gesprochen zu hören."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktueller Text lautet %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Kein Text eingegeben"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird <xliff:g id="ORIGINAL">%2$s</xliff:g> in <xliff:g id="CORRECTED">%3$s</xliff:g> korrigiert."</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird automatisch korrigiert."</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastencode %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Umschalttaste"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Umschalttaste aktiviert (zum Deaktivieren berĂŒhren)"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index c2ee2d39b..8b6d0d135 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ÎŁÏ
ΜΎÎÏÏΔ ÎΜα ÏÎ”Ï Î±ÎșÎżÏ
ÏÏÎčÎșÏΜ ÎłÎčα Μα αÎșÎżÏÏΔÏΔ Ïα ÏλΟÎșÏÏα ÏÎżÏ
ÎșÏÎŽÎčÎșÎżÏ ÏÏÏÏÎČαÏÎ·Ï ÎœÎ± ΔÎșÏÏÎœÎżÏΜÏαÎč ÎŽÏ
ΜαÏÎŹ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"΀ο ÏÏÎÏÎżÎœ ÎșÎ”ÎŻÎŒÎ”ÎœÎż Î”ÎŻÎœÎ±Îč %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ÎΔΜ Ï
ÏÎŹÏÏΔÎč ÎșÎ”ÎŻÎŒÎ”ÎœÎż"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"΀ο ÏλΟÎșÏÏÎż <xliff:g id="KEY">%1$s</xliff:g> ÎŽÎčÎżÏΞÏΜΔÎč ÏÎż ÏÏÎżÎčÏΔίο <xliff:g id="ORIGINAL">%2$s</xliff:g> ÏΔ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"΀ο ÏλΟÎșÏÏÎż <xliff:g id="KEY">%1$s</xliff:g> ÎŽÎčαΞÎÏΔÎč αÏ
ÏÏΌαÏη ÎŽÎčÏÏΞÏÏη"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ÎÏÎŽÎčÎșÏÏ ÏλΟÎșÏÏÎżÏ
%d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"΀ο Shift Î”ÎŻÎœÎ±Îč ΔΜΔÏγοÏÎżÎčηΌÎÎœÎż (ÏαÏÎźÏÏΔ ÎłÎčα αÏΔΜΔÏγοÏοίηÏη)"</string> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 0586bcca4..1891a3fdb 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index 0586bcca4..1891a3fdb 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 42c3932f6..331eb3878 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Enchufa tus auriculares para escuchar en voz alta quĂ© teclas presionas al ingresar una contraseña."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ingresĂł texto."</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> por <xliff:g id="CORRECTED">%3$s</xliff:g>."</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automĂĄticamente."</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Clave de cĂłdigo %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"MayĂșs"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Se activĂł el modo MayĂșscula (toca para desactivarlo)."</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 4d72799f4..75069703b 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Conecta un auricular para escuchar las contraseñas en voz alta."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s."</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ha introducido texto."</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> a <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automĂĄticamente"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo del teclado: %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"MayĂșs"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"MayĂșsculas activadas (tocar para inhabilitar)"</string> diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml index 5706fa105..c7e6fe90c 100644 --- a/java/res/values-et-rEE/strings.xml +++ b/java/res/values-et-rEE/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Ăhendage peakomplekt, et kuulata paroole."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Praegune tekst on %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Teksti ei ole sisestatud"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> parandab valiku <xliff:g id="ORIGINAL">%2$s</xliff:g> valikuks <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klahv <xliff:g id="KEY">%1$s</xliff:g> rakendab automaatse paranduse"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Klahvi kood: %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"TĂ”stuklahv"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"TĂ”stuklahv sees (puudutage keelamiseks)"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index c03e440e4..febe01bf1 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -86,10 +86,8 @@ <!-- no translation found for spoken_current_text_is (2485723011272583845) --> <skip /> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ù
ŰȘÙÛ Ùۧ۱ۯ ÙŰŽŰŻÙ Ű§ŰłŰȘ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>Ű â<xliff:g id="ORIGINAL">%2$s</xliff:g> ۱ۧ ŰšÙ <xliff:g id="CORRECTED">%3$s</xliff:g> ŰȘŰ”ŰÛŰ Ù
ÛâÚ©ÙŰŻ"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ŰŻŰ§Ű±Ű§Û ŰȘŰ”ŰÛŰ ŰźÙۯکۧ۱ ۧ۳ŰȘ"</string> <!-- String.format failed for translation --> <!-- no translation found for spoken_description_unknown (3197434010402179157) --> <skip /> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 744e60420..8f2caabbf 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"LiitĂ€ kuulokkeet, niin kuulet mitĂ€ nĂ€ppĂ€imiĂ€ painat kirjoittaessasi salasanaa."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Nykyinen teksti on %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ei kirjoitettua tekstiĂ€"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korjaa kohteen <xliff:g id="ORIGINAL">%2$s</xliff:g> kohteeksi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> suorittaa automaattisen korjauksen"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"NĂ€ppĂ€imen koodi %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Vaihto"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Vaihto pÀÀllĂ€ (poista kĂ€ytöstĂ€ napauttamalla)"</string> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 52a9640b9..fba029887 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des Ă©couteurs pour entendre l\'Ă©noncĂ© Ă haute voix des touches lors de la saisie du mot de passe."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> en <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispose de la fonctionnalitĂ© de correction automatique"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activĂ©e (appuyer pour dĂ©sactiver)"</string> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 5fedd4ebd..9d6c8f489 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des Ă©couteurs pour entendre l\'Ă©noncĂ© Ă haute voix des touches lors de la saisie du mot de passe."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet de remplacer \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" par \"<xliff:g id="CORRECTED">%3$s</xliff:g>\"."</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet d\'activer la correction automatique."</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activĂ©e (appuyer pour dĂ©sactiver)"</string> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 3b3228a9f..0e9729629 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"à€à€Œà„à€° à€žà„ à€Źà„à€Čà„ à€à€ à€Șà€Ÿà€žà€”à€°à„à€Ą à€à„à€à€à€żà€Żà€Ÿà€ à€žà„à€šà€šà„ à€à„ à€Čà€żà€ à€čà„à€Ąà€žà„à€ à€Șà„âà€Čà€ à€à€š à€à€°à„à€."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"à€”à€°à„à€€à€źà€Ÿà€š à€Șà€Ÿà€ %s à€čà„"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"à€à„à€ à€Șà€Ÿà€ à€Šà€°à„à€ à€šà€čà„à€ à€à€żà€Żà€Ÿ à€à€Żà€Ÿ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>, <xliff:g id="ORIGINAL">%2$s</xliff:g> à€à„ à€žà„à€§à€Ÿà€°à€à€° <xliff:g id="CORRECTED">%3$s</xliff:g> à€Źà€šà€Ÿ à€Šà„à€€à„ à€čà„"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> à€žà„ à€žà„âà€”à€€: à€žà„à€§à€Ÿà€° à€čà„à€à€Ÿ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"à€à„à€à€à„ à€à„à€Ą %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"à€¶à€żà€«à€Œà„à€"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift à€à€Ÿà€Čà„ (à€
à€à„à€·à€ź à€à€°à€šà„ à€à„ à€Čà€żà€ à€à„à€Ș à€à€°à„à€)"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index caef9541d..f5c9ad503 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"PrikljuÄite sluĆĄalice da biste Äuli tipke zaporke izgovorene naglas."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"TrenutaÄni tekst je %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nije unesen tekst"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ispravlja <xliff:g id="ORIGINAL">%2$s</xliff:g> u <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ima samoispravljanje"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"KĂŽd tipke %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"UkljuÄena tipka Shift (dotaknite da onemoguÄite)"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 3b0ee4798..5f4a18106 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Csatlakoztasson egy headsetet, ha hallani szeretnĂ© a jelszĂłt felolvasva."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"A jelenlegi szöveg: %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Szöveg nincs megadva"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> gomb: a(z) <xliff:g id="ORIGINAL">%2$s</xliff:g> Ă©rtĂ©ket <xliff:g id="CORRECTED">%3$s</xliff:g> Ă©rtĂ©kre javĂtja"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatikus javĂtĂĄs van beĂĄllĂtva a következĆhöz: <xliff:g id="KEY">%1$s</xliff:g>"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"BillentyƱkĂłd: %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift be van kapcsolva (Ă©rintse meg a kikapcsolĂĄshoz)"</string> diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml index 409c61e5a..57c0e61d9 100644 --- a/java/res/values-hy-rAM/strings.xml +++ b/java/res/values-hy-rAM/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ŐŐ«ŐĄÖÖŐ„Ö ŐĄŐŻŐĄŐ¶Ő»ŐĄŐŻŐĄŐŹŐšŐ ŐąŐĄÖŐ±ÖŐĄŐ±ŐĄŐ”Ő¶ ŐĄÖŐżŐĄŐœŐĄŐ¶ŐŸŐžŐČ ŐŁŐĄŐČŐżŐ¶ŐĄŐąŐĄŐŒŐš ŐŹŐœŐ„ŐŹŐžÖ Ő°ŐĄŐŽŐĄÖ:"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ŐŐŸŐ”ŐĄŐŹ ŐżŐ„ÖŐœŐżŐš %s Ő§"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ŐŐ„ÖŐœŐż ŐčŐ« ŐŽŐžÖŐżÖŐĄŐŁÖŐŸŐ„ŐŹ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>-Őš Ő·ŐżŐŻŐžÖŐŽ Ő§ <xliff:g id="ORIGINAL">%2$s</xliff:g>-Őš Ö Ő€ŐĄÖŐ±Ő¶ŐžÖŐŽ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-Ő¶ ŐžÖŐ¶Ő« Ő«Ő¶ÖŐ¶ŐžÖÖŐžÖŐ”Ő¶ Ő·ŐżŐŻŐžÖŐŽ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ÔČŐĄŐ¶ŐĄŐŹŐžÖ ŐŻŐžŐ€ŐšŐ %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift-Őš ŐŽŐ«ŐĄÖŐŸŐĄŐź Ő§ (Ő°ŐșŐ„ŐŹ ŐĄŐ¶Ő»ŐĄŐżŐ„ŐŹŐžÖ Ő°ŐĄŐŽŐĄÖ)"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index bfbd2a700..acd839413 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Pasang headset untuk mendengar tombol sandi yang diucapkan dengan keras."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Teks saat ini adalah %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Tidak ada teks yang dimasukkan"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> mengoreksi <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> memiliki koreksi otomatis"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Kode tombol %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift hidup (ketuk untuk mematikan)"</string> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 6989643af..2357aa267 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Collega gli auricolari per ascoltare la pronuncia dei tasti premuti per la password."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Il testo attuale Ăš %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nessun testo inserito"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL">%2$s</xliff:g> con <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ha la funzione di correzione automatica"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Codice tasto %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Maiuscolo"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maiuscolo attivo (tocca per disattivare)"</string> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index b3ee01410..1ad2c3e45 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ŚŚŚš ŚŚŚŚ ŚŚŚȘ ŚŚŚ ŚŚ©ŚŚŚą ŚŚ§ŚšŚŚ Ś©Ś ŚŚ€ŚȘŚŚŚȘ ŚĄŚŚĄŚŚ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ŚŚŚ§ŚĄŚ ŚŚ ŚŚŚŚ ŚŚŚ %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ŚŚ ŚŚŚŚ ŚŚ§ŚĄŚ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ŚŚȘŚ§Ś ŚŚȘ <xliff:g id="ORIGINAL">%2$s</xliff:g> Ś-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ŚŚŚŠŚą ŚȘŚŚ§ŚŚ ŚŚŚŚŚŚŚ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Ś§ŚŚ ŚŚ§Ś© %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift Ś€ŚŚąŚ (ŚŚ§Ś© ŚŚŚ ŚŚŚ©ŚŚŚȘ)"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 37f06377e..9cda30b73 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ăăčăŻăŒăăźăăŒăéłćٰćșćăăăăźă§ăăăă»ăăăæ„ç¶ăăŠăă ăăă"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"çŸćšăźăăăčă:%s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ăăăčăăć
„ćăăăŠăăŸăă"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>ăŻ<xliff:g id="ORIGINAL">%2$s</xliff:g>ă<xliff:g id="CORRECTED">%3$s</xliff:g>ă«äżźæŁăăŸă"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>ă§èȘćäżźæŁăćźèĄăăăŸă"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ăăŒăłăŒă:%d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ShiftæćčïŒăżăăăăŠè§Łé€ïŒ"</string> diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index 193f5115d..3fd168095 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ášáááá ááá á§áŁá áĄááȘáááá, á ááá ááááĄááááá ááá áá€ááá ááá áááᥠáááááášáááᥠáĄááźááááá."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"áááááááá á áąáá„áĄáąá áá áᥠ%s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"áąáá„áĄáąá áá ášáá§áááááá"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ášáááĄáŹáá ááᥠ<xliff:g id="ORIGINAL">%2$s</xliff:g>-ᥠ<xliff:g id="CORRECTED">%3$s</xliff:g>-áá"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-ᥠáááąáááá áá„áȘáá áááá©ááá"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"áááááááąáŁá áᥠáááá %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift á©áá ááŁááá (ášáááźáá áááááĄáá ááááá)"</string> diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml index 7294c7ee0..34c426944 100644 --- a/java/res/values-km-rKH/strings.xml +++ b/java/res/values-km-rKH/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"áááâáá¶á ááŸáááážâáááá¶ááâáá¶áááâááááá¶ááá"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"áąáááááâáá
áá
á»ááááááâááș %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"áááá¶áâáąáááááâââáá¶áâáááá
áŒá"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> áá <xliff:g id="ORIGINAL">%2$s</xliff:g> áá
<xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> áá¶áâáá¶áâááâáááááâáááááááá·"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ááŒáâáááá¶ááâá
á»á
%d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ááŸá Shift (âáááâááŸáááážâáá·á)"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 1497812e9..630ae7e4b 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ëčë°ëČíž í€ë„Œ ìì±ìŒëĄ ë€ìŒë €ë©Ž í€ëì
ì ì°êȰíìžì."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ì
ë „í í
ì€íž: %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"ì
ë „í í
ì€íž ìì"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>ì(넌) ë넎멎 <xliff:g id="ORIGINAL">%2$s</xliff:g>ì(넌) <xliff:g id="CORRECTED">%3$s</xliff:g>(ìŒ)ëĄ ìì í©ëë€."</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>ì(넌) ë넎멎 ìë ìì ë©ëë€."</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"í€ ìœë %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ìííž í€"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ìŹì©(ìŹì©íì§ ììŒë €ë©Ž ííìžì.)"</string> diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index 9bf46275d..0d7f7a27d 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"àșȘàșœàșàșȘàșČàșàș«àșčàșàș±àșà»àșàș·à»àșàșàș±àșàș„àș°àș«àș±àșàșà»àșČàș."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"àșà»à»àșàș§àșČàșĄàșàș°àșàșžàșàș±àșà»àșĄà»àș %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"àșà»à»àșĄàș”àșàșČàșà»àșȘà»àșà»à»àșàș§àșČàșĄ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> àșàș·àșà»àșàșàșàșČàș <xliff:g id="ORIGINAL">%2$s</xliff:g> à»àșàș±àș <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> àșà»à»àșĄàș”àșàșČàșàșàș§àșàșàșłàșàș·àșàșàș±àșàșàș°à»àșàșĄàș±àș"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"àș„àș°àș«àș±àșàșàș°à»àș %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift à»àșàș”àșàșàșłà»àșà»àșąàșčà» (àșàș»àșà»àșàș·à»àșàșàșŽàșàșàșłà»àșà»)"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 88328db7d..d40b54bc4 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Prijunkite ausines, kad iĆĄgirstumÄte sakomus slaptaĆŸodĆŸio klaviĆĄus."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Dabartinis tekstas yra %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"NÄra ÄŻvesto teksto"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"â<xliff:g id="KEY">%1$s</xliff:g>â pataiso â<xliff:g id="ORIGINAL">%2$s</xliff:g>â ÄŻ â<xliff:g id="CORRECTED">%3$s</xliff:g>â"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"â<xliff:g id="KEY">%1$s</xliff:g>â atlieka automatinÄŻ taisymÄ
"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"KlaviĆĄo kodas %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Antrojo lygio klaviĆĄas"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Äźjungtas antrasis lygis (palieskite, kad iĆĄjungtumÄte)"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 069c48701..cde9c3462 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Pievienojiet austiĆas, lai dzirdÄtu paroles rakstzÄ«mes."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"PaĆĄreizÄjais teksts ir %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nav ievadÄ«ts teksts"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"NospieĆŸot taustiĆu <xliff:g id="KEY">%1$s</xliff:g>, â<xliff:g id="ORIGINAL">%2$s</xliff:g>â tiek labots uz â<xliff:g id="CORRECTED">%3$s</xliff:g>â."</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"TaustiĆam <xliff:g id="KEY">%1$s</xliff:g> ir automÄtiskas laboĆĄanas funkcija."</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"TaustiĆu kods %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"PÄrslÄgĆĄanas taustiĆĆĄ"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"PÄrslÄgĆĄanas taustiĆĆĄ iespÄjots (pieskarieties, lai atspÄjotu)"</string> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index 3b039f120..177b5334f 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ĐŃŃŃ ÒŻĐłĐœĐžĐč ŃĐŸĐČŃĐœŃŃĐŽŃĐł ŃĐ°ĐœĐłĐ°Đ°Ń ŃĐœŃĐžŃ
ŃĐł ŃĐŸĐœŃĐŸŃ
ŃĐœ ŃŃлЎ ŃĐžŃ
ŃĐČŃŃŃ Đ·Đ°Đ»ĐłĐ°ĐœĐ° ŃŃ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ĐĐŽĐŸĐŸĐłĐžĐčĐœ ŃĐ”ĐșŃŃ %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșŃŃ ĐŸŃŃŃĐ»Đ°Đ°ĐłÒŻĐč"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ĐœŃ <xliff:g id="ORIGINAL">%2$s</xliff:g>-Đł <xliff:g id="CORRECTED">%3$s</xliff:g> Đ±ĐŸĐ»ĐłĐŸĐ¶ залŃŃŃĐ»ĐœĐ°"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> аĐČŃĐŸĐŒĐ°Ń Đ·Đ°Đ»ŃŃŃлагŃŃаĐč"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ĐąĐŸĐČŃĐžĐčĐœ ĐșĐŸĐŽ %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ĐĄŃлгŃŃ
"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ĐĄŃлгŃŃ
ĐžĐčĐł ОЎŃĐČŃ
Đ¶ÒŻÒŻĐ»ŃŃĐœ (ŃĐŸĐČŃОж ОЎŃĐČŃ
ĐłÒŻĐčĐ¶ÒŻÒŻĐ»ĐœŃ ÒŻÒŻ)"</string> diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml index ee241d64b..0e8b4eb00 100644 --- a/java/res/values-ms-rMY/strings.xml +++ b/java/res/values-ms-rMY/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Pasangkan set kepala untuk mendengar kekunci kata laluan disebut dengan kuat."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Teks semasa adalah %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Tiada teks dimasukkan"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> membetulkan <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> mempunyai auto pembetulan"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Kod kunci %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Kunci anjak dihidupkan (ketik untuk melumpuhkan)"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index ae43d1078..1bd91b6b4 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Koble til hodetelefoner for Ă„ hĂžre opplesing av bokstavene i passordet."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Gjeldende tekst er %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen tekst er skrevet inn"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigerer <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> har automatisk korrigering"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastaturkode %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift er pĂ„ (trykk for Ă„ deaktivere)"</string> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index de20da4f8..b1d1bb3dd 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Sluit een headset aan om wachtwoordtoetsen hardop te laten voorlezen."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige tekst is %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen tekst ingevoerd"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"Met <xliff:g id="KEY">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Met <xliff:g id="KEY">%1$s</xliff:g> voert u automatische correctie uit"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Toetscode %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om uit te schakelen)"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 390837596..f830b37c1 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"PodĆÄ
cz zestaw sĆuchawkowy, aby usĆyszeÄ znaki hasĆa wypowiadane na gĆos."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktualny tekst: %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie wprowadzono tekstu"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> zapewnia autokorektÄ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Kod klawisza: %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift wĆÄ
czony (kliknij, by wyĆÄ
czyÄ)"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 4c81479e5..dbf34f95a 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Ligar auscultadores com microfone integrado para ouvir as teclas da palavra-passe."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual Ă© %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tem correção automĂĄtica"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo da tecla %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (tocar para desativar)"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index ef533ae83..3f9837223 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Conecte um fone de ouvido para ouvir as chaves de senha em voz alta."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual Ă© %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> possui correção automĂĄtica"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo de tecla %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (toque para desativar)"</string> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 930b68bd6..f67f58ebd 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ConectaĆŁi un set cÄĆti-microfon pentru a auzi tastele apÄsate cĂąnd introduceĆŁi parola."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Textul curent este %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nu a fost introdus text"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corecteazÄ <xliff:g id="ORIGINAL">%2$s</xliff:g> cu <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispune de corectare automatÄ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Tasta cu codul %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Tasta Shift este activatÄ (apÄsaĆŁi pentru a o dezactiva)"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 8bfb011c5..20f358a77 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ĐĐŸĐŽĐșĐ»ŃŃĐžŃĐ” гаŃĐœĐžŃŃŃŃ, ŃŃĐŸĐ±Ń ŃŃĐ»ŃŃаŃŃ ĐżĐ°ŃĐŸĐ»Ń."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ĐĐČĐ”ĐŽĐ”ĐœĐœŃĐč ŃĐ”ĐșŃŃ: %s."</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșŃŃ ĐœĐ” ĐČĐČĐ”ĐŽĐ”Đœ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"ĐŃĐž ĐœĐ°Đ¶Đ°ŃОО ĐșлаĐČĐžŃĐž \"<xliff:g id="KEY">%1$s</xliff:g>\" ŃĐ»ĐŸĐČĐŸ \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" бŃĐŽĐ”Ń ĐžŃĐżŃаĐČĐ»Đ”ĐœĐŸ ĐœĐ° \"<xliff:g id="CORRECTED">%3$s</xliff:g>\""</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"ĐĐ»Ń ĐșлаĐČĐžŃĐž \"<xliff:g id="KEY">%1$s</xliff:g>\" ĐœĐ°Đ·ĐœĐ°ŃĐ”ĐœĐ° ĐŸĐżĐ”ŃаŃĐžŃ Đ°ĐČŃĐŸĐžŃĐżŃаĐČĐ»Đ”ĐœĐžŃ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ĐĐŸĐŽ ĐșлаĐČĐžŃĐž:%d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ĐлаĐČĐžŃа ĐČĐ”ŃŃ
ĐœĐ”ĐłĐŸ ŃДгОŃŃŃа"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ĐĐ”ŃŃ
ĐœĐžĐč ŃДгОŃŃŃ ĐČĐșĐ»ŃŃĐ”Đœ (ĐœĐ°Đ¶ĐŒĐžŃĐ”, ŃŃĐŸĐ±Ń ĐŸŃĐșĐ»ŃŃĐžŃŃ)"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 5c25f28ce..3f6706c08 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Ak si chcete pri zadĂĄvanĂ hesla vypoÄuĆ„ nahlas vyslovenĂ© klĂĄvesy, pripojte nĂĄhlavnĂș sĂșpravu."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"AktuĂĄlny text je %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie je zadanĂœ ĆŸiadny text"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"KlĂĄvesom <xliff:g id="KEY">%1$s</xliff:g> opravĂte <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"KlĂĄvesom <xliff:g id="KEY">%1$s</xliff:g> spustĂte automatickĂ© opravy"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"KĂłd klĂĄvesu %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"KlĂĄves Shift je zapnutĂœ (zakĂĄĆŸete ho klepnutĂm)"</string> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index dde01dd68..6c8115e78 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"PrikljuÄite sluĆĄalke, Äe ĆŸelite sliĆĄati izgovorjene tipke gesla."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Trenutno besedilo je %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ni vnesenega besedila"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"Tipka <xliff:g id="KEY">%1$s</xliff:g> popravi <xliff:g id="ORIGINAL">%2$s</xliff:g> v <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Tipka <xliff:g id="KEY">%1$s</xliff:g> izvede samodejno popravljanje"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Koda tipke %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift je vklopljen (dotaknite se, da onemogoÄite)"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index e7f6904c1..92e465c63 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ĐŁĐșŃŃŃĐžŃĐ” ŃĐ»ŃŃалОŃĐ” Ўа бОŃŃĐ” ŃŃлО ĐœĐ°ĐłĐ»Đ°Ń ĐžĐ·ĐłĐŸĐČĐŸŃĐ”ĐœĐ” ŃаŃŃĐ”ŃĐ” за Đ»ĐŸĐ·ĐžĐœĐșŃ."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ĐąŃĐ”ĐœŃŃĐœĐž ŃĐ”ĐșŃŃ ŃĐ” %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșŃŃ ĐœĐžŃĐ” ŃĐœĐ”Ń"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ĐžŃĐżŃаĐČŃа <xliff:g id="ORIGINAL">%2$s</xliff:g> Ń <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ĐžĐŒĐ° ŃŃĐœĐșŃĐžŃŃ Đ°ŃŃĐŸĐŒĐ°ŃŃĐșĐŸĐł ĐžŃĐżŃаĐČŃаŃа"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ĐĂŽĐŽ ŃаŃŃĐ”Ńа %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ŃĐ” ŃĐșŃŃŃĐ”Đœ (ĐŽĐŸĐŽĐžŃĐœĐžŃĐ” Ўа бОŃŃĐ” га ĐŸĐœĐ”ĐŒĐŸĐłŃŃОлО)"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 0fcca4837..009e8299a 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Anslut hörlurar om du vill att lösenordet ska lĂ€sas upp."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Nuvarande text Ă€r %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen text har angetts"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> Ă€ndrar <xliff:g id="ORIGINAL">%2$s</xliff:g> till <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatisk korrigering anvĂ€nds för <xliff:g id="KEY">%1$s</xliff:g>"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Nyckelkod %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Skift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift pĂ„ (knacka lĂ€tt för att inaktivera)"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index fbf868f24..a6ada5ba4 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Chomeka plagi ya kifaa cha kichwa cha kusikiza ili kusikiliza msimbo wa nenosiri inayozungumwa kwa sauti ya juu."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Maandishi ya sasa ni %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Hakuna maandishi yaliyoingizwa"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL">%2$s</xliff:g> hadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ina urekebishaji wa kiotomatiki"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Msimbo wa kitufe %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Badilisha"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift imewashwa (gonga ili kulemaza)"</string> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index f84b32c62..f2e252d4d 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"àčàžȘàž”àžąàžàžàžžàžàž«àžčàžàž±àžàčàžàž·àčàžàžàž±àžàčàžȘàž”àžąàžàčàžĄàž·àčàžàžàžŽàžĄàžàčàžŁàž«àž±àžȘàžàčàžČàž"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"àžàčàžàžàž§àžČàžĄàžàž±àžàžàžžàžàž±àžàžàž·àž %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"àčàžĄàčàžĄàž”àžàčàžàžàž§àžČàžĄ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> àžàž°àčàžàčàčàž <xliff:g id="ORIGINAL">%2$s</xliff:g> àčàžàčàž <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> àžàž°àžĄàž”àžàžČàžŁàčàžàčàčàžàžàž±àžàčàžàžĄàž±àžàžŽ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"àžŁàž«àž±àžȘàžàž”àžąàč %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift àčàžàžŽàžàžàžąàžčàč (àčàžàž°àčàžàž·àčàžàžàžŽàžàčàžàčàžàžČàž)"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index af1c68e01..9a3554588 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Mag-plug in ng headset upang marinig ang mga password key na binabanggit nang malakas."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Ang kasalukuyang teksto ay %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Walang tekstong inilagay"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"Itatama ng pagpindot sa <xliff:g id="KEY">%1$s</xliff:g> ang <xliff:g id="ORIGINAL">%2$s</xliff:g> at gagawing <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"May awtomatikong pagwasto ang <xliff:g id="KEY">%1$s</xliff:g>"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Code ng key %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Naka-on ang shift (i-tap upang huwag paganahin)"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 4c8e1a81d..ab376e190 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Ćifre tuĆlarının sesli okunmasını dinlemek için mikrofonlu kulaklık takın."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Mevcut metin: %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Hiç metin girilmedi"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> tuĆuna basıldıÄında <xliff:g id="ORIGINAL">%2$s</xliff:g>, <xliff:g id="CORRECTED">%3$s</xliff:g> olarak dĂŒzeltilir"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tuĆunda otomatik dĂŒzeltme iĆlevi var"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"TuĆ kodu: %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Ăst Karakter"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Ăst karakter açık (devre dıĆı bırakmak için hafifçe vurun)"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 287bcb397..2c516523f 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"ĐŃĐŽĐșĐ»ŃŃŃŃŃ ĐłĐ°ŃĐœŃŃŃŃŃ, ŃĐŸĐ± ĐżŃĐŸŃĐ»ŃŃ
аŃĐž ĐČŃĐŽŃĐČĐŸŃĐ”ĐœŃ ĐČĐłĐŸĐ»ĐŸŃ ŃĐžĐŒĐČĐŸĐ»Đž паŃĐŸĐ»Ń."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ĐĐŸŃĐŸŃĐœĐžĐč ŃĐ”ĐșŃŃ â %s."</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșŃŃ ĐœĐ” ĐČĐČĐ”ĐŽĐ”ĐœĐŸ"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ĐČОпŃаĐČĐ»ŃŃ <xliff:g id="ORIGINAL">%2$s</xliff:g> ĐœĐ° <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ĐŒĐ°Ń ŃŃĐœĐșŃŃŃ Đ°ĐČŃĐŸĐŒĐ°ŃĐžŃĐœĐŸĐłĐŸ ĐČОпŃаĐČĐ»Đ”ĐœĐœŃ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"ĐĐŸĐŽ ĐșлаĐČŃŃŃ â %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"ĐлаĐČŃŃа Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ŃĐČŃĐŒĐșĐœĐ”ĐœĐŸ (ŃĐČОЎĐșĐŸ ŃĐŸŃĐșĐœŃŃŃŃŃ, ŃĐŸĐ± ĐČĐžĐŒĐșĐœŃŃĐž)"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 8764df5e0..64b804af5 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"CáșŻm tai nghe Äá» nghe máșt kháș©u."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"KĂœ tá»± hiá»n táșĄi lĂ %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"KhĂŽng cĂł kĂœ tá»± nĂ o ÄÆ°á»Łc nháșp"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> sá»a <xliff:g id="ORIGINAL">%2$s</xliff:g> thĂ nh <xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> cĂł tĂnh nÄng tá»± Äá»ng sá»a"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"MĂŁ phĂm %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift Äang báșt (báș„m Äá» táșŻt)"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index f7ceff44f..7683c848e 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"éèŠæć
„èłæșæèœćŹć°ćŻç çæéźćٰă"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"ćœćææŹäžș%s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"æȘèŸć
„æć"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"æ<xliff:g id="KEY">%1$s</xliff:g>ćŻć°<xliff:g id="ORIGINAL">%2$s</xliff:g>æŽæŁäžș<xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"æ<xliff:g id="KEY">%1$s</xliff:g>ćŻæ§èĄèȘćšæŽæŁ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"éźç äžș %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift æšĄćŒć·ČćŻçšïŒçčæćłćŻćçšïŒ"</string> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index be00628e9..b18e0d9d3 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"æäžèłæ©ćłćŻèœć°çł»ç”±æèźćŻçąŒé”ă"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"çźćæćçș %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"æȘ茞ć
„æć"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"æă<xliff:g id="KEY">%1$s</xliff:g>ăćŻć°ă<xliff:g id="ORIGINAL">%2$s</xliff:g>ăäżźæŁçșă<xliff:g id="CORRECTED">%3$s</xliff:g>ă"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"ă<xliff:g id="KEY">%1$s</xliff:g>ăé”ć
·èȘćäżźæŁćèœ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"æé”ä»ŁçąŒ %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift é”"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift é”ć·Čéć (èŒæćłćŻćçš)"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index b773ad21c..ef3c833db 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"éŁæ„èłæ©ćłćŻèœć系由æèźćŻçąŒæé”ă"</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"çźćæćçș %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"æȘ茞ć
„æć"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"æäžă<xliff:g id="KEY">%1$s</xliff:g>ăćŻć°ă<xliff:g id="ORIGINAL">%2$s</xliff:g>ăäżźæŁçșă<xliff:g id="CORRECTED">%3$s</xliff:g>ă"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"æäžă<xliff:g id="KEY">%1$s</xliff:g>ăćŻć·èĄèȘćäżźæŁ"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"æé”ä»ŁçąŒ %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift é”"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift é”ć·Čéć (èŒæćłćŻćçš)"</string> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 8f722cce3..2dafde941 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -84,10 +84,8 @@ <string name="spoken_use_headphones" msgid="896961781287283493">"Plaka ku-headset ukuze uzwe okhiye bephasiwedi ezindlebeni zakho bezwakala kakhulu."</string> <string name="spoken_current_text_is" msgid="2485723011272583845">"Umbhalo wamanje ngu %s"</string> <string name="spoken_no_text_entered" msgid="7479685225597344496">"Awukho umbhalo ofakiwe"</string> - <!-- no translation found for spoken_auto_correct (5381764628886369268) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) --> - <skip /> + <string name="spoken_auto_correct" msgid="5381764628886369268">"I-<xliff:g id="KEY">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL">%2$s</xliff:g> ibe yi-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string> + <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"I-<xliff:g id="KEY">%1$s</xliff:g> inokulungiswa okuzenzakalelayo"</string> <string name="spoken_description_unknown" msgid="3197434010402179157">"Ikhodi yokhiye %d"</string> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"U-Shift uvuliwe (thepha ukuwuvimbela)"</string> diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml index 3803cb776..94fadb964 100644 --- a/java/res/values/colors.xml +++ b/java/res/values/colors.xml @@ -39,7 +39,6 @@ <color name="typed_word_color_ics">#D833B5E5</color> <color name="suggested_word_color_ics">#B233B5E5</color> <color name="highlight_translucent_color_ics">#9933B5E5</color> - <color name="key_text_color_ics">@android:color/white</color> <color name="key_text_shadow_color_ics">@android:color/transparent</color> <color name="key_text_inactivated_color_ics">#66E0E4E5</color> <color name="key_hint_letter_color_ics">#80000000</color> @@ -66,4 +65,7 @@ <!-- TODO: Color which should be included in the theme --> <color name="emoji_key_background_color">#00000000</color> <color name="emoji_key_pressed_background_color">#30FFFFFF</color> + + <color name="key_text_color_normal_ics">@android:color/white</color> + <color name="key_text_color_functional_ics">@android:color/white</color> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 06f4b4789..a779c6efa 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -174,10 +174,10 @@ <!-- Spoken description when there is no text entered --> <string name="spoken_no_text_entered">No text entered</string> - <!-- Spoken description to let the user know what auto-correction will be performed when a key is pressed. --> - <string name="spoken_auto_correct"><xliff:g id="key" example="Space">%1$s</xliff:g> corrects <xliff:g id="original">%2$s</xliff:g> to <xliff:g id="corrected">%3$s</xliff:g></string> + <!-- Spoken description to let the user know what auto-correction will be performed when a key is pressed. An auto-correction replaces a single word with one or more words. --> + <string name="spoken_auto_correct"><xliff:g id="key" example="Space">%1$s</xliff:g> corrects <xliff:g id="original_word">%2$s</xliff:g> to <xliff:g id="corrected">%3$s</xliff:g></string> <!-- Spoken description used during obscured (e.g. password) entry to let the user know that auto-correction will be performed when a key is pressed. --> - <string name="spoken_auto_correct_obscured"><xliff:g id="key" example="Space">%1$s</xliff:g> has auto-correction</string> + <string name="spoken_auto_correct_obscured"><xliff:g id="key" example="Space">%1$s</xliff:g> performs auto-correction</string> <!-- Spoken description for unknown keyboard keys. --> <string name="spoken_description_unknown">Key code %d</string> diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml index fbd8492cd..cf94b06ed 100644 --- a/java/res/xml-sw600dp/rows_symbols.xml +++ b/java/res/xml-sw600dp/rows_symbols.xml @@ -68,5 +68,7 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/row_symbols4" /> + <include + latin:keyboardLayout="@xml/key_f2" /> </Row> </merge> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index 67ed9620d..c9d87bfd4 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -121,6 +121,27 @@ latin:keyIcon="!icon/emoji_key" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> + <!-- Overriding EnterKeyStyle here --> + <switch> + <!-- Shift + Enter in textMultiLine field. --> + <case + latin:isMultiLine="true" + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" + > + <key-style + latin:styleName="enterKeyStyle" + latin:parentStyle="shiftEnterKeyStyle" /> + </case> + <!-- Smiley in textShortMessage field. + Overrides common enter key style. --> + <case + latin:mode="im" + > + <key-style + latin:styleName="enterKeyStyle" + latin:parentStyle="emojiKeyStyle" /> + </case> + </switch> <key-style latin:styleName="tabKeyStyle" latin:code="!code/key_tab" diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml index 0bf412fff..fbfdc5f72 100644 --- a/java/res/xml/row_symbols4.xml +++ b/java/res/xml/row_symbols4.xml @@ -39,8 +39,4 @@ <include latin:keyboardLayout="@xml/key_space_symbols" /> <include latin:keyboardLayout="@xml/keys_comma_period" /> - <Key - latin:keyStyle="emojiKeyStyle" - latin:keyWidth="fillRight" /> - </merge> diff --git a/java/res/xml/rows_symbols.xml b/java/res/xml/rows_symbols.xml index 3f102e277..d0606c63b 100644 --- a/java/res/xml/rows_symbols.xml +++ b/java/res/xml/rows_symbols.xml @@ -60,5 +60,8 @@ latin:keyWidth="15%p" /> <include latin:keyboardLayout="@xml/row_symbols4" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> </Row> </merge> diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java index 684165240..c28d72949 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java @@ -24,6 +24,8 @@ import android.preference.PreferenceActivity; * Preference screen. */ public final class DictionarySettingsActivity extends PreferenceActivity { + private static final String DEFAULT_FRAGMENT = DictionarySettingsFragment.class.getName(); + @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -32,11 +34,17 @@ public final class DictionarySettingsActivity extends PreferenceActivity { @Override public Intent getIntent() { final Intent modIntent = new Intent(super.getIntent()); - modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DictionarySettingsFragment.class.getName()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT); modIntent.putExtra(EXTRA_NO_HEADERS, true); // Important note : the original intent should contain a String extra with the key // DictionarySettingsFragment.DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT so that the // fragment can know who the client is. return modIntent; } + + // TODO: Uncomment the override annotation once we start using SDK version 19. + // @Override + public boolean isValidFragment(String fragmentName) { + return fragmentName.equals(DEFAULT_FRAGMENT); + } } diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java index 267fad5cd..71790b7d6 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java @@ -75,9 +75,7 @@ public class EmojiLayoutParams { public void setActionBarProperties(LinearLayout ll) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); - lp.height = mEmojiActionBarHeight; - lp.topMargin = 0; - lp.bottomMargin = mBottomPadding; + lp.height = mEmojiActionBarHeight - mBottomPadding; ll.setLayoutParams(lp); } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 3ea68806b..f7ec9509d 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -139,6 +139,8 @@ public class Key implements Comparable<Key> { private final OptionalAttributes mOptionalAttributes; + private static final int DEFAULT_TEXT_COLOR = 0xFFFFFFFF; + private static final class OptionalAttributes { /** Text to output when pressed. This can be multiple characters, like ".com" */ public final String mOutputText; @@ -602,7 +604,22 @@ public class Key implements Comparable<Key> { } public final int selectTextColor(final KeyDrawParams params) { - return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor; + if (isShiftedLetterActivated()) { + return params.mTextInactivatedColor; + } + if (params.mTextColorStateList == null) { + return DEFAULT_TEXT_COLOR; + } + final int[] state; + // TODO: Hack!!!!!!!! Consider having a new attribute for the functional text labels. + // Currently, we distinguish "input key" from "functional key" by checking the + // length of the label( > 1) and "functional" attributes (= true). + if (mLabel != null && mLabel.length() > 1) { + state = getCurrentDrawableState(); + } else { + state = KEY_STATE_NORMAL; + } + return params.mTextColorStateList.getColorForState(state, DEFAULT_TEXT_COLOR); } public final int selectHintTextSize(final KeyDrawParams params) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 74edd87cf..ad6e2c0f2 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -155,7 +155,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public void saveKeyboardState() { - if (getKeyboard() != null) { + if (getKeyboard() != null || isShowingEmojiKeyboard()) { mState.onSaveKeyboardState(); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java index 1716fa049..b528b692e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard.internal; +import android.content.res.ColorStateList; import android.graphics.Typeface; import com.android.inputmethod.latin.utils.ResourceUtils; @@ -32,7 +33,7 @@ public final class KeyDrawParams { public int mHintLabelSize; public int mPreviewTextSize; - public int mTextColor; + public ColorStateList mTextColorStateList; public int mTextInactivatedColor; public int mTextShadowColor; public int mHintLetterColor; @@ -57,7 +58,7 @@ public final class KeyDrawParams { mHintLabelSize = copyFrom.mHintLabelSize; mPreviewTextSize = copyFrom.mPreviewTextSize; - mTextColor = copyFrom.mTextColor; + mTextColorStateList = copyFrom.mTextColorStateList; mTextInactivatedColor = copyFrom.mTextInactivatedColor; mTextShadowColor = copyFrom.mTextShadowColor; mHintLetterColor = copyFrom.mHintLetterColor; @@ -89,8 +90,8 @@ public final class KeyDrawParams { attr.mShiftedLetterHintRatio, mShiftedLetterHintSize); mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize); mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize); - - mTextColor = selectColor(attr.mTextColor, mTextColor); + mTextColorStateList = + attr.mTextColorStateList != null ? attr.mTextColorStateList : mTextColorStateList; mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor); mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor); mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java index 7a2622cbb..8bdad364c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard.internal; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Typeface; import android.util.SparseIntArray; @@ -37,7 +38,7 @@ public final class KeyVisualAttributes { public final float mHintLabelRatio; public final float mPreviewTextRatio; - public final int mTextColor; + public final ColorStateList mTextColorStateList; public final int mTextInactivatedColor; public final int mTextShadowColor; public final int mHintLetterColor; @@ -115,7 +116,7 @@ public final class KeyVisualAttributes { mPreviewTextRatio = ResourceUtils.getFraction(keyAttr, R.styleable.Keyboard_Key_keyPreviewTextRatio); - mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0); + mTextColorStateList = keyAttr.getColorStateList(R.styleable.Keyboard_Key_keyTextColor); mTextInactivatedColor = keyAttr.getColor( R.styleable.Keyboard_Key_keyTextInactivatedColor, 0); mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 9f9fdaa6f..506dfa751 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -178,6 +178,8 @@ public final class KeyboardState { if (!state.mIsAlphabetShiftLocked) { setShifted(state.mShiftMode); } + // TODO: is this the right place to do this? Should we do this in setShift* instead? + mSwitchActions.requestUpdatingShiftState(); } else { mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked; } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 306c1a253..c79a4ff90 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -249,6 +249,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final File file = new File(mContext.getFilesDir(), mFilename); BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); + mBinaryDictionary = new BinaryDictionary( + file.getAbsolutePath(), 0 /* offset */, file.length(), + true /* useFullEditDistance */, null, mDictType, mIsUpdatable); } else { mDictionaryWriter.clear(); } @@ -273,11 +276,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { lastModifiedTime); } - private void runGCIfRequired() { + /** + * Check whether GC is needed and run GC if required. + */ + protected void runGCIfRequired(final boolean mindsBlockByGC) { + if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + runGCIfRequiredInternalLocked(mindsBlockByGC); + } + }); + } + + private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) { if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; - if (mBinaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + // Calls to needsToRunGC() need to be serialized. + if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) { if (setIsRegeneratingIfNotRegenerating()) { - getExecutor(mFilename).execute(new Runnable() { + // Run GC after currently existing time sensitive operations. + getExecutor(mFilename).executePrioritized(new Runnable() { @Override public void run() { try { @@ -300,11 +318,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename); return; } - runGCIfRequired(); getExecutor(mFilename).execute(new Runnable() { @Override public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); mBinaryDictionary.addUnigramWord(word, frequency); } else { // TODO: Remove. @@ -324,11 +342,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mFilename); return; } - runGCIfRequired(); getExecutor(mFilename).execute(new Runnable() { @Override public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); mBinaryDictionary.addBigramWords(word0, word1, frequency); } else { // TODO: Remove. @@ -348,11 +366,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mFilename); return; } - runGCIfRequired(); getExecutor(mFilename).execute(new Runnable() { @Override public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); mBinaryDictionary.removeBigramWords(word0, word1); } else { // TODO: Remove. @@ -479,8 +497,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final long length = file.length(); // Build the new binary dictionary - final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length, - true /* useFullEditDistance */, null, mDictType, mIsUpdatable); + final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0 /* offset */, + length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable); // Ensure all threads accessing the current dictionary have finished before // swapping in the new one. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 96e16de0d..0f3d28976 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -77,6 +77,7 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister; import com.android.inputmethod.latin.personalization.PersonalizationHelper; @@ -567,6 +568,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); registerReceiver(mDictionaryPackInstallReceiver, newDictFilter); + DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); + mInputUpdater = new InputUpdater(this); } @@ -2929,6 +2932,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } tryFixLyingCursorPosition(); + mKeyboardSwitcher.updateShiftState(); if (tryResumeSuggestions) mHandler.postResumeSuggestions(); } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 665c7a27c..2c3d1346f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -295,7 +295,6 @@ public final class BinaryDictDecoderUtils { return address; } } - int address; switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: return dictBuffer.readUnsignedByte(); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index af61f2979..b6024243f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -278,7 +278,6 @@ public class BinaryDictEncoderUtils { // For future reference, the code to remove duplicate is a simple : list.remove(node); list.add(ptNodeArray); final ArrayList<PtNode> branches = ptNodeArray.mData; - final int nodeSize = branches.size(); for (PtNode ptNode : branches) { if (null != ptNode.mChildren) flattenTreeInner(list, ptNode.mChildren); } @@ -427,9 +426,6 @@ public class BinaryDictEncoderUtils { nodeCountSize + nodeArrayOffset + nodeffset; nodeffset += ptNode.mCachedSize; } - final int nodeSize = nodeCountSize + nodeffset - + (formatOptions.mSupportsDynamicUpdate - ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0); nodeArrayOffset += nodeArray.mCachedSize; } return nodeArrayOffset; @@ -653,8 +649,8 @@ public class BinaryDictEncoderUtils { return flags; } - /* package */ static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress, - final int childrenOffset, final FormatOptions formatOptions) { + /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset, + final FormatOptions formatOptions) { return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0, getByteSize(childrenOffset), node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(), diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index e90137674..0f7d2f6c9 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -288,42 +288,6 @@ public final class BinaryDictIOUtils { return BinaryDictEncoderUtils.getByteSize(value); } - // TODO: Remove this method. - @Deprecated - static void skipPtNode(final DictBuffer dictBuffer, final FormatOptions formatOptions) { - final int flags = dictBuffer.readUnsignedByte(); - BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions); - skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); - BinaryDictDecoderUtils.readChildrenAddress(dictBuffer, flags, formatOptions); - if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte(); - if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) { - final int shortcutsSize = dictBuffer.readUnsignedShort(); - dictBuffer.position(dictBuffer.position() + shortcutsSize - - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE); - } - if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) { - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = dictBuffer.readUnsignedByte(); - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - dictBuffer.readUnsignedByte(); - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - dictBuffer.readUnsignedShort(); - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - dictBuffer.readUnsignedInt24(); - break; - } - if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break; - } - if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - throw new RuntimeException("Too many bigrams in a PtNode."); - } - } - } - static void skipString(final DictBuffer dictBuffer, final boolean hasMultipleChars) { if (hasMultipleChars) { diff --git a/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java new file mode 100644 index 000000000..413d0301c --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * An interface of a binary dictionary updater. + */ +public interface DictUpdater { + + /** + * Deletes the word from the binary dictionary. + * + * @param word the word to be deleted. + */ + public void deleteWord(final String word) throws IOException, UnsupportedFormatException; + + /** + * Inserts a word into a binary dictionary. + * + * @param word the word to be inserted. + * @param frequency the frequency of the new word. + * @param bigramStrings bigram list, or null if none. + * @param shortcuts shortcut list, or null if none. + * @param isBlackListEntry whether this should be a blacklist entry. + */ + // TODO: Support batch insertion. + public void insertWord(final String word, final int frequency, + final ArrayList<WeightedString> bigramStrings, + final ArrayList<WeightedString> shortcuts, final boolean isNotAWord, + final boolean isBlackListEntry) throws IOException, UnsupportedFormatException; +} diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java index 411e265b3..336277196 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java @@ -42,44 +42,22 @@ public final class DynamicBinaryDictIOUtils { // This utility class is not publicly instantiable. } - private static int markAsDeleted(final int flags) { + /* package */ static int markAsDeleted(final int flags) { return (flags & (~FormatSpec.MASK_CHILDREN_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED; } /** - * Delete the word from the binary file. - * - * @param dictDecoder the dict decoder. - * @param word the word we delete - * @throws IOException - * @throws UnsupportedFormatException - */ - @UsedForTesting - public static void deleteWord(final Ver3DictDecoder dictDecoder, final String word) - throws IOException, UnsupportedFormatException { - final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); - dictBuffer.position(0); - final FileHeader header = dictDecoder.readHeader(); - final int wordPosition = dictDecoder.getTerminalPosition(word); - if (wordPosition == FormatSpec.NOT_VALID_WORD) return; - - dictBuffer.position(wordPosition); - final int flags = dictBuffer.readUnsignedByte(); - dictBuffer.position(wordPosition); - dictBuffer.put((byte)markAsDeleted(flags)); - } - - /** * Update a parent address in a PtNode that is referred to by ptNodeOriginAddress. * - * @param dictBuffer the DictBuffer to write. + * @param dictUpdater the DictUpdater to write. * @param ptNodeOriginAddress the address of the PtNode. * @param newParentAddress the absolute address of the parent. * @param formatOptions file format options. */ - private static void updateParentAddress(final DictBuffer dictBuffer, + private static void updateParentAddress(final Ver3DictUpdater dictUpdater, final int ptNodeOriginAddress, final int newParentAddress, final FormatOptions formatOptions) { + final DictBuffer dictBuffer = dictUpdater.getDictBuffer(); final int originalPosition = dictBuffer.position(); dictBuffer.position(ptNodeOriginAddress); if (!formatOptions.mSupportsDynamicUpdate) { @@ -104,46 +82,45 @@ public final class DynamicBinaryDictIOUtils { /** * Update parent addresses in a node array stored at ptNodeOriginAddress. * - * @param dictBuffer the DictBuffer to be modified. + * @param dictUpdater the DictUpdater to be modified. * @param ptNodeOriginAddress the address of the node array to update. * @param newParentAddress the address to be written. * @param formatOptions file format options. */ - private static void updateParentAddresses(final DictBuffer dictBuffer, + private static void updateParentAddresses(final Ver3DictUpdater dictUpdater, final int ptNodeOriginAddress, final int newParentAddress, final FormatOptions formatOptions) { - final int originalPosition = dictBuffer.position(); - dictBuffer.position(ptNodeOriginAddress); + final int originalPosition = dictUpdater.getPosition(); + dictUpdater.setPosition(ptNodeOriginAddress); do { - final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer); + final int count = dictUpdater.readPtNodeCount(); for (int i = 0; i < count; ++i) { - updateParentAddress(dictBuffer, dictBuffer.position(), newParentAddress, + updateParentAddress(dictUpdater, dictUpdater.getPosition(), newParentAddress, formatOptions); - BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions); + dictUpdater.skipPtNode(formatOptions); } - final int forwardLinkAddress = dictBuffer.readUnsignedInt24(); - dictBuffer.position(forwardLinkAddress); - } while (formatOptions.mSupportsDynamicUpdate - && dictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS); - dictBuffer.position(originalPosition); + if (!dictUpdater.readAndFollowForwardLink()) break; + if (dictUpdater.getPosition() == FormatSpec.NO_FORWARD_LINK_ADDRESS) break; + } while (formatOptions.mSupportsDynamicUpdate); + dictUpdater.setPosition(originalPosition); } /** * Update a children address in a PtNode that is addressed by ptNodeOriginAddress. * - * @param dictBuffer the DictBuffer to write. + * @param dictUpdater the DictUpdater to write. * @param ptNodeOriginAddress the address of the PtNode. * @param newChildrenAddress the absolute address of the child. * @param formatOptions file format options. */ - private static void updateChildrenAddress(final DictBuffer dictBuffer, + private static void updateChildrenAddress(final Ver3DictUpdater dictUpdater, final int ptNodeOriginAddress, final int newChildrenAddress, final FormatOptions formatOptions) { + final DictBuffer dictBuffer = dictUpdater.getDictBuffer(); final int originalPosition = dictBuffer.position(); dictBuffer.position(ptNodeOriginAddress); final int flags = dictBuffer.readUnsignedByte(); - final int parentAddress = BinaryDictDecoderUtils.readParentAddress(dictBuffer, - formatOptions); + BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions); BinaryDictIOUtils.skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte(); final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS @@ -156,31 +133,33 @@ public final class DynamicBinaryDictIOUtils { * Helper method to move a PtNode to the tail of the file. */ private static int movePtNode(final OutputStream destination, - final DictBuffer dictBuffer, final PtNodeInfo info, + final Ver3DictUpdater dictUpdater, final PtNodeInfo info, final int nodeArrayOriginAddress, final int oldNodeAddress, final FormatOptions formatOptions) throws IOException { - updateParentAddress(dictBuffer, oldNodeAddress, dictBuffer.limit() + 1, formatOptions); + final DictBuffer dictBuffer = dictUpdater.getDictBuffer(); + updateParentAddress(dictUpdater, oldNodeAddress, dictBuffer.limit() + 1, formatOptions); dictBuffer.position(oldNodeAddress); final int currentFlags = dictBuffer.readUnsignedByte(); dictBuffer.position(oldNodeAddress); dictBuffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG)))); int size = FormatSpec.PTNODE_FLAGS_SIZE; - updateForwardLink(dictBuffer, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions); + updateForwardLink(dictUpdater, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions); size += BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { info }); return size; } @SuppressWarnings("unused") - private static void updateForwardLink(final DictBuffer dictBuffer, + private static void updateForwardLink(final Ver3DictUpdater dictUpdater, final int nodeArrayOriginAddress, final int newNodeArrayAddress, final FormatOptions formatOptions) { - dictBuffer.position(nodeArrayOriginAddress); + final DictBuffer dictBuffer = dictUpdater.getDictBuffer(); + dictUpdater.setPosition(nodeArrayOriginAddress); int jumpCount = 0; while (jumpCount++ < MAX_JUMPS) { - final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer); + final int count = dictUpdater.readPtNodeCount(); for (int i = 0; i < count; ++i) { - BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions); + dictUpdater.readPtNode(dictUpdater.getPosition(), formatOptions); } final int forwardLinkAddress = dictBuffer.readUnsignedInt24(); if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) { @@ -208,7 +187,7 @@ public final class DynamicBinaryDictIOUtils { * @param shortcutTargets the shortcut targets for this PtNode. * @param bigrams the bigrams for this PtNode. * @param destination the stream representing the tail of the file. - * @param dictBuffer the DictBuffer representing the (constant-size) body of the file. + * @param dictUpdater the DictUpdater. * @param oldPtNodeArrayOrigin the origin of the old PtNode array this PtNode was a part of. * @param oldPtNodeOrigin the old origin where this PtNode used to be stored. * @param formatOptions format options for this dictionary. @@ -219,7 +198,7 @@ public final class DynamicBinaryDictIOUtils { final int length, final int flags, final int frequency, final int parentAddress, final ArrayList<WeightedString> shortcutTargets, final ArrayList<PendingAttribute> bigrams, final OutputStream destination, - final DictBuffer dictBuffer, final int oldPtNodeArrayOrigin, + final Ver3DictUpdater dictUpdater, final int oldPtNodeArrayOrigin, final int oldPtNodeOrigin, final FormatOptions formatOptions) throws IOException { int size = 0; final int newPtNodeOrigin = fileEndAddress + 1; @@ -232,7 +211,7 @@ public final class DynamicBinaryDictIOUtils { flags, writtenCharacters, frequency, parentAddress, fileEndAddress + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets, bigrams); - movePtNode(destination, dictBuffer, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin, + movePtNode(destination, dictUpdater, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin, formatOptions); return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE; } @@ -240,7 +219,7 @@ public final class DynamicBinaryDictIOUtils { /** * Insert a word into a binary dictionary. * - * @param dictDecoder the dict decoder. + * @param dictUpdater the dict updater. * @param destination a stream to the underlying file, with the pointer at the end of the file. * @param word the word to insert. * @param frequency the frequency of the new word. @@ -253,17 +232,17 @@ public final class DynamicBinaryDictIOUtils { // TODO: Support batch insertion. // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary. @UsedForTesting - public static void insertWord(final Ver3DictDecoder dictDecoder, + public static void insertWord(final Ver3DictUpdater dictUpdater, final OutputStream destination, final String word, final int frequency, final ArrayList<WeightedString> bigramStrings, final ArrayList<WeightedString> shortcuts, final boolean isNotAWord, final boolean isBlackListEntry) throws IOException, UnsupportedFormatException { final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>(); - final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); + final DictBuffer dictBuffer = dictUpdater.getDictBuffer(); if (bigramStrings != null) { for (final WeightedString bigram : bigramStrings) { - int position = dictDecoder.getTerminalPosition(bigram.mWord); + int position = dictUpdater.getTerminalPosition(bigram.mWord); if (position == FormatSpec.NOT_VALID_WORD) { // TODO: figure out what is the correct thing to do here. } else { @@ -278,7 +257,7 @@ public final class DynamicBinaryDictIOUtils { // find the insert position of the word. if (dictBuffer.position() != 0) dictBuffer.position(0); - final FileHeader fileHeader = dictDecoder.readHeader(); + final FileHeader fileHeader = dictUpdater.readHeader(); int wordPos = 0, address = dictBuffer.position(), nodeOriginAddress = dictBuffer.position(); final int[] codePoints = FusionDictionary.getCodePoints(word); @@ -293,7 +272,7 @@ public final class DynamicBinaryDictIOUtils { for (int i = 0; i < ptNodeCount; ++i) { address = dictBuffer.position(); - final PtNodeInfo currentInfo = dictDecoder.readPtNode(address, + final PtNodeInfo currentInfo = dictUpdater.readPtNode(address, fileHeader.mFormatOptions); final boolean isMovedNode = BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, fileHeader.mFormatOptions); @@ -319,12 +298,12 @@ public final class DynamicBinaryDictIOUtils { false /* isBlackListEntry */, fileHeader.mFormatOptions); int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p, flags, frequency, nodeParentAddress, shortcuts, bigrams, destination, - dictBuffer, nodeOriginAddress, address, fileHeader.mFormatOptions); + dictUpdater, nodeOriginAddress, address, fileHeader.mFormatOptions); final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p, currentInfo.mCharacters.length); if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { - updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress, + updateParentAddresses(dictUpdater, currentInfo.mChildrenAddress, newNodeAddress + written + 1, fileHeader.mFormatOptions); } final PtNodeInfo newInfo2 = new PtNodeInfo( @@ -360,13 +339,13 @@ public final class DynamicBinaryDictIOUtils { fileHeader.mFormatOptions); int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p, prefixFlags, -1 /* frequency */, nodeParentAddress, null, null, - destination, dictBuffer, nodeOriginAddress, address, + destination, dictUpdater, nodeOriginAddress, address, fileHeader.mFormatOptions); final int[] suffixCharacters = Arrays.copyOfRange( currentInfo.mCharacters, p, currentInfo.mCharacters.length); if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { - updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress, + updateParentAddresses(dictUpdater, currentInfo.mChildrenAddress, newNodeAddress + written + 1, fileHeader.mFormatOptions); } final int suffixFlags = BinaryDictEncoderUtils.makePtNodeFlags( @@ -417,7 +396,7 @@ public final class DynamicBinaryDictIOUtils { -1 /* endAddress */, flags, currentInfo.mCharacters, frequency, nodeParentAddress, currentInfo.mChildrenAddress, shortcuts, bigrams); - movePtNode(destination, dictBuffer, newInfo, nodeOriginAddress, address, + movePtNode(destination, dictUpdater, newInfo, nodeOriginAddress, address, fileHeader.mFormatOptions); return; } @@ -436,7 +415,7 @@ public final class DynamicBinaryDictIOUtils { * ab - cd - e */ final int newNodeArrayAddress = dictBuffer.limit(); - updateChildrenAddress(dictBuffer, address, newNodeArrayAddress, + updateChildrenAddress(dictUpdater, address, newNodeArrayAddress, fileHeader.mFormatOptions); final int newNodeAddress = newNodeArrayAddress + 1; final boolean hasMultipleChars = (wordLen - wordPos) > 1; diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 9481a8c14..a5516bd41 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -266,11 +266,14 @@ public final class FormatSpec { // tat = Terminal Address Table static final String TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat"; static final String BIGRAM_FILE_EXTENSION = ".bigram"; - static final String BIGRAM_LOOKUP_TABLE_FILE_EXTENSION = ".bigram_lookup"; - static final String BIGRAM_ADDRESS_TABLE_FILE_EXTENSION = ".bigram_index"; + static final String LOOKUP_TABLE_FILE_SUFFIX = "_lookup"; + static final String CONTENT_TABLE_FILE_SUFFIX = "_index"; static final int FREQUENCY_AND_FLAGS_SIZE = 2; static final int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3; static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4; + static final int BIGRAM_CONTENT_COUNT = 1; + static final int BIGRAM_FREQ_CONTENT_INDEX = 0; + static final String BIGRAM_FREQ_CONTENT_ID = "_freq"; static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE; static final int NO_PARENT_ADDRESS = 0; diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java b/java/src/com/android/inputmethod/latin/makedict/SparseTable.java index 96d057a44..7592a0c13 100644 --- a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java +++ b/java/src/com/android/inputmethod/latin/makedict/SparseTable.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; import java.io.FileInputStream; @@ -37,35 +38,39 @@ public class SparseTable { /** * mLookupTable is indexed by terminal ID, containing exactly one entry for every mBlockSize * terminals. - * It contains at index i = j / mBlockSize the index in mContentsTable where the values for - * terminals with IDs j to j + mBlockSize - 1 are stored as an mBlockSize-sized integer array. + * It contains at index i = j / mBlockSize the index in each ArrayList in mContentsTables where + * the values for terminals with IDs j to j + mBlockSize - 1 are stored as an mBlockSize-sized + * integer array. */ private final ArrayList<Integer> mLookupTable; - private final ArrayList<Integer> mContentTable; + private final ArrayList<ArrayList<Integer>> mContentTables; private final int mBlockSize; + private final int mContentTableCount; public static final int NOT_EXIST = -1; + public static final int SIZE_OF_INT_IN_BYTES = 4; @UsedForTesting - public SparseTable(final int initialCapacity, final int blockSize) { + public SparseTable(final int initialCapacity, final int blockSize, + final int contentTableCount) { mBlockSize = blockSize; final int lookupTableSize = initialCapacity / mBlockSize + (initialCapacity % mBlockSize > 0 ? 1 : 0); mLookupTable = new ArrayList<Integer>(Collections.nCopies(lookupTableSize, NOT_EXIST)); - mContentTable = new ArrayList<Integer>(); + mContentTableCount = contentTableCount; + mContentTables = CollectionUtils.newArrayList(); + for (int i = 0; i < mContentTableCount; ++i) { + mContentTables.add(new ArrayList<Integer>()); + } } @UsedForTesting - public SparseTable(final int[] lookupTable, final int[] contentTable, final int blockSize) { + public SparseTable(final ArrayList<Integer> lookupTable, + final ArrayList<ArrayList<Integer>> contentTables, final int blockSize) { mBlockSize = blockSize; - mLookupTable = new ArrayList<Integer>(lookupTable.length); - for (int i = 0; i < lookupTable.length; ++i) { - mLookupTable.add(lookupTable[i]); - } - mContentTable = new ArrayList<Integer>(contentTable.length); - for (int i = 0; i < contentTable.length; ++i) { - mContentTable.add(contentTable[i]); - } + mContentTableCount = contentTables.size(); + mLookupTable = lookupTable; + mContentTables = contentTables; } /** @@ -75,8 +80,8 @@ public class SparseTable { * Otherwise, IndexOutOfBoundsException will be raised. */ @UsedForTesting - private static void convertByteArrayToIntegerArray(final byte[] byteArray, - final ArrayList<Integer> integerArray) { + private static ArrayList<Integer> convertByteArrayToIntegerArray(final byte[] byteArray) { + final ArrayList<Integer> integerArray = new ArrayList<Integer>(byteArray.length / 4); for (int i = 0; i < byteArray.length; i += 4) { int value = 0; for (int j = i; j < i + 4; ++j) { @@ -85,39 +90,43 @@ public class SparseTable { } integerArray.add(value); } + return integerArray; } @UsedForTesting - public SparseTable(final byte[] lookupTable, final byte[] contentTable, final int blockSize) { - mBlockSize = blockSize; - mLookupTable = new ArrayList<Integer>(lookupTable.length / 4); - mContentTable = new ArrayList<Integer>(contentTable.length / 4); - convertByteArrayToIntegerArray(lookupTable, mLookupTable); - convertByteArrayToIntegerArray(contentTable, mContentTable); + public int get(final int contentTableIndex, final int index) { + if (!contains(index)) { + return NOT_EXIST; + } + return mContentTables.get(contentTableIndex).get( + mLookupTable.get(index / mBlockSize) + (index % mBlockSize)); } @UsedForTesting - public int get(final int index) { - if (index < 0 || index / mBlockSize >= mLookupTable.size() - || mLookupTable.get(index / mBlockSize) == NOT_EXIST) { - return NOT_EXIST; + public ArrayList<Integer> getAll(final int index) { + final ArrayList<Integer> ret = CollectionUtils.newArrayList(); + for (int i = 0; i < mContentTableCount; ++i) { + ret.add(get(i, index)); } - return mContentTable.get(mLookupTable.get(index / mBlockSize) + (index % mBlockSize)); + return ret; } @UsedForTesting - public void set(final int index, final int value) { + public void set(final int contentTableIndex, final int index, final int value) { if (mLookupTable.get(index / mBlockSize) == NOT_EXIST) { - mLookupTable.set(index / mBlockSize, mContentTable.size()); - for (int i = 0; i < mBlockSize; ++i) { - mContentTable.add(NOT_EXIST); + mLookupTable.set(index / mBlockSize, mContentTables.get(contentTableIndex).size()); + for (int i = 0; i < mContentTableCount; ++i) { + for (int j = 0; j < mBlockSize; ++j) { + mContentTables.get(i).add(NOT_EXIST); + } } } - mContentTable.set(mLookupTable.get(index / mBlockSize) + (index % mBlockSize), value); + mContentTables.get(contentTableIndex).set( + mLookupTable.get(index / mBlockSize) + (index % mBlockSize), value); } - public void remove(final int index) { - set(index, NOT_EXIST); + public void remove(final int indexOfContent, final int index) { + set(indexOfContent, index, NOT_EXIST); } @UsedForTesting @@ -127,7 +136,8 @@ public class SparseTable { @UsedForTesting /* package */ int getContentTableSize() { - return mContentTable.size(); + // This class always has at least one content table. + return mContentTables.get(0).size(); } @UsedForTesting @@ -136,36 +146,51 @@ public class SparseTable { } public boolean contains(final int index) { - return get(index) != NOT_EXIST; + if (index < 0 || index / mBlockSize >= mLookupTable.size() + || mLookupTable.get(index / mBlockSize) == NOT_EXIST) { + return false; + } + return true; } @UsedForTesting - public void write(final OutputStream lookupOutStream, final OutputStream contentOutStream) + public void write(final OutputStream lookupOutStream, final OutputStream[] contentOutStreams) throws IOException { + if (contentOutStreams.length != mContentTableCount) { + throw new RuntimeException(contentOutStreams.length + " streams are given, but the" + + " table has " + mContentTableCount + " content tables."); + } for (final int index : mLookupTable) { - BinaryDictEncoderUtils.writeUIntToStream(lookupOutStream, index, 4); + BinaryDictEncoderUtils.writeUIntToStream(lookupOutStream, index, SIZE_OF_INT_IN_BYTES); } - for (final int index : mContentTable) { - BinaryDictEncoderUtils.writeUIntToStream(contentOutStream, index, 4); + for (int i = 0; i < contentOutStreams.length; ++i) { + for (final int data : mContentTables.get(i)) { + BinaryDictEncoderUtils.writeUIntToStream(contentOutStreams[i], data, + SIZE_OF_INT_IN_BYTES); + } } } @UsedForTesting - public void writeToFiles(final File lookupTableFile, final File contentFile) + public void writeToFiles(final File lookupTableFile, final File[] contentFiles) throws IOException { - FileOutputStream lookupTableOutStream = null; - FileOutputStream contentOutStream = null; + FileOutputStream lookupTableOutStream = null; + final FileOutputStream[] contentTableOutStreams = new FileOutputStream[mContentTableCount]; try { lookupTableOutStream = new FileOutputStream(lookupTableFile); - contentOutStream = new FileOutputStream(contentFile); - write(lookupTableOutStream, contentOutStream); + for (int i = 0; i < contentFiles.length; ++i) { + contentTableOutStreams[i] = new FileOutputStream(contentFiles[i]); + } + write(lookupTableOutStream, contentTableOutStreams); } finally { if (lookupTableOutStream != null) { lookupTableOutStream.close(); } - if (contentOutStream != null) { - contentOutStream.close(); + for (int i = 0; i < contentTableOutStreams.length; ++i) { + if (contentTableOutStreams[i] != null) { + contentTableOutStreams[i].close(); + } } } } @@ -185,10 +210,14 @@ public class SparseTable { } @UsedForTesting - public static SparseTable readFromFiles(final File lookupTableFile, final File contentFile, + public static SparseTable readFromFiles(final File lookupTableFile, final File[] contentFiles, final int blockSize) throws IOException { - final byte[] lookupTable = readFileToByteArray(lookupTableFile); - final byte[] content = readFileToByteArray(contentFile); - return new SparseTable(lookupTable, content, blockSize); + final ArrayList<ArrayList<Integer>> contentTables = + new ArrayList<ArrayList<Integer>>(contentFiles.length); + for (int i = 0; i < contentFiles.length; ++i) { + contentTables.add(convertByteArrayToIntegerArray(readFileToByteArray(contentFiles[i]))); + } + return new SparseTable(convertByteArrayToIntegerArray(readFileToByteArray(lookupTableFile)), + contentTables, blockSize); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java index 75d1058ad..b87259c38 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java @@ -53,9 +53,9 @@ public class Ver3DictDecoder extends DictDecoder { } } - private final File mDictionaryBinaryFile; + protected final File mDictionaryBinaryFile; private final DictionaryBufferFactory mBufferFactory; - private DictBuffer mDictBuffer; + protected DictBuffer mDictBuffer; /* package */ Ver3DictDecoder(final File file, final int factoryFlag) { mDictionaryBinaryFile = file; @@ -169,7 +169,8 @@ public class Ver3DictDecoder extends DictDecoder { addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, addressPointer); if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - MakedictLog.d("too many bigrams in a PtNode."); + throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size() + + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); } } else { bigrams = null; diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java index 76f0f4052..d9e19899c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java @@ -133,12 +133,10 @@ public class Ver3DictEncoder implements DictEncoder { countSize); } - private void writePtNodeFlags(final PtNode ptNode, final int parentAddress, - final FormatOptions formatOptions) { + private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) { final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, - BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mPosition, childrenPos, - formatOptions), + BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions), FormatSpec.PTNODE_FLAGS_SIZE); } @@ -244,7 +242,7 @@ public class Ver3DictEncoder implements DictEncoder { @Override public void writePtNode(final PtNode ptNode, final int parentPosition, final FormatOptions formatOptions, final FusionDictionary dict) { - writePtNodeFlags(ptNode, parentPosition, formatOptions); + writePtNodeFlags(ptNode, formatOptions); writeParentPosition(parentPosition, ptNode, formatOptions); writeCharacters(ptNode.mChars, ptNode.hasSeveralChars()); writeFrequency(ptNode.mFrequency); diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java new file mode 100644 index 000000000..fa7ae310a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; + +/** + * An implementation of DictUpdater for version 3 binary dictionary. + */ +@UsedForTesting +public class Ver3DictUpdater extends Ver3DictDecoder implements DictUpdater { + private OutputStream mOutStream; + + @UsedForTesting + public Ver3DictUpdater(final File dictFile, final int factoryType) { + // DictUpdater must have an updatable DictBuffer. + super(dictFile, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY) + ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER); + mOutStream = null; + } + + private void openStreamAndBuffer() throws FileNotFoundException, IOException { + super.openDictBuffer(); + mOutStream = new FileOutputStream(mDictionaryBinaryFile, true /* append */); + } + + private void close() throws IOException { + if (mOutStream != null) { + mOutStream.close(); + mOutStream = null; + } + } + + @Override @UsedForTesting + public void deleteWord(final String word) throws IOException, UnsupportedFormatException { + if (mOutStream == null) openStreamAndBuffer(); + mDictBuffer.position(0); + super.readHeader(); + final int wordPos = getTerminalPosition(word); + if (wordPos != FormatSpec.NOT_VALID_WORD) { + mDictBuffer.position(wordPos); + final int flags = mDictBuffer.readUnsignedByte(); + mDictBuffer.position(wordPos); + mDictBuffer.put((byte) DynamicBinaryDictIOUtils.markAsDeleted(flags)); + } + close(); + } + + @Override @UsedForTesting + public void insertWord(final String word, final int frequency, + final ArrayList<WeightedString> bigramStrings, + final ArrayList<WeightedString> shortcuts, + final boolean isNotAWord, final boolean isBlackListEntry) + throws IOException, UnsupportedFormatException { + if (mOutStream == null) openStreamAndBuffer(); + DynamicBinaryDictIOUtils.insertWord(this, mOutStream, word, frequency, bigramStrings, + shortcuts, isNotAWord, isBlackListEntry); + close(); + } +} diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index fa19e2677..5089687da 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -42,7 +42,7 @@ public class Ver4DictDecoder extends DictDecoder { private static final int FILETYPE_TRIE = 1; private static final int FILETYPE_FREQUENCY = 2; private static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3; - private static final int FILETYPE_BIGRAM = 4; + private static final int FILETYPE_BIGRAM_FREQ = 4; private final File mDictDirectory; private final DictionaryBufferFactory mBufferFactory; @@ -85,9 +85,10 @@ public class Ver4DictDecoder extends DictDecoder { } else if (fileType == FILETYPE_TERMINAL_ADDRESS_TABLE) { return new File(mDictDirectory, mDictDirectory.getName() + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); - } else if (fileType == FILETYPE_BIGRAM) { + } else if (fileType == FILETYPE_BIGRAM_FREQ) { return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.BIGRAM_FILE_EXTENSION); + mDictDirectory.getName() + FormatSpec.BIGRAM_FILE_EXTENSION + + FormatSpec.BIGRAM_FREQ_CONTENT_ID); } else { throw new RuntimeException("Unsupported kind of file : " + fileType); } @@ -95,12 +96,11 @@ public class Ver4DictDecoder extends DictDecoder { @Override public void openDictBuffer() throws FileNotFoundException, IOException { - final String filename = mDictDirectory.getName(); mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE)); mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY)); mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer( getFile(FILETYPE_TERMINAL_ADDRESS_TABLE)); - mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM)); + mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM_FREQ)); loadBigramAddressSparseTable(); } @@ -127,11 +127,12 @@ public class Ver4DictDecoder extends DictDecoder { } private void loadBigramAddressSparseTable() throws IOException { - final File lookupIndexFile = new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.BIGRAM_LOOKUP_TABLE_FILE_EXTENSION); - final File contentFile = new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.BIGRAM_ADDRESS_TABLE_FILE_EXTENSION); - mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, contentFile, + final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName() + + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); + final File freqsFile = new File(mDictDirectory, mDictDirectory.getName() + + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + + FormatSpec.BIGRAM_FREQ_CONTENT_ID); + mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, new File[] { freqsFile }, FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE); } @@ -208,7 +209,7 @@ public class Ver4DictDecoder extends DictDecoder { final ArrayList<PendingAttribute> bigrams; if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { bigrams = new ArrayList<PendingAttribute>(); - final int posOfBigrams = mBigramAddressTable.get(terminalId); + final int posOfBigrams = mBigramAddressTable.get(0 /* contentTableIndex */, terminalId); mBigramBuffer.position(posOfBigrams); while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE, @@ -224,7 +225,8 @@ public class Ver4DictDecoder extends DictDecoder { if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; } if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - MakedictLog.d("too many bigrams in a node."); + throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size() + + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); } } else { bigrams = null; diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index 4c25faf88..b38c33019 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -26,7 +26,6 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -44,19 +43,115 @@ public class Ver4DictEncoder implements DictEncoder { private byte[] mTrieBuf; private int mTriePos; private int mHeaderSize; - private SparseTable mBigramAddressTable; private OutputStream mTrieOutStream; private OutputStream mFreqOutStream; private OutputStream mTerminalAddressTableOutStream; - private OutputStream mBigramOutStream; private File mDictDir; private String mBaseFilename; + private BigramContentWriter mBigramWriter; @UsedForTesting public Ver4DictEncoder(final File dictPlacedDir) { mDictPlacedDir = dictPlacedDir; } + private interface SparseTableContentWriterInterface { + public void write(final OutputStream outStream) throws IOException; + } + + private static class SparseTableContentWriter { + private final int mContentCount; + private final SparseTable mSparseTable; + private final File mLookupTableFile; + protected final File mBaseDir; + private final File[] mAddressTableFiles; + private final File[] mContentFiles; + protected final OutputStream[] mContentOutStreams; + + public SparseTableContentWriter(final String name, final int contentCount, + final int initialCapacity, final int blockSize, final File baseDir, + final String[] contentFilenames, final String[] contentIds) { + if (contentFilenames.length != contentIds.length) { + throw new RuntimeException("The length of contentFilenames and the length of" + + " contentIds are different " + contentFilenames.length + ", " + + contentIds.length); + } + mContentCount = contentCount; + mSparseTable = new SparseTable(initialCapacity, blockSize, contentCount); + mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); + mAddressTableFiles = new File[mContentCount]; + mContentFiles = new File[mContentCount]; + mBaseDir = baseDir; + for (int i = 0; i < mContentCount; ++i) { + mAddressTableFiles[i] = new File(mBaseDir, + name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]); + mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]); + } + mContentOutStreams = new OutputStream[mContentCount]; + } + + public void openStreams() throws FileNotFoundException { + for (int i = 0; i < mContentCount; ++i) { + mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]); + } + } + + protected void write(final int contentIndex, final int index, + final SparseTableContentWriterInterface writer) throws IOException { + mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length()); + writer.write(mContentOutStreams[contentIndex]); + mContentOutStreams[contentIndex].flush(); + } + + public void closeStreams() throws IOException { + mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles); + for (int i = 0; i < mContentCount; ++i) { + mContentOutStreams[i].close(); + } + } + } + + private static class BigramContentWriter extends SparseTableContentWriter { + + public BigramContentWriter(final String name, final int initialCapacity, + final File baseDir) { + super(name + FormatSpec.BIGRAM_FILE_EXTENSION, FormatSpec.BIGRAM_CONTENT_COUNT, + initialCapacity, FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir, + new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION }, + new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID }); + } + + public void writeBigramsForOneWord(final int terminalId, + final Iterator<WeightedString> bigramIterator, final FusionDictionary dict) + throws IOException { + write(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, + new SparseTableContentWriterInterface() { + @Override + public void write(final OutputStream outStream) throws IOException { + writeBigramsForOneWordInternal(outStream, bigramIterator, dict); + } + }); + } + + private void writeBigramsForOneWordInternal(final OutputStream outStream, + final Iterator<WeightedString> bigramIterator, final FusionDictionary dict) + throws IOException { + while (bigramIterator.hasNext()) { + final WeightedString bigram = bigramIterator.next(); + final PtNode target = + FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); + final int unigramFrequencyForThisWord = target.mFrequency; + final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags( + bigramIterator.hasNext(), 0, bigram.mFrequency, + unigramFrequencyForThisWord, bigram.mWord); + BinaryDictEncoderUtils.writeUIntToStream(outStream, bigramFlags, + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); + BinaryDictEncoderUtils.writeUIntToStream(outStream, target.mTerminalId, + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE); + } + } + } + private void openStreams(final FormatOptions formatOptions, final DictionaryOptions dictOptions) throws FileNotFoundException, IOException { final FileHeader header = new FileHeader(0, dictOptions, formatOptions); @@ -66,8 +161,6 @@ public class Ver4DictEncoder implements DictEncoder { final File freqFile = new File(mDictDir, mBaseFilename + FormatSpec.FREQ_FILE_EXTENSION); final File terminalAddressTableFile = new File(mDictDir, mBaseFilename + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); - final File bigramFile = new File(mDictDir, - mBaseFilename + FormatSpec.BIGRAM_FILE_EXTENSION); if (!mDictDir.isDirectory()) { if (mDictDir.exists()) mDictDir.delete(); mDictDir.mkdirs(); @@ -78,7 +171,6 @@ public class Ver4DictEncoder implements DictEncoder { mTrieOutStream = new FileOutputStream(trieFile); mFreqOutStream = new FileOutputStream(freqFile); mTerminalAddressTableOutStream = new FileOutputStream(terminalAddressTableFile); - mBigramOutStream = new FileOutputStream(bigramFile); } private void close() throws IOException { @@ -92,14 +184,10 @@ public class Ver4DictEncoder implements DictEncoder { if (mTerminalAddressTableOutStream != null) { mTerminalAddressTableOutStream.close(); } - if (mBigramOutStream != null) { - mBigramOutStream.close(); - } } finally { mTrieOutStream = null; mFreqOutStream = null; mTerminalAddressTableOutStream = null; - mBigramOutStream = null; } } @@ -135,10 +223,8 @@ public class Ver4DictEncoder implements DictEncoder { if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes); writeTerminalData(flatNodes, terminalCount); - mBigramAddressTable = new SparseTable(terminalCount, - FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE); + mBigramWriter = new BigramContentWriter(mBaseFilename, terminalCount, mDictDir); writeBigrams(flatNodes, dict); - writeBigramAddressSparseTable(); final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1); final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize; @@ -181,12 +267,10 @@ public class Ver4DictEncoder implements DictEncoder { countSize); } - private void writePtNodeFlags(final PtNode ptNode, final int parentAddress, - final FormatOptions formatOptions) { + private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) { final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos, - BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mTriePos, childrenPos, - formatOptions), + BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions), FormatSpec.PTNODE_FLAGS_SIZE); } @@ -231,8 +315,7 @@ public class Ver4DictEncoder implements DictEncoder { while (shortcutIterator.hasNext()) { final WeightedString target = shortcutIterator.next(); final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags( - shortcutIterator.hasNext(), - target.mFrequency); + shortcutIterator.hasNext(), target.mFrequency); mTrieBuf[mTriePos++] = (byte)shortcutFlags; final int shortcutShift = CharEncoding.writeString(mTrieBuf, mTriePos, target.mWord); @@ -248,39 +331,16 @@ public class Ver4DictEncoder implements DictEncoder { private void writeBigrams(final ArrayList<PtNodeArray> flatNodes, final FusionDictionary dict) throws IOException { - final ByteArrayOutputStream bigramBuffer = new ByteArrayOutputStream(); - + mBigramWriter.openStreams(); for (final PtNodeArray nodeArray : flatNodes) { for (final PtNode ptNode : nodeArray.mData) { if (ptNode.mBigrams != null) { - final int startPos = bigramBuffer.size(); - mBigramAddressTable.set(ptNode.mTerminalId, startPos); - final Iterator<WeightedString> bigramIterator = ptNode.mBigrams.iterator(); - while (bigramIterator.hasNext()) { - final WeightedString bigram = bigramIterator.next(); - final PtNode target = - FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); - final int unigramFrequencyForThisWord = target.mFrequency; - final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags( - bigramIterator.hasNext(), 0, bigram.mFrequency, - unigramFrequencyForThisWord, bigram.mWord); - BinaryDictEncoderUtils.writeUIntToStream(bigramBuffer, bigramFlags, - FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); - BinaryDictEncoderUtils.writeUIntToStream(bigramBuffer, target.mTerminalId, - FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE); - } + mBigramWriter.writeBigramsForOneWord(ptNode.mTerminalId, + ptNode.mBigrams.iterator(), dict); } } } - bigramBuffer.writeTo(mBigramOutStream); - } - - private void writeBigramAddressSparseTable() throws IOException { - final File lookupIndexFile = - new File(mDictDir, mBaseFilename + FormatSpec.BIGRAM_LOOKUP_TABLE_FILE_EXTENSION); - final File contentFile = - new File(mDictDir, mBaseFilename + FormatSpec.BIGRAM_ADDRESS_TABLE_FILE_EXTENSION); - mBigramAddressTable.writeToFiles(lookupIndexFile, contentFile); + mBigramWriter.closeStreams(); } @Override @@ -292,7 +352,7 @@ public class Ver4DictEncoder implements DictEncoder { @Override public void writePtNode(final PtNode ptNode, final int parentPosition, final FormatOptions formatOptions, final FusionDictionary dict) { - writePtNodeFlags(ptNode, parentPosition, formatOptions); + writePtNodeFlags(ptNode, formatOptions); writeParentPosition(parentPosition, ptNode, formatOptions); writeCharacters(ptNode.mChars, ptNode.hasSeveralChars()); if (ptNode.isTerminal()) { diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 7cf4f0c88..c8b62b6c8 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -230,10 +230,15 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB mSessions.remove(session); } + @UsedForTesting public void clearAndFlushDictionary() { // Clear the node structure on memory clear(); // Then flush the cleared state of the dictionary on disk. asyncFlashAllBinaryDictionary(); } + + /* package */ void decayIfNeeded() { + runGCIfRequired(false /* mindsBlockByGC */); + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java new file mode 100644 index 000000000..e9ca662e7 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.personalization; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import java.util.concurrent.TimeUnit; + +/** + * Broadcast receiver for periodically updating decaying dictionaries. + */ +public class DictionaryDecayBroadcastReciever extends BroadcastReceiver { + /** + * The root domain for the personalization. + */ + private static final String PERSONALIZATION_DOMAIN = + "com.android.inputmethod.latin.personalization"; + + /** + * The action of the intent to tell the time to decay dictionaries. + */ + private static final String DICTIONARY_DECAY_INTENT_ACTION = + PERSONALIZATION_DOMAIN + ".DICT_DECAY"; + + /** + * Interval to update for decaying dictionaries. + */ + private static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60); + + public static void setUpIntervalAlarmForDictionaryDecaying(Context context) { + AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + final Intent updateIntent = new Intent(DICTIONARY_DECAY_INTENT_ACTION); + updateIntent.setClass(context, DictionaryDecayBroadcastReciever.class); + final long alarmTime = System.currentTimeMillis() + DICTIONARY_DECAY_INTERVAL; + final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0 /* requestCode */, + updateIntent, PendingIntent.FLAG_CANCEL_CURRENT); + if (null != alarmManager) alarmManager.setInexactRepeating(AlarmManager.RTC, + alarmTime, DICTIONARY_DECAY_INTERVAL, pendingIntent); + } + + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); + if (action.equals(DICTIONARY_DECAY_INTENT_ACTION)) { + PersonalizationHelper.tryDecayingAllOpeningUserHistoryDictionary(); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index 8c9484b12..221ddeeba 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -29,7 +29,6 @@ import java.util.concurrent.ConcurrentHashMap; public class PersonalizationHelper { private static final String TAG = PersonalizationHelper.class.getSimpleName(); private static final boolean DEBUG = false; - private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap(); @@ -62,6 +61,18 @@ public class PersonalizationHelper { } } + public static void tryDecayingAllOpeningUserHistoryDictionary() { + for (final ConcurrentHashMap.Entry<String, SoftReference<UserHistoryDictionary>> entry + : sLangUserHistoryDictCache.entrySet()) { + if (entry.getValue() != null) { + final UserHistoryDictionary dict = entry.getValue().get(); + if (dict != null) { + dict.decayIfNeeded(); + } + } + } + } + public static void registerPersonalizationDictionaryUpdateSession(final Context context, final PersonalizationDictionaryUpdateSession session, String locale) { final PersonalizationPredictionDictionary predictionDictionary = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java index b499c26b6..ef6ab2a38 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java @@ -38,4 +38,10 @@ public final class DebugSettingsActivity extends PreferenceActivity { super.onCreate(savedInstanceState); setTitle(R.string.english_ime_debug_settings); } + + // TODO: Uncomment the override annotation once we start using SDK version 19. + // @Override + public boolean isValidFragment(String fragmentName) { + return fragmentName.equals(DEFAULT_FRAGMENT); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java index 6c3818651..ad68f8c37 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java @@ -32,4 +32,10 @@ public final class SettingsActivity extends PreferenceActivity { intent.putExtra(EXTRA_NO_HEADERS, true); return intent; } + + // TODO: Uncomment the override annotation once we start using SDK version 19. + // @Override + public boolean isValidFragment(String fragmentName) { + return fragmentName.equals(DEFAULT_FRAGMENT); + } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java index 119ca4755..aba563746 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java @@ -24,6 +24,8 @@ import android.preference.PreferenceActivity; * Spell checker preference screen. */ public final class SpellCheckerSettingsActivity extends PreferenceActivity { + private static final String DEFAULT_FRAGMENT = SpellCheckerSettingsFragment.class.getName(); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -32,8 +34,14 @@ public final class SpellCheckerSettingsActivity extends PreferenceActivity { @Override public Intent getIntent() { final Intent modIntent = new Intent(super.getIntent()); - modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SpellCheckerSettingsFragment.class.getName()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT); modIntent.putExtra(EXTRA_NO_HEADERS, true); return modIntent; } + + // TODO: Uncomment the override annotation once we start using SDK version 19. + // @Override + public boolean isValidFragment(String fragmentName) { + return fragmentName.equals(DEFAULT_FRAGMENT); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 44b201642..ff332cdee 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -61,10 +61,8 @@ public final class AdditionalSubtypeUtils { StringUtils.appendToCommaSplittableTextIfNotExists( IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue); final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); - return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, - localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue - + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE - + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false); + return buildInputMethodSubtype( + nameId, localeString, layoutExtraValue, additionalSubtypeExtraValue); } public static String getPrefSubtype(final InputMethodSubtype subtype) { @@ -137,4 +135,27 @@ public final class AdditionalSubtypeUtils { } return sb.toString(); } + + private static InputMethodSubtype buildInputMethodSubtype(int nameId, String localeString, + String layoutExtraValue, String additionalSubtypeExtraValue) { + // CAVEAT! If you want to change subtypeId after changing the extra values, + // you must change "getInputMethodSubtypeId". But it will remove the additional keyboard + // from the current users. So, you should be really careful to change it. + final int subtypeId = getInputMethodSubtypeId(nameId, localeString, layoutExtraValue, + additionalSubtypeExtraValue); + // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19. + return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, + localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false, + subtypeId); + } + + private static int getInputMethodSubtypeId(int nameId, String localeString, + String layoutExtraValue, String additionalSubtypeExtraValue) { + // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19. + return (new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, + localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue, + false, false)).hashCode(); + } } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 36afea54b..ca6a77997 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -85,8 +85,8 @@ LATIN_IME_CORE_SRC_FILES := \ $(addprefix suggest/policyimpl/dictionary/utils/, \ buffer_with_extendable_buffer.cpp \ byte_array_utils.cpp \ - decaying_utils.cpp \ dict_file_writing_utils.cpp \ + forgetting_curve_utils.cpp \ format_utils.cpp) \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ $(addprefix suggest/policyimpl/typing/, \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 38159b0f3..8f21c50ec 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -67,7 +67,6 @@ static jboolean latinime_BinaryDictionary_createEmptyDictFile(JNIEnv *env, jclas valueChars[valueUtf8Length] = '\0'; HeaderReadWriteUtils::AttributeMap::mapped_type value; HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value); - attributeMap[key] = value; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp index 67a085de3..8753c6eb0 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp @@ -20,7 +20,7 @@ #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" -#include "suggest/policyimpl/dictionary/utils/decaying_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" namespace latinime { @@ -43,7 +43,7 @@ void DynamicBigramListPolicy::getNextBigram(int *const outBigramPos, int *const } *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags); *outHasNext = BigramListReadWriteUtils::hasNext(bigramFlags); - if (mIsDecayingDict && !DecayingUtils::isValidBigram(*outProbability)) { + if (mIsDecayingDict && !ForgettingCurveUtils::isValidEncodedProbability(*outProbability)) { // This bigram is too weak to output. *outBigramPos = NOT_A_DICT_POS; } else { @@ -261,8 +261,8 @@ bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramTarg const int originalProbability = BigramListReadWriteUtils::getProbabilityFromFlags( bigramFlags); const int probabilityToWrite = mIsDecayingDict ? - DecayingUtils::getUpdatedBigramProbabilityDelta( - originalProbability, probability) : probability; + ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability, + probability) : probability; const BigramListReadWriteUtils::BigramFlags updatedFlags = BigramListReadWriteUtils::setProbabilityInFlags(bigramFlags, probabilityToWrite); @@ -294,7 +294,7 @@ bool DynamicBigramListPolicy::writeNewBigramEntry(const int bigramTargetPos, con int *const writingPos) { // hasNext is false because we are adding a new bigram entry at the end of the bigram list. const int probabilityToWrite = mIsDecayingDict ? - DecayingUtils::getUpdatedBigramProbabilityDelta(NOT_A_PROBABILITY, probability) : + ForgettingCurveUtils::getUpdatedEncodedProbability(NOT_A_PROBABILITY, probability) : probability; return BigramListReadWriteUtils::createAndWriteBigramEntry(mBuffer, bigramTargetPos, probabilityToWrite, false /* hasNext */, writingPos); @@ -365,9 +365,9 @@ bool DynamicBigramListPolicy::updateProbabilityForDecay( *outRemoved = false; if (mIsDecayingDict) { // Update bigram probability for decaying. - const int newProbability = DecayingUtils::getBigramProbabilityDeltaToSave( + const int newProbability = ForgettingCurveUtils::getEncodedProbabilityToSave( BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags)); - if (DecayingUtils::isValidBigram(newProbability)) { + if (ForgettingCurveUtils::isValidEncodedProbability(newProbability)) { // Write new probability. const BigramListReadWriteUtils::BigramFlags updatedBigramFlags = BigramListReadWriteUtils::setProbabilityInFlags( diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp index 081163a4d..324b53062 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp @@ -16,7 +16,7 @@ #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h" -#include "suggest/policyimpl/dictionary/utils/decaying_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" namespace latinime { @@ -29,14 +29,14 @@ bool DynamicPatriciaTrieGcEventListeners bool isUselessPtNode = !node->isTerminal(); if (node->isTerminal() && mIsDecayingDict) { const int newProbability = - DecayingUtils::getUnigramProbabilityToSave(node->getProbability()); + ForgettingCurveUtils::getEncodedProbabilityToSave(node->getProbability()); int writingPos = node->getProbabilityFieldPos(); // Update probability. if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition( mBuffer, newProbability, &writingPos)) { return false; } - if (!DecayingUtils::isValidUnigram(newProbability)) { + if (!ForgettingCurveUtils::isValidEncodedProbability(newProbability)) { isUselessPtNode = false; } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp index a5a42ead1..60d0db0c0 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp @@ -28,7 +28,7 @@ #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" -#include "suggest/policyimpl/dictionary/utils/decaying_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" namespace latinime { @@ -154,7 +154,7 @@ int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const in int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability, const int bigramProbability) const { if (mHeaderPolicy.isDecayingDict()) { - return DecayingUtils::getProbability(unigramProbability, bigramProbability); + return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability); } else { if (unigramProbability == NOT_A_PROBABILITY) { return NOT_A_PROBABILITY; @@ -344,10 +344,10 @@ bool DynamicPatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { // Needs to reduce dictionary size. return true; } else if (mHeaderPolicy.isDecayingDict()) { - if (mUnigramCount >= DecayingUtils::MAX_UNIGRAM_COUNT) { + if (mUnigramCount >= ForgettingCurveUtils::MAX_UNIGRAM_COUNT) { // Unigram count exceeds the limit. return true; - } else if (mBigramCount >= DecayingUtils::MAX_BIGRAM_COUNT) { + } else if (mBigramCount >= ForgettingCurveUtils::MAX_BIGRAM_COUNT) { // Bigram count exceeds the limit. return true; } else if (mindsBlockByGC && needsToDecay()) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp index db6d48fdb..70a9ee564 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp @@ -25,8 +25,8 @@ #include "suggest/policyimpl/dictionary/header/header_policy.h" #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h" -#include "suggest/policyimpl/dictionary/utils/decaying_utils.h" #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" #include "utils/hash_map_compat.h" namespace latinime { @@ -494,7 +494,7 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, return false; } if (mNeedsToDecay && traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted - .getValidUnigramCount() > DecayingUtils::MAX_UNIGRAM_COUNT_AFTER_GC) { + .getValidUnigramCount() > ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC) { // TODO: Remove more unigrams. } @@ -507,7 +507,7 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, } if (mNeedsToDecay && traversePolicyToUpdateBigramProbability.getValidBigramEntryCount() - > DecayingUtils::MAX_BIGRAM_COUNT_AFTER_GC) { + > ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC) { // TODO: Remove more bigrams. } @@ -545,7 +545,8 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, int DynamicPatriciaTrieWritingHelper::getUpdatedProbability(const int originalProbability, const int newProbability) { if (mNeedsToDecay) { - return DecayingUtils::getUpdatedUnigramProbability(originalProbability, newProbability); + return ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability, + newProbability); } else { return newProbability; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp index 2694ce8d5..5ded8f6a1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp @@ -139,6 +139,9 @@ const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY int *const writingPos) { for (AttributeMap::const_iterator it = headerAttributes->begin(); it != headerAttributes->end(); ++it) { + if (it->first.empty() || it->second.empty()) { + continue; + } // Write a key. if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(), true /* writesTerminator */, writingPos)) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp deleted file mode 100644 index 942a74238..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "suggest/policyimpl/dictionary/utils/decaying_utils.h" - -#include "suggest/policyimpl/dictionary/utils/probability_utils.h" - -namespace latinime { - -const int DecayingUtils::MAX_UNIGRAM_COUNT = 12000; -const int DecayingUtils::MAX_UNIGRAM_COUNT_AFTER_GC = 10000; -const int DecayingUtils::MAX_BIGRAM_COUNT = 12000; -const int DecayingUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000; - -const int DecayingUtils::MAX_COMPUTED_PROBABILITY = 127; -const int DecayingUtils::MAX_UNIGRAM_PROBABILITY = 120; -const int DecayingUtils::MIN_VALID_UNIGRAM_PROBABILITY = 24; -const int DecayingUtils::UNIGRAM_PROBABILITY_STEP = 8; -const int DecayingUtils::MAX_BIGRAM_PROBABILITY_DELTA = 15; -const int DecayingUtils::MIN_VALID_BIGRAM_PROBABILITY_DELTA = 3; -const int DecayingUtils::BIGRAM_PROBABILITY_DELTA_STEP = 1; - -/* static */ int DecayingUtils::getProbability(const int encodedUnigramProbability, - const int encodedBigramProbabilityDelta) { - if (encodedUnigramProbability == NOT_A_PROBABILITY) { - return NOT_A_PROBABILITY; - } else if (encodedBigramProbabilityDelta == NOT_A_PROBABILITY) { - const int rawProbability = ProbabilityUtils::backoff(decodeUnigramProbability( - encodedUnigramProbability)); - return min(getDecayedProbability(rawProbability), MAX_COMPUTED_PROBABILITY); - } else { - const int rawProbability = ProbabilityUtils::computeProbabilityForBigram( - decodeUnigramProbability(encodedUnigramProbability), - decodeBigramProbabilityDelta(encodedBigramProbabilityDelta)); - return min(getDecayedProbability(rawProbability), MAX_COMPUTED_PROBABILITY); - } -} - -/* static */ int DecayingUtils::getUpdatedUnigramProbability(const int originalEncodedProbability, - const int newProbability) { - if (originalEncodedProbability == NOT_A_PROBABILITY) { - // The unigram is not in this dictionary. - if (newProbability == NOT_A_PROBABILITY) { - // The unigram is not in other dictionaries. - return 0; - } else { - return MIN_VALID_UNIGRAM_PROBABILITY; - } - } else { - if (newProbability != NOT_A_PROBABILITY - && originalEncodedProbability < MIN_VALID_UNIGRAM_PROBABILITY) { - return MIN_VALID_UNIGRAM_PROBABILITY; - } - return min(originalEncodedProbability + UNIGRAM_PROBABILITY_STEP, MAX_UNIGRAM_PROBABILITY); - } -} - -/* static */ int DecayingUtils::getUnigramProbabilityToSave(const int encodedProbability) { - return max(encodedProbability - UNIGRAM_PROBABILITY_STEP, 0); -} - -/* static */ int DecayingUtils::getBigramProbabilityDeltaToSave(const int encodedProbabilityDelta) { - return max(encodedProbabilityDelta - BIGRAM_PROBABILITY_DELTA_STEP, 0); -} - -/* static */ int DecayingUtils::getUpdatedBigramProbabilityDelta( - const int originalEncodedProbabilityDelta, const int newProbability) { - if (originalEncodedProbabilityDelta == NOT_A_PROBABILITY) { - // The bigram relation is not in this dictionary. - if (newProbability == NOT_A_PROBABILITY) { - // The bigram target is not in other dictionaries. - return 0; - } else { - return MIN_VALID_BIGRAM_PROBABILITY_DELTA; - } - } else { - if (newProbability != NOT_A_PROBABILITY - && originalEncodedProbabilityDelta < MIN_VALID_BIGRAM_PROBABILITY_DELTA) { - return MIN_VALID_BIGRAM_PROBABILITY_DELTA; - } - return min(originalEncodedProbabilityDelta + BIGRAM_PROBABILITY_DELTA_STEP, - MAX_BIGRAM_PROBABILITY_DELTA); - } -} - -/* static */ int DecayingUtils::isValidUnigram(const int encodedUnigramProbability) { - return encodedUnigramProbability >= MIN_VALID_UNIGRAM_PROBABILITY; -} - -/* static */ int DecayingUtils::isValidBigram(const int encodedBigramProbabilityDelta) { - return encodedBigramProbabilityDelta >= MIN_VALID_BIGRAM_PROBABILITY_DELTA; -} - -/* static */ int DecayingUtils::decodeUnigramProbability(const int encodedProbability) { - const int probability = encodedProbability - MIN_VALID_UNIGRAM_PROBABILITY; - if (probability < 0) { - return NOT_A_PROBABILITY; - } else { - return min(probability, MAX_UNIGRAM_PROBABILITY); - } -} - -/* static */ int DecayingUtils::decodeBigramProbabilityDelta(const int encodedProbabilityDelta) { - const int probabilityDelta = encodedProbabilityDelta - MIN_VALID_BIGRAM_PROBABILITY_DELTA; - if (probabilityDelta < 0) { - return NOT_A_PROBABILITY; - } else { - return min(probabilityDelta, MAX_BIGRAM_PROBABILITY_DELTA); - } -} - -/* static */ int DecayingUtils::getDecayedProbability(const int rawProbability) { - return rawProbability; -} - -} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp new file mode 100644 index 000000000..b502fe25d --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cmath> +#include <stdlib.h> + +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" + +namespace latinime { + +const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT = 12000; +const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC = 10000; +const int ForgettingCurveUtils::MAX_BIGRAM_COUNT = 12000; +const int ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000; + +const int ForgettingCurveUtils::MAX_COMPUTED_PROBABILITY = 127; +const int ForgettingCurveUtils::MAX_ENCODED_PROBABILITY = 15; +const int ForgettingCurveUtils::MIN_VALID_ENCODED_PROBABILITY = 3; +const int ForgettingCurveUtils::ENCODED_PROBABILITY_STEP = 1; +// Currently, we try to decay each uni/bigram once every 2 hours. Accordingly, the expected +// duration of the decay is approximately 66hours. +const float ForgettingCurveUtils::MIN_PROBABILITY_TO_DECAY = 0.03f; + +const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable; + +/* static */ int ForgettingCurveUtils::getProbability(const int encodedUnigramProbability, + const int encodedBigramProbability) { + if (encodedUnigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (encodedBigramProbability == NOT_A_PROBABILITY) { + return backoff(decodeProbability(encodedUnigramProbability)); + } else { + const int unigramProbability = decodeProbability(encodedUnigramProbability); + const int bigramProbability = decodeProbability(encodedBigramProbability); + return min(max(unigramProbability, bigramProbability), MAX_COMPUTED_PROBABILITY); + } +} + +// Caveat: Unlike getProbability(), this method doesn't assume special bigram probability encoding +// (i.e. unigram probability + bigram probability delta). +/* static */ int ForgettingCurveUtils::getUpdatedEncodedProbability( + const int originalEncodedProbability, const int newProbability) { + if (originalEncodedProbability == NOT_A_PROBABILITY) { + // The bigram relation is not in this dictionary. + if (newProbability == NOT_A_PROBABILITY) { + // The bigram target is not in other dictionaries. + return 0; + } else { + return MIN_VALID_ENCODED_PROBABILITY; + } + } else { + if (newProbability != NOT_A_PROBABILITY + && originalEncodedProbability < MIN_VALID_ENCODED_PROBABILITY) { + return MIN_VALID_ENCODED_PROBABILITY; + } + return min(originalEncodedProbability + ENCODED_PROBABILITY_STEP, MAX_ENCODED_PROBABILITY); + } +} + +/* static */ int ForgettingCurveUtils::isValidEncodedProbability(const int encodedProbability) { + return encodedProbability >= MIN_VALID_ENCODED_PROBABILITY; +} + +/* static */ int ForgettingCurveUtils::getEncodedProbabilityToSave(const int encodedProbability) { + const int currentEncodedProbability = max(min(encodedProbability, MAX_ENCODED_PROBABILITY), 0); + // TODO: Implement the decay in more proper way. + const float currentRate = static_cast<float>(currentEncodedProbability) + / static_cast<float>(MAX_ENCODED_PROBABILITY); + const float thresholdToDecay = MIN_PROBABILITY_TO_DECAY + + (1.0f - MIN_PROBABILITY_TO_DECAY) * (1.0f - currentRate); + const float randValue = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); + if (thresholdToDecay < randValue) { + return max(currentEncodedProbability - ENCODED_PROBABILITY_STEP, 0); + } else { + return currentEncodedProbability; + } +} + +/* static */ int ForgettingCurveUtils::decodeProbability(const int encodedProbability) { + if (encodedProbability < MIN_VALID_ENCODED_PROBABILITY) { + return NOT_A_PROBABILITY; + } else { + return min(sProbabilityTable.getProbability(encodedProbability), MAX_ENCODED_PROBABILITY); + } +} + +// See comments in ProbabilityUtils::backoff(). +/* static */ int ForgettingCurveUtils::backoff(const int unigramProbability) { + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else { + return max(unigramProbability - 8, 0); + } +} + +ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTable() { + // Table entry is as follows: + // 1, 1, 1, 2, 3, 5, 6, 9, 13, 18, 25, 34, 48, 66, 91, 127. + // Note that first MIN_VALID_ENCODED_PROBABILITY values are not used. + mTable.resize(MAX_ENCODED_PROBABILITY + 1); + for (int i = 0; i <= MAX_ENCODED_PROBABILITY; ++i) { + const int probability = static_cast<int>(powf(static_cast<float>(MAX_COMPUTED_PROBABILITY), + static_cast<float>(i) / static_cast<float>(MAX_ENCODED_PROBABILITY))); + mTable[i] = min(MAX_COMPUTED_PROBABILITY, max(0, probability)); + } +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h index 1ca03918f..d666f22aa 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h @@ -14,8 +14,10 @@ * limitations under the License. */ -#ifndef LATINIME_DECAYING_UTILS_H -#define LATINIME_DECAYING_UTILS_H +#ifndef LATINIME_FORGETTING_CURVE_UTILS_H +#define LATINIME_FORGETTING_CURVE_UTILS_H + +#include <vector> #include "defines.h" @@ -24,8 +26,7 @@ namespace latinime { // TODO: Check the elapsed time and decrease the probability depending on the time. Time field is // required to introduced to each terminal PtNode and bigram entry. // TODO: Quit using bigram probability to indicate the delta. -// TODO: Quit using bigram probability delta. -class DecayingUtils { +class ForgettingCurveUtils { public: static const int MAX_UNIGRAM_COUNT; static const int MAX_UNIGRAM_COUNT_AFTER_GC; @@ -33,38 +34,46 @@ class DecayingUtils { static const int MAX_BIGRAM_COUNT_AFTER_GC; static int getProbability(const int encodedUnigramProbability, - const int encodedBigramProbabilityDelta); + const int encodedBigramProbability); - static int getUpdatedUnigramProbability(const int originalEncodedProbability, + static int getUpdatedEncodedProbability(const int originalEncodedProbability, const int newProbability); - static int getUpdatedBigramProbabilityDelta(const int originalEncodedProbabilityDelta, - const int newProbability); + static int isValidEncodedProbability(const int encodedProbability); - static int isValidUnigram(const int encodedUnigramProbability); + static int getEncodedProbabilityToSave(const int encodedProbability); - static int isValidBigram(const int encodedProbabilityDelta); + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ForgettingCurveUtils); - static int getUnigramProbabilityToSave(const int encodedProbability); + class ProbabilityTable { + public: + ProbabilityTable(); - static int getBigramProbabilityDeltaToSave(const int encodedProbabilityDelta); + int getProbability(const int encodedProbability) const { + if (encodedProbability < 0 || encodedProbability > static_cast<int>(mTable.size())) { + return NOT_A_PROBABILITY; + } + return mTable[encodedProbability]; + } - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DecayingUtils); + private: + DISALLOW_COPY_AND_ASSIGN(ProbabilityTable); + + std::vector<int> mTable; + }; static const int MAX_COMPUTED_PROBABILITY; - static const int MAX_UNIGRAM_PROBABILITY; - static const int MIN_VALID_UNIGRAM_PROBABILITY; - static const int UNIGRAM_PROBABILITY_STEP; - static const int MAX_BIGRAM_PROBABILITY_DELTA; - static const int MIN_VALID_BIGRAM_PROBABILITY_DELTA; - static const int BIGRAM_PROBABILITY_DELTA_STEP; + static const int MAX_ENCODED_PROBABILITY; + static const int MIN_VALID_ENCODED_PROBABILITY; + static const int ENCODED_PROBABILITY_STEP; + static const float MIN_PROBABILITY_TO_DECAY; - static int decodeUnigramProbability(const int encodedProbability); + static const ProbabilityTable sProbabilityTable; - static int decodeBigramProbabilityDelta(const int encodedProbability); + static int decodeProbability(const int encodedProbability); - static int getDecayedProbability(const int rawProbability); + static int backoff(const int unigramProbability); }; } // namespace latinime -#endif /* LATINIME_DECAYING_UTILS_H */ +#endif /* LATINIME_FORGETTING_CURVE_UTILS_H */ diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index 8a439fc22..b2d31c21f 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -50,14 +50,18 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { - binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY); - binaryDictionary.flushWithGC(); + // Entries having low probability would be suppressed once in 2 GCs. + final int count = 2; + for (int i = 0; i < count; i++) { + binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY); + binaryDictionary.flushWithGC(); + } } private void forcePassingLongTime(final BinaryDictionary binaryDictionary) { // Currently, probabilities are decayed when GC is run. All entries that have never been - // typed in 32 GCs are removed. - final int count = 32; + // typed in 128 GCs would be removed. + final int count = 128; for (int i = 0; i < count; i++) { binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY); binaryDictionary.flushWithGC(); diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index a4d94262f..aa1658301 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -104,7 +104,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } sStarBigrams.put(0, new ArrayList<Integer>()); - for (int i = 1; i < sWords.size(); ++i) { + // MAX - 1 because we added one above already + final int maxBigrams = Math.min(sWords.size(), FormatSpec.MAX_BIGRAMS_IN_A_PTNODE - 1); + for (int i = 1; i < maxBigrams; ++i) { sStarBigrams.get(0).add(i); } @@ -544,8 +546,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, - int index, boolean contained) { - final int expectedFrequency = (UNIGRAM_FREQ + index) % 255; + final boolean contained) { long diff = -1; int position = -1; try { @@ -603,7 +604,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { // Test a word that is contained within the dictionary. long sum = 0; for (int i = 0; i < sWords.size(); ++i) { - final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), i, true); + final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), true); sum += time == -1 ? 0 : time; } Log.d(TAG, "per search : " + (((double)sum) / sWords.size() / 1000000) + " : " + message @@ -616,11 +617,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { for (int i = 0; i < 1000; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); if (sWords.indexOf(word) != -1) continue; - checkGetTerminalPosition(dictDecoder, word, i, false); + checkGetTerminalPosition(dictDecoder, word, false); } } - private void runGetTerminalPositionTests(final ArrayList<String> results, final int bufferType, + private void runGetTerminalPositionTests(final int bufferType, final FormatOptions formatOptions) { runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram"); } @@ -628,17 +629,17 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testGetTerminalPosition() { final ArrayList<String> results = CollectionUtils.newArrayList(); - runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION2); - runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION2); + runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION2); - runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION2); + runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -656,27 +657,21 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); - final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, DictDecoder.USE_BYTEARRAY); - try { - dictDecoder.openDictBuffer(); - } catch (IOException e) { - // ignore - Log.e(TAG, "IOException while opening the buffer", e); - } - assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); + final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file, + DictDecoder.USE_WRITABLE_BYTEBUFFER); try { MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - dictDecoder.getTerminalPosition(sWords.get(0))); - DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(0)); + dictUpdater.getTerminalPosition(sWords.get(0))); + dictUpdater.deleteWord(sWords.get(0)); assertEquals(FormatSpec.NOT_VALID_WORD, - dictDecoder.getTerminalPosition(sWords.get(0))); + dictUpdater.getTerminalPosition(sWords.get(0))); MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - dictDecoder.getTerminalPosition(sWords.get(5))); - DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(5)); + dictUpdater.getTerminalPosition(sWords.get(5))); + dictUpdater.deleteWord(sWords.get(5)); assertEquals(FormatSpec.NOT_VALID_WORD, - dictDecoder.getTerminalPosition(sWords.get(5))); + dictUpdater.getTerminalPosition(sWords.get(5))); } catch (IOException e) { } catch (UnsupportedFormatException e) { } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java index a83749499..acd65856c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java @@ -27,9 +27,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import com.android.inputmethod.latin.utils.CollectionUtils; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -186,46 +184,31 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { private long insertAndCheckWord(final File file, final String word, final int frequency, final boolean exist, final ArrayList<WeightedString> bigrams, final ArrayList<WeightedString> shortcuts) { - BufferedOutputStream outStream = null; long amountOfTime = -1; try { - final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - dictDecoder.openDictBuffer(); - outStream = new BufferedOutputStream(new FileOutputStream(file, true)); if (!exist) { assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); } final long now = System.nanoTime(); - DynamicBinaryDictIOUtils.insertWord(dictDecoder, outStream, word, frequency, bigrams, - shortcuts, false, false); + dictUpdater.insertWord(word, frequency, bigrams, shortcuts, false, false); amountOfTime = System.nanoTime() - now; - outStream.flush(); MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); - outStream.close(); } catch (IOException e) { Log.e(TAG, "Raised an IOException while inserting a word", e); } catch (UnsupportedFormatException e) { Log.e(TAG, "Raised an UnsupportedFormatException error while inserting a word", e); - } finally { - if (outStream != null) { - try { - outStream.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close the output stream", e); - } - } } return amountOfTime; } private void deleteWord(final File file, final String word) { try { - final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - dictDecoder.openDictBuffer(); - DynamicBinaryDictIOUtils.deleteWord(dictDecoder, word); + dictUpdater.deleteWord(word); } catch (IOException e) { } catch (UnsupportedFormatException e) { } diff --git a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java index 132483d5e..aeb8552bd 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java @@ -21,10 +21,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Random; @@ -36,9 +34,6 @@ import java.util.Random; public class SparseTableTests extends AndroidTestCase { private static final String TAG = SparseTableTests.class.getSimpleName(); - private static final int[] SMALL_INDEX = { SparseTable.NOT_EXIST, 0 }; - private static final int[] BIG_INDEX = { SparseTable.NOT_EXIST, 1, 2, 3, 4, 5, 6, 7}; - private final Random mRandom; private final ArrayList<Integer> mRandomIndex; @@ -59,32 +54,21 @@ public class SparseTableTests extends AndroidTestCase { } } - public void testInitializeWithArray() { - final SparseTable table = new SparseTable(SMALL_INDEX, BIG_INDEX, BLOCK_SIZE); - for (int i = 0; i < 8; ++i) { - assertEquals(SparseTable.NOT_EXIST, table.get(i)); - } - assertEquals(SparseTable.NOT_EXIST, table.get(8)); - for (int i = 9; i < 16; ++i) { - assertEquals(i - 8, table.get(i)); - } - } - public void testSet() { - final SparseTable table = new SparseTable(16, BLOCK_SIZE); - table.set(3, 6); - table.set(8, 16); + final SparseTable table = new SparseTable(16, BLOCK_SIZE, 1); + table.set(0, 3, 6); + table.set(0, 8, 16); for (int i = 0; i < 16; ++i) { if (i == 3 || i == 8) { - assertEquals(i * 2, table.get(i)); + assertEquals(i * 2, table.get(0, i)); } else { - assertEquals(SparseTable.NOT_EXIST, table.get(i)); + assertEquals(SparseTable.NOT_EXIST, table.get(0, i)); } } } private void generateRandomIndex(final int size, final int prop) { - for (int i = 0; i < DEFAULT_SIZE; ++i) { + for (int i = 0; i < size; ++i) { if (mRandom.nextInt(100) < prop) { mRandomIndex.set(i, mRandom.nextInt()); } else { @@ -94,11 +78,11 @@ public class SparseTableTests extends AndroidTestCase { } private void runTestRandomSet() { - final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE); + final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, 1); int elementCount = 0; for (int i = 0; i < DEFAULT_SIZE; ++i) { if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) { - table.set(i, mRandomIndex.get(i)); + table.set(0, i, mRandomIndex.get(i)); elementCount++; } } @@ -107,29 +91,24 @@ public class SparseTableTests extends AndroidTestCase { + table.getContentTableSize()); Log.d(TAG, "the table has " + elementCount + " elements"); for (int i = 0; i < DEFAULT_SIZE; ++i) { - assertEquals(table.get(i), (int)mRandomIndex.get(i)); + assertEquals(table.get(0, i), (int)mRandomIndex.get(i)); } // flush and reload OutputStream lookupOutStream = null; OutputStream contentOutStream = null; - InputStream lookupInStream = null; - InputStream contentInStream = null; try { final File lookupIndexFile = File.createTempFile("testRandomSet", ".small"); final File contentFile = File.createTempFile("testRandomSet", ".big"); lookupOutStream = new FileOutputStream(lookupIndexFile); contentOutStream = new FileOutputStream(contentFile); - table.write(lookupOutStream, contentOutStream); - lookupInStream = new FileInputStream(lookupIndexFile); - contentInStream = new FileInputStream(contentFile); - final byte[] lookupArray = new byte[(int) lookupIndexFile.length()]; - final byte[] contentArray = new byte[(int) contentFile.length()]; - lookupInStream.read(lookupArray); - contentInStream.read(contentArray); - final SparseTable newTable = new SparseTable(lookupArray, contentArray, BLOCK_SIZE); + table.write(lookupOutStream, new OutputStream[] { contentOutStream }); + lookupOutStream.flush(); + contentOutStream.flush(); + final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile, + new File[] { contentFile }, BLOCK_SIZE); for (int i = 0; i < DEFAULT_SIZE; ++i) { - assertEquals(table.get(i), newTable.get(i)); + assertEquals(table.get(0, i), newTable.get(0, i)); } } catch (IOException e) { Log.d(TAG, "IOException while flushing and realoding", e); @@ -157,4 +136,60 @@ public class SparseTableTests extends AndroidTestCase { runTestRandomSet(); } } + + public void testMultipleContents() { + final int numOfContents = 5; + generateRandomIndex(DEFAULT_SIZE, 20); + final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, numOfContents); + for (int i = 0; i < mRandomIndex.size(); ++i) { + if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) { + for (int j = 0; j < numOfContents; ++j) { + table.set(j, i, mRandomIndex.get(i)); + } + } + } + + OutputStream lookupOutStream = null; + OutputStream[] contentsOutStream = new OutputStream[numOfContents]; + try { + final File lookupIndexFile = File.createTempFile("testMultipleContents", "small"); + lookupOutStream = new FileOutputStream(lookupIndexFile); + final File[] contentFiles = new File[numOfContents]; + for (int i = 0; i < numOfContents; ++i) { + contentFiles[i] = File.createTempFile("testMultipleContents", "big" + i); + contentsOutStream[i] = new FileOutputStream(contentFiles[i]); + } + table.write(lookupOutStream, contentsOutStream); + lookupOutStream.flush(); + for (int i = 0; i < numOfContents; ++i) { + contentsOutStream[i].flush(); + } + final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile, contentFiles, + BLOCK_SIZE); + for (int i = 0; i < numOfContents; ++i) { + for (int j = 0; j < DEFAULT_SIZE; ++j) { + assertEquals(table.get(i, j), newTable.get(i, j)); + } + } + } catch (IOException e) { + Log.d(TAG, "IOException while flushing and reloading", e); + } finally { + if (lookupOutStream != null) { + try { + lookupOutStream.close(); + } catch (IOException e) { + Log.d(TAG, "IOException while closing the stream", e); + } + } + for (int i = 0; i < numOfContents; ++i) { + if (contentsOutStream[i] != null) { + try { + contentsOutStream[i].close(); + } catch (IOException e) { + Log.d(TAG, "IOException while closing the stream.", e); + } + } + } + } + } } diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index ddc9546c5..7c1decb71 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -109,35 +109,54 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { dict.close(); } + /** + * Clear all entries in the user history dictionary. + * @param testFilenameSuffix file name suffix used for testing. + */ + private void clearHistory(final String testFilenameSuffix) { + final UserHistoryDictionary dict = + PersonalizationHelper.getUserHistoryDictionary(getContext(), + testFilenameSuffix /* locale */, mPrefs); + dict.clearAndFlushDictionary(); + dict.close(); + } + + /** + * Shut down executer and wait until all operations of user history are done. + * @param testFilenameSuffix file name suffix used for testing. + */ + private void waitForWriting(final String testFilenameSuffix) { + try { + final UserHistoryDictionary dict = + PersonalizationHelper.getUserHistoryDictionary(getContext(), + testFilenameSuffix, mPrefs); + dict.shutdownExecutorForTests(); + while (!dict.isTerminatedForTests()) { + Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); + } + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: ", e); + } + } + public void testRandomWords() { - File dictFile = null; Log.d(TAG, "This test can be used for profiling."); Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); + final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix + + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + final int numberOfWords = 1000; final Random random = new Random(123456); try { + clearHistory(testFilenameSuffix); addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, true /* checksContents */); } finally { - try { - final UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffix, mPrefs); - Log.d(TAG, "waiting for writing ..."); - dict.shutdownExecutorForTests(); - while (!dict.isTerminatedForTests()) { - Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); - } - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: " + e); - } - - final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix - + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; - dictFile = new File(getContext().getFilesDir(), fileName); - + Log.d(TAG, "waiting for writing ..."); + waitForWriting(testFilenameSuffix); + final File dictFile = new File(getContext().getFilesDir(), fileName); if (dictFile != null) { assertTrue(dictFile.exists()); assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); @@ -162,6 +181,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; dictFiles[i] = new File(getContext().getFilesDir(), fileName); + clearHistory(testFilenameSuffixes[i]); } final long start = System.currentTimeMillis(); @@ -178,19 +198,9 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " + (end - start) + " ms"); } finally { - try { - Log.d(TAG, "waiting for writing ..."); - for (int i = 0; i < numberOfLanguages; i++) { - final UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffixes[i], mPrefs); - dict.shutdownExecutorForTests(); - while (!dict.isTerminatedForTests()) { - Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); - } - } - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: " + e); + Log.d(TAG, "waiting for writing ..."); + for (int i = 0; i < numberOfLanguages; i++) { + waitForWriting(testFilenameSuffixes[i]); } for (final File file : dictFiles) { if (file != null) { @@ -203,33 +213,21 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } public void testAddManyWords() { - File dictFile = null; final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); final int numberOfWords = ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? 10000 : 1000; final Random random = new Random(123456); - - UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffix, mPrefs); + clearHistory(testFilenameSuffix); try { addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, true /* checksContents */); - dict.close(); } finally { - try { - Log.d(TAG, "waiting for writing ..."); - dict.shutdownExecutorForTests(); - while (!dict.isTerminatedForTests()) { - Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); - } - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: ", e); - } + Log.d(TAG, "waiting for writing ..."); + waitForWriting(testFilenameSuffix); final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; - dictFile = new File(getContext().getFilesDir(), fileName); + final File dictFile = new File(getContext().getFilesDir(), fileName); if (dictFile != null) { assertTrue(dictFile.exists()); assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); |