Merge the dictionary pack in Latin IME.

Bug: 8161354
Change-Id: I17c23f56dd3bc2f27726556bf2c5a9d5520bd172
main
Jean Chalard 2013-03-15 19:00:51 +09:00
parent 5b04829254
commit 0cc0544a29
93 changed files with 7481 additions and 34 deletions

View File

@ -23,8 +23,11 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<application android:label="@string/aosp_android_keyboard_ime_name"
android:icon="@mipmap/ic_ime_settings"
@ -91,5 +94,44 @@
<action android:name="android.text.style.SUGGESTION_PICKED" />
</intent-filter>
</receiver>
<provider android:name="com.android.inputmethod.dictionarypack.DictionaryProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="@string/authority"
android:multiprocess="false"
android:label="@string/dictionary_provider_name">
</provider>
<service android:name="com.android.inputmethod.dictionarypack.DictionaryService"
android:label="@string/dictionary_service_name">
</service>
<receiver android:name="com.android.inputmethod.dictionarypack.EventHandler">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="com.android.inputmethod.latin.dictionarypack.UPDATE_NOW" />
</intent-filter>
</receiver>
<activity android:name="com.android.inputmethod.dictionarypack.DictionarySettingsActivity"
android:label="@string/dictionary_settings_title"
android:icon="@mipmap/ic_ime_settings"
android:theme="@android:style/Theme.Holo"
android:uiOptions="splitActionBarWhenNarrow">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<activity android:name="com.android.inputmethod.dictionarypack.DownloadOverMeteredDialog"
android:label="@string/dictionary_install_over_metered_network_prompt"
android:icon="@mipmap/ic_ime_settings"
android:theme="@android:style/Theme.Holo">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<!-- TODO: Remove paddingRight, layout_marginLeft, layout_marginRight for API version 17+ -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize"
android:background="?android:attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginStart="15dip"
android:layout_marginRight="6dip"
android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+android:id/title"
android:layout_marginLeft="5dip"
android:layout_marginStart="5dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView
android:id="@+android:id/summary"
android:layout_marginLeft="5dip"
android:layout_marginStart="5dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="1" />
<!-- <ProgressBar -->
<!-- android:id="@+id/dictionary_line_progress_bar" -->
<!-- style="@android:style/Widget.Holo.ProgressBar.Horizontal" -->
<!-- android:layout_width="match_parent" -->
<!-- android:layout_height="match_parent" -->
<!-- android:gravity="center" /> -->
</LinearLayout>
<Button
android:id="@+android:id/wordlist_button"
android:layout_weight="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/install_dict" />
</LinearLayout>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="middle"
android:dividerPadding="0dip" >
<!-- The list of packages that correspond to the requesting UID
and the account/authtokenType that is being requested -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
android:layout_weight="1"
android:gravity="top|center_horizontal">
<TextView
android:id="@+id/download_over_metered_prompt"
android:paddingTop="14dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:paddingBottom="12dip" />
</ScrollView>
<!-- The buttons to trigger download or wait -->
<LinearLayout
android:id="@+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/buttonBarStyle">
<Button
android:id="@+id/deny_button"
android:text="@string/do_not_download_over_metered"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="2"
android:onClick="onClickDeny"
style="?android:attr/buttonBarButtonStyle" />
<!-- The text of this button contains the size of the dictionary so it will be filled programmatically -->
<Button
android:id="@+id/allow_button"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="2"
android:onClick="onClickAllow"
style="?android:attr/buttonBarButtonStyle" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loading_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" >
<ProgressBar android:id="@+id/loading_progress_bar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/loading_progress_bar"
android:layout_centerHorizontal="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/message_loading"
android:paddingTop="4dip"
android:singleLine="true" />
</RelativeLayout>

1
java/res/raw/empty.dict Normal file
View File

@ -0,0 +1 @@
x<EFBFBD>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Verstek"</string>
<string name="language_settings" msgid="1671153053201809031">"Taal en invoer"</string>
<string name="select_input_method" msgid="4301602374609275003">"Kies invoermetode"</string>
<string name="app_name" msgid="1017058186322714405">"Woordeboekverskaffer"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Woordeboekverskaffer"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Woordeboek-diens"</string>
<string name="download_description" msgid="3274861514695032954">"Woordeboek se opdateerinligting"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Addisionele woordeboeke"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Woordeboek beskikbaar"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Instellings vir woordeboeke"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Gebruikerwoordeboeke"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Gebruikerwoordeboek"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Woordeboek beskikbaar"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Laai tans af"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Geïnstalleer"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Geïnstalleer, gedeaktiveer"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Kan nie aan woordeboekdiens koppel nie"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Geen woordeboeke beskikbaar nie"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Herlaai"</string>
<string name="last_update" msgid="3101549719827600346">"Laas opgedateer"</string>
<string name="message_updating" msgid="820186276704134720">"Kontroleer vir opdaterings"</string>
<string name="message_loading" msgid="8611339149825047446">"Laai tans..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Hoofwoordeboek"</string>
<string name="cancel" msgid="5586531736609183304">"Kanselleer"</string>
<string name="install_dict" msgid="5525005524697607865">"Installeer"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Kanselleer aflaaisel"</string>
<string name="disable_dict" msgid="7685810040236497700">"Deaktiveer"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktiveer"</string>
<string name="delete_dict" msgid="5817159290503843766">"Vee uit"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Die gekose taal op jou mobiele toestel het \'n beskikbare woordeboek.&lt;br/&gt; Ons beveel aan dat die <xliff:g id="LANGUAGE">%1$s</xliff:g>-woordeboek &lt;b&gt;afgelaai&lt;/b&gt; word om jou tikervaring \'n beter een te maak.&lt;br/&gt; &lt;br/&gt; Dit kan \'n minuut of twee neem om oor 3G af te laai. Heffings mag geld as jy nie \'n &lt;b&gt;onbeperkte dataplan&lt;/b&gt;.&lt;br/&gt; het nie As jy onseker oor jou dataplan is, beveel ons aan dat jy \'n WiFi-verbinding soek om outomaties te begin aflaai.&lt;br/&gt; &lt;br/&gt; Wenk: Jy kan woordeboeke aflaai en verwyder deur te gaan na &lt;b&gt;Taal en invoer&lt;/b&gt; in die &lt;b&gt;Instellings&lt;/b&gt;-kieslys van jou mobiele toestel."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Laai nou af (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Laai oor Wi-Fi af"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"\'n Woordeboek is vir <xliff:g id="LANGUAGE">%1$s</xliff:g> beskikbaar"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Druk om te hersien en af te laai"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"ነባሪ"</string>
<string name="language_settings" msgid="1671153053201809031">"ቋንቋ እና ግቤት"</string>
<string name="select_input_method" msgid="4301602374609275003">"የግቤት ስልት ይምረጡ"</string>
<string name="app_name" msgid="1017058186322714405">"መዝገበ ቃላት አቅራቢ"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"መዝገበ ቃላት አቅራቢ"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"የመዝገበ ቃላት አገልግሎት"</string>
<string name="download_description" msgid="3274861514695032954">"መዝገበ ቃላት ማዘመኛ መረጃ"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"እየታከሉ የሚያድጉ መዝገበ ቃላቶች"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"መዝገበ ቃላት ይገኛል"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"የመዝገበ ቃላት ቅንብሮች"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"የተጠቃሚ መዝገበ ቃላት"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"የተጠቃሚ መዝገበ ቃላት"</string>
<string name="dictionary_available" msgid="3192920608520618083">"መዝገበ ቃላት አለ"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"በአሁን ጊዜ በማውረድ ላይ"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"ተጭኗል"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"ተጭኗል፣ ተሰናክሏል"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"ወደ መዝገበ ቃላት አገልገሎት በማገናኘት ላይ ችግር"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"ምንም መዝገበ ቃላት የሉም"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"አድስ"</string>
<string name="last_update" msgid="3101549719827600346">"ለመጨረሻ ጊዜ የዘመነው"</string>
<string name="message_updating" msgid="820186276704134720">"ዝማኔዎችን በመፈተሽ ላይ"</string>
<string name="message_loading" msgid="8611339149825047446">"በመጫን ላይ…"</string>
<string name="main_dict_description" msgid="1679964306980098570">"ዋና መዝገበ ቃል"</string>
<string name="cancel" msgid="5586531736609183304">"ተወው"</string>
<string name="install_dict" msgid="5525005524697607865">"ጫን"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"ማውረድን ተወው"</string>
<string name="disable_dict" msgid="7685810040236497700">"አሰናክል"</string>
<string name="enable_dict" msgid="3848179784702473680">"አንቃ"</string>
<string name="delete_dict" msgid="5817159290503843766">"ሰርዝ"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"መዝገበ ቃላት ለ<xliff:g id="LANGUAGE">%1$s</xliff:g> ይገኛል"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"ለመገምገምና ለማውረድ ተጫን"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"الافتراضية"</string>
<string name="language_settings" msgid="1671153053201809031">"اللغة والإدخال"</string>
<string name="select_input_method" msgid="4301602374609275003">"اختيار أسلوب الإدخال"</string>
<string name="app_name" msgid="1017058186322714405">"مقدم القاموس"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"مقدم القاموس"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"خدمة القاموس"</string>
<string name="download_description" msgid="3274861514695032954">"معلومات تحديث القاموس"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"القواميس الإضافية"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"قاموس متوفر"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"إعدادات القواميس"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"قواميس المستخدم"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"قاموس المستخدم"</string>
<string name="dictionary_available" msgid="3192920608520618083">"القاموس متاح"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"يتم حاليًا التنزيل"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"مثبت"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"مثبت، معطل"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"مشكلة في الاتصال بخدمة القاموس"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"لا تتوفر أية قواميس"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"تحديث"</string>
<string name="last_update" msgid="3101549719827600346">"تاريخ آخر تحديث"</string>
<string name="message_updating" msgid="820186276704134720">"جارٍ البحث عن تحديثات"</string>
<string name="message_loading" msgid="8611339149825047446">"تحميل..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"القاموس الرئيسي"</string>
<string name="cancel" msgid="5586531736609183304">"إلغاء"</string>
<string name="install_dict" msgid="5525005524697607865">"تثبيت"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"إلغاء التنزيل"</string>
<string name="disable_dict" msgid="7685810040236497700">"تعطيل"</string>
<string name="enable_dict" msgid="3848179784702473680">"تمكين"</string>
<string name="delete_dict" msgid="5817159290503843766">"حذف"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"هناك قاموس متوفر للغة <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"اضغط للمراجعة والتنزيل"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Па змаўчанні"</string>
<string name="language_settings" msgid="1671153053201809031">"Мова і ўвод"</string>
<string name="select_input_method" msgid="4301602374609275003">"Выберыце метад уводу"</string>
<string name="app_name" msgid="1017058186322714405">"Пастаўшчык слоўніка"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Пастаўшчык слоўніка"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Слоўнік"</string>
<string name="download_description" msgid="3274861514695032954">"Інфармацыя абнаўлення слоўніка"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Дадатковыя слоўнікі"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Даступны слоўнік"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Налады для слоўнікаў"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Карыстальніцкія слоўнікі"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Карыстальніцкі слоўнік"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Даступны слоўнік"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Спампоўваецца зараз"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Усталяваны"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Усталявана, адключана"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Праблема падключэння да слоўніка"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Слоўнікаў няма"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Абнавіць"</string>
<string name="last_update" msgid="3101549719827600346">"Апошняе абнаўленне"</string>
<string name="message_updating" msgid="820186276704134720">"Праверка наяўнасці абнаўленняў"</string>
<string name="message_loading" msgid="8611339149825047446">"Загрузка..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Асноўны слоўнік"</string>
<string name="cancel" msgid="5586531736609183304">"Адмена"</string>
<string name="install_dict" msgid="5525005524697607865">"Усталяваць"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Адмяніць спампаванне"</string>
<string name="disable_dict" msgid="7685810040236497700">"Адключыць"</string>
<string name="enable_dict" msgid="3848179784702473680">"Уключыць"</string>
<string name="delete_dict" msgid="5817159290503843766">"Выдаліць"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Слоўнік для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\""</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Нацiснiце, каб прагледзець i спампаваць"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Стандартни"</string>
<string name="language_settings" msgid="1671153053201809031">"Език и въвеждане"</string>
<string name="select_input_method" msgid="4301602374609275003">"Избор на метод на въвеждане"</string>
<string name="app_name" msgid="1017058186322714405">"Доставчик на речника"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Доставчик на речника"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Услуга за речник"</string>
<string name="download_description" msgid="3274861514695032954">"Информация за актуализацията на речниците"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Добавени речници"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Налице е речник"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Настройки за речници"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Потребителски речници"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Потребителски речник"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Речникът е налице"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Изтеглят се понастоящем"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Инсталирано"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Инсталирано, деактивирано"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Има проблем с връзката"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Няма налични речници"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Опресняване"</string>
<string name="last_update" msgid="3101549719827600346">"Последна актуализация:"</string>
<string name="message_updating" msgid="820186276704134720">"Проверява се за актуализации"</string>
<string name="message_loading" msgid="8611339149825047446">"Зарежда се..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Основен речник"</string>
<string name="cancel" msgid="5586531736609183304">"Отказ"</string>
<string name="install_dict" msgid="5525005524697607865">"Инсталиране"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Анулиране на изтеглянето"</string>
<string name="disable_dict" msgid="7685810040236497700">"Деактивиране"</string>
<string name="enable_dict" msgid="3848179784702473680">"Активиране"</string>
<string name="delete_dict" msgid="5817159290503843766">"Изтриване"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"За <xliff:g id="LANGUAGE">%1$s</xliff:g> е налице речник"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Натиснете, за да прегледате и изтеглите"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Predeterminat"</string>
<string name="language_settings" msgid="1671153053201809031">"Idioma i introducció"</string>
<string name="select_input_method" msgid="4301602374609275003">"Selecció de mètodes d\'introducció"</string>
<string name="app_name" msgid="1017058186322714405">"Proveïdor de diccionaris"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Proveïdor de diccionaris"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Servei de diccionari"</string>
<string name="download_description" msgid="3274861514695032954">"Informació d\'actualització del diccionari"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Diccionaris complementaris"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Diccionari disponible"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Configuració dels diccionaris"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Diccionaris de l\'usuari"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Diccionari de l\'usuari"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Diccionari disponible"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"S\'està baixant"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instal·lat"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instal·lat, desactivat"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"S\'ha produït un problema en connectar al servei de diccionari"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"No hi ha cap diccionari disponible."</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Actualitza"</string>
<string name="last_update" msgid="3101549719827600346">"Última actualització"</string>
<string name="message_updating" msgid="820186276704134720">"S\'està comprovant si hi ha actualitzacions"</string>
<string name="message_loading" msgid="8611339149825047446">"S\'està carregant..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Diccionari principal"</string>
<string name="cancel" msgid="5586531736609183304">"Cancel·la"</string>
<string name="install_dict" msgid="5525005524697607865">"Instal·la"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancel·la la baixada"</string>
<string name="disable_dict" msgid="7685810040236497700">"Desactiva"</string>
<string name="enable_dict" msgid="3848179784702473680">"Activa"</string>
<string name="delete_dict" msgid="5817159290503843766">"Suprimeix"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"L\'idioma seleccionat al teu dispositiu mòbil té un diccionari disponible.&lt;br/&gt; Et recomanem que &lt;b&gt;baixis&lt;/b&gt; el diccionari de <xliff:g id="LANGUAGE">%1$s</xliff:g> per millorar la teva experiència d\'escriptura.&lt;br/&gt; &lt;br/&gt; La baixada pot trigar un parell de minuts mitjançant 3G. És possible que s\'apliquin càrrecs si no tens un &lt;b&gt;pla de dades il·limitat&lt;/b&gt;.&lt;br/&gt; Si no estàs segur de quin pla de dades tens, et recomanem que cerquis una connexió Wi-Fi per començar la baixada automàticament.&lt;br/&gt; &lt;br/&gt; Consell: Pots baixar i eliminar diccionaris si vas a la secció &lt;b&gt;Idioma i entrada&lt;/b&gt; del menú &lt;b&gt;Configuració&lt;/b&gt; del dispositiu mòbil."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Baixa ara (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Baixa mitjançant Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Hi ha un diccionari disponible per a l\'idioma: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Prem per opinar i per baixar"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Výchozí"</string>
<string name="language_settings" msgid="1671153053201809031">"Jazyk a zadávání"</string>
<string name="select_input_method" msgid="4301602374609275003">"Výběr metody zadávání dat"</string>
<string name="app_name" msgid="1017058186322714405">"Poskytovatel slovníku"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Poskytovatel slovníku"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Služba slovníku"</string>
<string name="download_description" msgid="3274861514695032954">"Informace o aktualizaci slovníku"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Doplňkové slovníky"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Je k dispozici slovník"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Nastavení pro slovníky"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Uživatelské slovníky"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Uživatelský slovník"</string>
<string name="dictionary_available" msgid="3192920608520618083">"K dispozici je slovník"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Aktuální stahování"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Nainstalováno"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Nainstalováno, zakázáno"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Probl. s přip. k sl."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Žádné slovníky"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Aktualizovat"</string>
<string name="last_update" msgid="3101549719827600346">"Poslední aktualizace"</string>
<string name="message_updating" msgid="820186276704134720">"Kontrola aktualizací"</string>
<string name="message_loading" msgid="8611339149825047446">"Načítání..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Hlavní slovník"</string>
<string name="cancel" msgid="5586531736609183304">"Zrušit"</string>
<string name="install_dict" msgid="5525005524697607865">"Nainstalovat"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Zrušit stahování"</string>
<string name="disable_dict" msgid="7685810040236497700">"Zakázat"</string>
<string name="enable_dict" msgid="3848179784702473680">"Povolit"</string>
<string name="delete_dict" msgid="5817159290503843766">"Smazat"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Je k dispozici slovník pro jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Stisknutím zkontrolujete a stáhnete"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Standard"</string>
<string name="language_settings" msgid="1671153053201809031">"Sprog og input"</string>
<string name="select_input_method" msgid="4301602374609275003">"Vælg inputmetode"</string>
<string name="app_name" msgid="1017058186322714405">"Dictionary Provider"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Dictionary Provider"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Ordbogstjeneste"</string>
<string name="download_description" msgid="3274861514695032954">"Opdateringsoplysninger for ordbøger"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Tillægsordbøger"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Der er en tilgængelig ordbog"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Indstillinger for ordbøger"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Brugerordbøger"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Brugerordbog"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Ordbog er tilgængelig"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Downloader i øjeblikket"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installeret"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installeret, deaktiveret"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Uden ordbogstjeneste"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Ingen tilg. ordbøger"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Opdater"</string>
<string name="last_update" msgid="3101549719827600346">"Sidst opdateret"</string>
<string name="message_updating" msgid="820186276704134720">"Søger efter opdateringer"</string>
<string name="message_loading" msgid="8611339149825047446">"Indlæser..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Hovedordbog"</string>
<string name="cancel" msgid="5586531736609183304">"Annuller"</string>
<string name="install_dict" msgid="5525005524697607865">"Installer"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Annuller download"</string>
<string name="disable_dict" msgid="7685810040236497700">"Deaktiver"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktivér"</string>
<string name="delete_dict" msgid="5817159290503843766">"Slet"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Det valgte sprog på din mobilenhed har en tilgængelig ordbog.&lt;br/&gt; Vi anbefaler, at du &lt;b&gt;downloader&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g>-ordbogen for at forbedre din skriveoplevelse.&lt;br/&gt; &lt;br/&gt; Downloaden kan tage 1-2 minutter via 3G. Der bliver muligvis opkrævet afgifter, hvis du ikke har et &lt;b&gt;ubegrænset dataabonnement&lt;/b&gt;.&lt;br/&gt;. Hvis du ikke er sikker på, hvilket dataabonnemt du har, anbefaler vi, at du finder en Wi-Fi-forbindelse for at starte automatisk download.&lt;br/&gt; &lt;br/&gt;Tip: Du kan downloade og fjerne ordbøger ved at gå til &lt;b&gt;Sprog og input &lt;/b&gt; i menuen &lt;b&gt;Indstillinger&lt;/b&gt; på din mobilenhed."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Download nu (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Download via Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Der er en tilgængelig ordbog for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Tryk for at gennemgå og downloade"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Standard"</string>
<string name="language_settings" msgid="1671153053201809031">"Sprache &amp; Eingabe"</string>
<string name="select_input_method" msgid="4301602374609275003">"Eingabemethode wählen"</string>
<string name="app_name" msgid="1017058186322714405">"Wörterbuchbereitstellung"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Wörterbuchbereitstellung"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Wörterbuch"</string>
<string name="download_description" msgid="3274861514695032954">"Update-Informationen für Wörterbuch"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Erweiterte Wörterbücher"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Wörterbuch verfügbar"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Einstellungen für Wörterbücher"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Meine Wörterbücher"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Mein Wörterbuch"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Wörterbuch verfügbar"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Aktuelle Downloads"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installiert"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installiert, deaktiviert"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Kein Wörterbuchdienst"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Keine Wörterbücher"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Aktualisieren"</string>
<string name="last_update" msgid="3101549719827600346">"Zuletzt aktualisiert"</string>
<string name="message_updating" msgid="820186276704134720">"Suche nach Updates..."</string>
<string name="message_loading" msgid="8611339149825047446">"Wird geladen..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Allgemeines Wörterbuch"</string>
<string name="cancel" msgid="5586531736609183304">"Abbrechen"</string>
<string name="install_dict" msgid="5525005524697607865">"Installieren"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Download abbrechen"</string>
<string name="disable_dict" msgid="7685810040236497700">"Deaktivieren"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktivieren"</string>
<string name="delete_dict" msgid="5817159290503843766">"Löschen"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Für die auf dem Mobilgerät ausgewählte Sprache ist ein Wörterbuch verfügbar.&lt;br/&gt; &lt;b&gt;Laden Sie das <xliff:g id="LANGUAGE">%1$s</xliff:g>-Wörterbuch herunter&lt;/b&gt; und verbessern Sie Ihre Eingabeerfahrung.&lt;br/&gt; &lt;br/&gt;Der Download über 3G kann ein bis zwei Minuten dauern. Falls Sie keine &lt;b&gt;Datenflatrate&lt;/b&gt; haben, fallen eventuell Gebühren an.&lt;br/&gt; Sollten Sie sich nicht sicher sein, welchen Datentarif Sie haben, suchen Sie eine WLAN-Verbindung, um den Download automatisch zu starten.&lt;br/&gt; &lt;br/&gt;Tipp: Im Menü &lt;b&gt;Einstellungen&lt;/b&gt; Ihres Mobilgeräts können Sie unter &lt;b&gt;Sprache &amp; Eingabe&lt;/b&gt; Wörterbücher herunterladen und entfernen."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Jetzt herunterladen (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Über WLAN herunterladen"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Es ist ein Wörterbuch für <xliff:g id="LANGUAGE">%1$s</xliff:g> verfügbar."</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Zum Lesen und Herunterladen drücken"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Προεπιλογή"</string>
<string name="language_settings" msgid="1671153053201809031">"Γλώσσα και εισαγωγή"</string>
<string name="select_input_method" msgid="4301602374609275003">"Επιλογή μεθόδου εισαγωγής"</string>
<string name="app_name" msgid="1017058186322714405">"Παροχέας λεξικού"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Παροχέας λεξικού"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Υπηρεσία λεξικού"</string>
<string name="download_description" msgid="3274861514695032954">"Ενημέρωση πληροφοριών λεξικού"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Πρόσθετα λεξικά"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Διαθέσιμο λεξικό"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Ρυθμίσεις για λεξικά"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Λεξικά χρήστη"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Λεξικό χρήστη"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Διαθέσιμο λεξικό"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Λήψη αυτήν τη στιγμή"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Εγκατεστημένο"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Εγκαταστάθηκε, απενεργοποιήθηκε"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Πρόβλ.σύνδ.στο λεξ."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Δεν υπάρχουν διαθέσιμα λεξικά"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Ανανέωση"</string>
<string name="last_update" msgid="3101549719827600346">"Τελευταία ενημέρωση"</string>
<string name="message_updating" msgid="820186276704134720">"Έλεγχος για ενημερώσεις"</string>
<string name="message_loading" msgid="8611339149825047446">"Φόρτωση..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Κύριο λεξικό"</string>
<string name="cancel" msgid="5586531736609183304">"Ακύρωση"</string>
<string name="install_dict" msgid="5525005524697607865">"Εγκατάσταση"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Ακύρωση λήψης"</string>
<string name="disable_dict" msgid="7685810040236497700">"Απενεργοποίηση"</string>
<string name="enable_dict" msgid="3848179784702473680">"Ενεργοποίηση"</string>
<string name="delete_dict" msgid="5817159290503843766">"Διαγραφή"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Υπάρχει διαθέσιμο λεξικό για τα <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Πατήστε για έλεγχο και λήψη"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Default"</string>
<string name="language_settings" msgid="1671153053201809031">"Language &amp; input"</string>
<string name="select_input_method" msgid="4301602374609275003">"Choose input method"</string>
<string name="app_name" msgid="1017058186322714405">"Dictionary Provider"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Dictionary Provider"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Dictionary Service"</string>
<string name="download_description" msgid="3274861514695032954">"Dictionary update information"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Add-on dictionaries"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dictionary available"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Settings for dictionaries"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"User dictionaries"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"User dictionary"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dictionary available"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Currently downloading"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installed"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installed, disabled"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problem while connecting to dictionary service"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"No dictionaries available"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Refresh"</string>
<string name="last_update" msgid="3101549719827600346">"Last updated"</string>
<string name="message_updating" msgid="820186276704134720">"Checking for updates"</string>
<string name="message_loading" msgid="8611339149825047446">"Loading..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Main dictionary"</string>
<string name="cancel" msgid="5586531736609183304">"Cancel"</string>
<string name="install_dict" msgid="5525005524697607865">"Install"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancel download"</string>
<string name="disable_dict" msgid="7685810040236497700">"Disable"</string>
<string name="enable_dict" msgid="3848179784702473680">"Enable"</string>
<string name="delete_dict" msgid="5817159290503843766">"Delete"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"The selected language on your mobile device has an available dictionary.&lt;br/&gt; We recommend &lt;b&gt;downloading&lt;/b&gt; the <xliff:g id="LANGUAGE">%1$s</xliff:g> dictionary to improve your typing experience.&lt;br/&gt; &lt;br/&gt; The download could take a minute or two over 3G. Charges may apply if you don\'t have an &lt;b&gt;unlimited data plan&lt;/b&gt;.&lt;br/&gt; If you are not sure which data plan you have, we recommend finding a Wifi connection to start the download automatically.&lt;br/&gt; &lt;br/&gt; Tip: You can download and remove dictionaries by going to &lt;b&gt;Language &amp; input&lt;/b&gt; in the &lt;b&gt;Settings&lt;/b&gt; menu of your mobile device."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Download now (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Download over Wifi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"A dictionary is available for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Press to review and download"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
<string name="language_settings" msgid="1671153053201809031">"Teclado e idioma"</string>
<string name="select_input_method" msgid="4301602374609275003">"Seleccionar método de entrada"</string>
<string name="app_name" msgid="1017058186322714405">"Proveedor de diccionarios"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Proveedor de diccionarios"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Servicio de diccionarios"</string>
<string name="download_description" msgid="3274861514695032954">"Información acerca de la actualización del diccionario"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Diccionarios complementarios"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Diccionario disponible"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Configuración de los diccionarios"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Diccionarios del usuario"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Diccionario del usuario"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Diccionario disponible"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Descarga en curso"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalado"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalado, inhabilitado"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Err. conex. con dic."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"No hay diccionarios."</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Actualizar"</string>
<string name="last_update" msgid="3101549719827600346">"Última actualización"</string>
<string name="message_updating" msgid="820186276704134720">"Buscando las actualizaciones"</string>
<string name="message_loading" msgid="8611339149825047446">"Cargando…"</string>
<string name="main_dict_description" msgid="1679964306980098570">"Diccionario principal"</string>
<string name="cancel" msgid="5586531736609183304">"Cancelar"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalar"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancelar la descarga"</string>
<string name="disable_dict" msgid="7685810040236497700">"Inhabilitar"</string>
<string name="enable_dict" msgid="3848179784702473680">"Activar"</string>
<string name="delete_dict" msgid="5817159290503843766">"Eliminar"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Hay un diccionario disponible para el idioma seleccionado en tu dispositivo móvil.&lt;br/&gt; Te recomendamos que &lt;b&gt;descargues&lt;/b&gt; el diccionario de <xliff:g id="LANGUAGE">%1$s</xliff:g> para mejorar tu experiencia de escritura.&lt;br/&gt; &lt;br/&gt; La descarga puede tardar unos minutos en redes 3G. Si no tienes un &lt;b&gt;plan de datos ilimitado&lt;/b&gt;, es posible que se apliquen cargos.&lt;br/&gt; Si no conoces las características de tu plan de datos, te recomendamos que uses una conexión Wi-Fi para iniciar la descarga automáticamente.&lt;br/&gt; &lt;br/&gt; Sugerencia: Puedes descargar y eliminar diccionarios en la sección &lt;b&gt;Idioma e introducción de texto&lt;/b&gt; del menú &lt;b&gt;Ajustes&lt;/b&gt; del dispositivo móvil."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Descargar ahora ( <xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Descargar mediante Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Hay un diccionario disponible de <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pulsa para consultar y descargar"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
<string name="language_settings" msgid="1671153053201809031">"Idioma y entrada de texto"</string>
<string name="select_input_method" msgid="4301602374609275003">"Selecciona un método de entrada"</string>
<string name="app_name" msgid="1017058186322714405">"Proveedor del diccionario"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Proveedor del diccionario"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Servicio de diccionario"</string>
<string name="download_description" msgid="3274861514695032954">"Información de actualización del diccionario"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Diccionarios complementarios"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Diccionario disponible"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Ajustes de diccionarios"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Diccionarios del usuario"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Diccionario del usuario"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Hay un diccionario disponible"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Descargas en curso"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalado"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalado (inhabilitado)"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Error al conectar"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"No hay diccionarios"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Actualizar"</string>
<string name="last_update" msgid="3101549719827600346">"Última actualización"</string>
<string name="message_updating" msgid="820186276704134720">"Buscando actualizaciones"</string>
<string name="message_loading" msgid="8611339149825047446">"Cargando..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Diccionario principal"</string>
<string name="cancel" msgid="5586531736609183304">"Cancelar"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalar"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancelar descarga"</string>
<string name="disable_dict" msgid="7685810040236497700">"Inhabilitar"</string>
<string name="enable_dict" msgid="3848179784702473680">"Habilitar"</string>
<string name="delete_dict" msgid="5817159290503843766">"Eliminar"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Hay un diccionario disponible para el idioma seleccionado en tu dispositivo móvil.&lt;br/&gt; Te recomendamos que &lt;b&gt;descargues&lt;/b&gt; el diccionario de <xliff:g id="LANGUAGE">%1$s</xliff:g> para mejorar tu experiencia de escritura.&lt;br/&gt; &lt;br/&gt; La descarga puede tardar unos minutos en redes 3G. Si no tienes un &lt;b&gt;plan de datos ilimitado&lt;/b&gt;, es posible que se apliquen cargos.&lt;br/&gt; Si no conoces las características de tu plan de datos, te recomendamos que uses una conexión Wi-Fi para iniciar la descarga automáticamente.&lt;br/&gt; &lt;br/&gt; Sugerencia: puedes descargar y eliminar diccionarios en la sección &lt;b&gt;Idioma e introducción de texto&lt;/b&gt; del menú &lt;b&gt;Ajustes&lt;/b&gt; del dispositivo móvil."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Descargar ahora (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Descargar mediante Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Hay un diccionario disponible de <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pulsa para comprobar y descargar"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Vaikeväärtus"</string>
<string name="language_settings" msgid="1671153053201809031">"Keeled ja sisestamine"</string>
<string name="select_input_method" msgid="4301602374609275003">"Valige sisestusmeetod"</string>
<string name="app_name" msgid="1017058186322714405">"Sõnastikupakkuja"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Sõnastikupakkuja"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Sõnaraamatuteenus"</string>
<string name="download_description" msgid="3274861514695032954">"Sõnastiku värskendamisteave"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Pistiksõnastikud"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Sõnastik on saadaval"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Sõnastike seaded"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Kasutajasõnastikud"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Kasutajasõnastik"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Sõnastik on saadaval"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Praegu allalaadimisel"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installitud"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installitud, keelatud"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Tõrge sõnast. ühend."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Sõnastikke pole"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Värskenda"</string>
<string name="last_update" msgid="3101549719827600346">"Viimati värskendatud"</string>
<string name="message_updating" msgid="820186276704134720">"Värskenduste otsimine"</string>
<string name="message_loading" msgid="8611339149825047446">"Laadimine ..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Põhisõnastik"</string>
<string name="cancel" msgid="5586531736609183304">"Tühista"</string>
<string name="install_dict" msgid="5525005524697607865">"Installi"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Tühista allalaadimine"</string>
<string name="disable_dict" msgid="7685810040236497700">"Keela"</string>
<string name="enable_dict" msgid="3848179784702473680">"Luba"</string>
<string name="delete_dict" msgid="5817159290503843766">"Kustuta"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Mobiilseadmes valitud keelele on saadaval sõnastik.&lt;br/&gt; Teksti sisestamiseks soovitame &lt;b&gt;alla laadida&lt;/b&gt; sõnastiku <xliff:g id="LANGUAGE">%1$s</xliff:g>.&lt;br/&gt; &lt;br/&gt; 3G kaudu allalaadimisele võib kuluda minut või paar. Kehtida võivad tasud, kui te ei kasuta &lt;b&gt;piiramatut andmepaketti&lt;/b&gt;.&lt;br/&gt; Kui te ei tea, millist andmepaketti kasutate, soovitame allalaadimise automaatseks käivitamiseks leida WiFi-ühenduse.&lt;br/&gt; &lt;br/&gt; Nõuanne: sõnastikke saate alla laadida ja eemaldada, tehes valiku &lt;b&gt;Keele &amp; sisend&lt;/b&gt; mobiilseadme menüüs &lt;b&gt;Seaded&lt;/b&gt;."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Laadi kohe alla (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Laadi alla WiFi kaudu"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Sõnastik on <xliff:g id="LANGUAGE">%1$s</xliff:g> keele jaoks saadaval"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Vajutage ülevaatamiseks ja allalaadimiseks"</string>
</resources>

View File

@ -174,4 +174,39 @@
<string name="button_default" msgid="3988017840431881491">"پیش‌فرض"</string>
<string name="language_settings" msgid="1671153053201809031">"زبان و ورودی"</string>
<string name="select_input_method" msgid="4301602374609275003">"انتخاب روش ورودی"</string>
<string name="app_name" msgid="1017058186322714405">"ارائه‌دهنده فرهنگ لغت"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"ارائه دهنده فرهنگ لغت"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"سرویس فرهنگ لغت"</string>
<string name="download_description" msgid="3274861514695032954">"اطلاعات به‌روزرسانی فرهنگ لغت"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"فرهنگ‌های لغت افزودنی"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"فرهنگ لغت در دسترس"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"تنظیمات برای فرهنگ‌ لغت‌ها"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"فرهنگ‌های لغت کاربر"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"فرهنگ‌ لغت کاربر"</string>
<string name="dictionary_available" msgid="3192920608520618083">"فرهنگ لغت موجود است"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"موارد در حال دانلود کنونی"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"نصب شد"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"نصب شد، غیرفعال شد"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"مشکل اتصال به سرویس فرهنگ لغت"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"هیچ فرهنگ لغتی موجود نیست"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"بازخوانی"</string>
<string name="last_update" msgid="3101549719827600346">"تاریخ آخرین به‌روزرسانی"</string>
<string name="message_updating" msgid="820186276704134720">"درحال بررسی به‌روزرسانی‌ها"</string>
<string name="message_loading" msgid="8611339149825047446">"در حال بارگیری..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"فرهنگ‌ لغت اصلی"</string>
<string name="cancel" msgid="5586531736609183304">"لغو"</string>
<string name="install_dict" msgid="5525005524697607865">"نصب"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"لغو دانلود"</string>
<string name="disable_dict" msgid="7685810040236497700">"غیرفعال کردن"</string>
<string name="enable_dict" msgid="3848179784702473680">"فعال کردن"</string>
<string name="delete_dict" msgid="5817159290503843766">"حذف"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"یک فرهنگ لغت برای <xliff:g id="LANGUAGE">%1$s</xliff:g> در دسترس است"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"برای مرور و دانلود فشار دهید"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Oletusarvot"</string>
<string name="language_settings" msgid="1671153053201809031">"Kieli ja syöttötapa"</string>
<string name="select_input_method" msgid="4301602374609275003">"Valitse syöttötapa"</string>
<string name="app_name" msgid="1017058186322714405">"Sanakirjan tarjoaja"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Sanakirjan tarjoaja"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Sanakirjapalvelu"</string>
<string name="download_description" msgid="3274861514695032954">"Sanakirjan päivitystiedot"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Sanakirjalisäosat"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Sanakirja saatavilla"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Sanakirjojen asetukset"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Käyttäjän sanakirjat"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Käyttäjän sanakirja"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Sanakirja saatavilla"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Ladataan parhaillaan"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Asennettu"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Asennettu, poistettu käytöstä"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Ongelma yhd. sanak.palveluun"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Ei sanakirj. saatav."</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Päivitä"</string>
<string name="last_update" msgid="3101549719827600346">"Viimeksi päivitetty"</string>
<string name="message_updating" msgid="820186276704134720">"Tarkistetaan päivityksiä"</string>
<string name="message_loading" msgid="8611339149825047446">"Ladataan..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Pääsanakirja"</string>
<string name="cancel" msgid="5586531736609183304">"Peruuta"</string>
<string name="install_dict" msgid="5525005524697607865">"Asenna"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Peruuta lataus"</string>
<string name="disable_dict" msgid="7685810040236497700">"Poista käytöstä"</string>
<string name="enable_dict" msgid="3848179784702473680">"Ota käyttöön"</string>
<string name="delete_dict" msgid="5817159290503843766">"Poista"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Kielen <xliff:g id="LANGUAGE">%1$s</xliff:g> sanakirja on saatavilla"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Paina tätä, jos haluat tarkastella kohdetta tai ladata sen"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Par défaut"</string>
<string name="language_settings" msgid="1671153053201809031">"Langue et saisie"</string>
<string name="select_input_method" msgid="4301602374609275003">"Sélectionnez le mode de saisie"</string>
<string name="app_name" msgid="1017058186322714405">"Fournisseur de dictionnaires"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Fournisseur de dictionnaires"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Service de dictionnaires"</string>
<string name="download_description" msgid="3274861514695032954">"Informations relatives à la mise à jour des dictionnaires"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dictionnaires complémentaires"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dictionnaire disponible"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Paramètres des dictionnaires"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Dictionnaires personnels"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Dictionnaire personnel"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dictionnaire disponible"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"En cours de téléchargement…"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installé"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installé, désactivé"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Pas de service dictionnaire."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Aucun dictionnaire."</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Actualiser"</string>
<string name="last_update" msgid="3101549719827600346">"Dernière mise à jour"</string>
<string name="message_updating" msgid="820186276704134720">"Recherche de mises à jour en cours…"</string>
<string name="message_loading" msgid="8611339149825047446">"Chargement…"</string>
<string name="main_dict_description" msgid="1679964306980098570">"Dictionnaire principal"</string>
<string name="cancel" msgid="5586531736609183304">"Annuler"</string>
<string name="install_dict" msgid="5525005524697607865">"Installer"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Annuler le téléchargement"</string>
<string name="disable_dict" msgid="7685810040236497700">"Désactiver"</string>
<string name="enable_dict" msgid="3848179784702473680">"Activer"</string>
<string name="delete_dict" msgid="5817159290503843766">"Supprimer"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Un dictionnaire est disponible pour la langue sélectionnée sur votre appareil mobile.&lt;br/&gt; Nous vous invitons à &lt;b&gt;télécharger&lt;/b&gt; le dictionnaire <xliff:g id="LANGUAGE">%1$s</xliff:g> pour faciliter votre saisie.&lt;br/&gt; &lt;br/&gt; Le téléchargement peut prendre une à deux minutes via une connexion 3G. Des frais peuvent s\'appliquer si vous ne disposez pas d\'un &lt;b&gt;forfait Internet illimité&lt;/b&gt;.&lt;br/&gt; Si vous n\'êtes pas sûr de votre forfait, nous vous conseillons d\'utiliser une connexion Wi-Fi pour lancer automatiquement le téléchargement.&lt;br/&gt; &lt;br/&gt; Astuce : Vous pouvez télécharger et supprimer des dictionnaires dans &lt;b&gt;Langue et saisie&lt;/b&gt; du menu &lt;b&gt;Paramètres&lt;/b&gt; de votre appareil mobile."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Télécharger maintenant (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> Mo)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Télécharger via Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Un dictionnaire est disponible en <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Appuyez ici pour consulter et télécharger le dictionnaire."</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"डिफ़ॉल्ट"</string>
<string name="language_settings" msgid="1671153053201809031">"भाषा और इनपुट"</string>
<string name="select_input_method" msgid="4301602374609275003">"इनपुट पद्धति चुनें"</string>
<string name="app_name" msgid="1017058186322714405">"डिक्‍शनरी प्रदाता"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"डिक्‍शनरी प्रदाता"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"डिक्‍शनरी सेवा"</string>
<string name="download_description" msgid="3274861514695032954">"डिक्‍शनरी अपडेट जानकारी"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"एड-ऑन डिक्शनरी"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"डिक्‍शनरी उपलब्‍ध"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"डिक्‍शनरी के लिए सेटिंग"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"उपयोगकर्ता डिक्शनरी"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"उपयोगकर्ता डिक्शनरी"</string>
<string name="dictionary_available" msgid="3192920608520618083">"डिक्शनरी उपलब्‍ध"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"वर्तमान में डाउनलोड हो रहा है"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"इंस्‍टॉल है"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"इंस्‍टॉल है, अक्षम है"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"डिक्‍श. सेवा से कनेक्‍ट करने में समस्‍या"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"कोई डिक्‍शनरी उपलब्‍ध नहीं"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"रीफ़्रेश करें"</string>
<string name="last_update" msgid="3101549719827600346">"अंतिम अपडेट"</string>
<string name="message_updating" msgid="820186276704134720">"अपडेट देखे जा रहे हैं"</string>
<string name="message_loading" msgid="8611339149825047446">"लोड हो रही है..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"मुख्‍य डिक्‍शनरी"</string>
<string name="cancel" msgid="5586531736609183304">"रद्द करें"</string>
<string name="install_dict" msgid="5525005524697607865">"इंस्‍टॉल करें"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"डाउनलोड रद्द करें"</string>
<string name="disable_dict" msgid="7685810040236497700">"अक्षम करें"</string>
<string name="enable_dict" msgid="3848179784702473680">"सक्षम करें"</string>
<string name="delete_dict" msgid="5817159290503843766">"हटाएं"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g> के लिए डिक्‍शनरी उपलब्‍ध है"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"समीक्षा और डाउनलोड करने के लिए दबाएं"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Zadano"</string>
<string name="language_settings" msgid="1671153053201809031">"Jezik i unos"</string>
<string name="select_input_method" msgid="4301602374609275003">"Odabir načina unosa"</string>
<string name="app_name" msgid="1017058186322714405">"Davatelj rječnika"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Davatelj rječnika"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Usluga rječnika"</string>
<string name="download_description" msgid="3274861514695032954">"Ažurirane informacije rječnika"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Rječnici - dodaci"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dostupan je rječnik"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Postavke za rječnike"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Korisnički rječnici"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Korisnički rječnik"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Rječnik je dostupan"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Trenutačno u preuzimanju"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalirano"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalirano, onemogućeno"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Nema usluge rječnika"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Rječnici nedostupni"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Osvježi"</string>
<string name="last_update" msgid="3101549719827600346">"Zadnje ažuriranje"</string>
<string name="message_updating" msgid="820186276704134720">"Provjera ažuriranja"</string>
<string name="message_loading" msgid="8611339149825047446">"Učitavanje..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Glavni rječnik"</string>
<string name="cancel" msgid="5586531736609183304">"Odustani"</string>
<string name="install_dict" msgid="5525005524697607865">"Instaliraj"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Otkaži preuzimanje"</string>
<string name="disable_dict" msgid="7685810040236497700">"Onemogući"</string>
<string name="enable_dict" msgid="3848179784702473680">"Omogući"</string>
<string name="delete_dict" msgid="5817159290503843766">"Izbriši"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Dostupan je rječnik za <xliff:g id="LANGUAGE">%1$s</xliff:g> jezik"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pritisnite za pregled i preuzimanje"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Alapértelmezett"</string>
<string name="language_settings" msgid="1671153053201809031">"Nyelv és bevitel"</string>
<string name="select_input_method" msgid="4301602374609275003">"Beviteli mód kiválasztása"</string>
<string name="app_name" msgid="1017058186322714405">"Szótárszolgáltató"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Szótárszolgáltató"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Szótár szolgáltatás"</string>
<string name="download_description" msgid="3274861514695032954">"Szótárfrissítéssel kapcsolatos információk"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Bővítmények: szótárak"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Van rendelkezésre álló szótár"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Szótárak beállításai"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Felhasználói szótárak"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Felhasználói szótár"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Van rendelkezésre álló szótár"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Jelenlegi letöltések"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Telepítve"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Telepítve, kikapcsolva"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Csatlakozási hiba"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Nincs szótár"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Frissítés"</string>
<string name="last_update" msgid="3101549719827600346">"Utoljára frissítve"</string>
<string name="message_updating" msgid="820186276704134720">"Frissítések keresése"</string>
<string name="message_loading" msgid="8611339149825047446">"Betöltés..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Fő szótár"</string>
<string name="cancel" msgid="5586531736609183304">"Mégse"</string>
<string name="install_dict" msgid="5525005524697607865">"Telepítés"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Letöltés megszakítása"</string>
<string name="disable_dict" msgid="7685810040236497700">"Kikapcsolás"</string>
<string name="enable_dict" msgid="3848179784702473680">"Bekapcsolás"</string>
<string name="delete_dict" msgid="5817159290503843766">"Törlés"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g> nyelvhez van rendelkezésre álló szótár"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Nyomja meg az áttekintéshez és letöltéshez"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Default"</string>
<string name="language_settings" msgid="1671153053201809031">"Bahasa &amp; masukan"</string>
<string name="select_input_method" msgid="4301602374609275003">"Pilih metode masukan"</string>
<string name="app_name" msgid="1017058186322714405">"Penyedia Kamus"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Penyedia Kamus"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Layanan Kamus"</string>
<string name="download_description" msgid="3274861514695032954">"Informasi pembaruan kamus"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Kamus pengaya"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Kamus tersedia"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Setelan untuk kamus"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Kamus pengguna"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Kamus pengguna"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Kamus yang tersedia"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Saat ini sedang mengunduh"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Terpasang"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Terpasang, dinonaktifkan"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Masalah koneksi ke layanan kamus"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Tidak tersedia kamus"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Segarkan"</string>
<string name="last_update" msgid="3101549719827600346">"Terakhir diperbarui"</string>
<string name="message_updating" msgid="820186276704134720">"Memeriksa pembaruan"</string>
<string name="message_loading" msgid="8611339149825047446">"Memuat..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Kamus utama"</string>
<string name="cancel" msgid="5586531736609183304">"Batal"</string>
<string name="install_dict" msgid="5525005524697607865">"Pasang"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Batalkan unduhan"</string>
<string name="disable_dict" msgid="7685810040236497700">"Nonaktifkan"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktifkan"</string>
<string name="delete_dict" msgid="5817159290503843766">"Hapus"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Kamus tersedia untuk bahasa <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Tekan untuk meninjau dan mengunduh"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Predefinito"</string>
<string name="language_settings" msgid="1671153053201809031">"Lingua e input"</string>
<string name="select_input_method" msgid="4301602374609275003">"Scegli il metodo di immissione"</string>
<string name="app_name" msgid="1017058186322714405">"Dictionary Provider"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Dictionary Provider"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Servizio dizionario"</string>
<string name="download_description" msgid="3274861514695032954">"Informazioni aggiornate dizionari"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dizionari aggiuntivi"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dizionario disponibile"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Impostazioni per dizionari"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Dizionari utente"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Dizionario utente"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dizionario disponibile"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"In fase di download"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installato"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installato, disabilitato"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problema conness. dizion."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Nessun dizionario"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Aggiorna"</string>
<string name="last_update" msgid="3101549719827600346">"Ultimo aggiornamento"</string>
<string name="message_updating" msgid="820186276704134720">"Verifica disponibilità aggiornamenti"</string>
<string name="message_loading" msgid="8611339149825047446">"Caricamento..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Dizionario principale"</string>
<string name="cancel" msgid="5586531736609183304">"Annulla"</string>
<string name="install_dict" msgid="5525005524697607865">"Installa"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Annulla download"</string>
<string name="disable_dict" msgid="7685810040236497700">"Disattiva"</string>
<string name="enable_dict" msgid="3848179784702473680">"Abilita"</string>
<string name="delete_dict" msgid="5817159290503843766">"Elimina"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"La lingua selezionata sul tuo dispositivo mobile ha un dizionario disponibile.&lt;br/&gt; Ti consigliamo di &lt;b&gt;scaricare&lt;/b&gt; il dizionario di <xliff:g id="LANGUAGE">%1$s</xliff:g> per migliorare l\'esperienza di digitazione.&lt;br/&gt; &lt;br/&gt; Il download potrebbe richiedere un paio di minuti su reti 3G. Potrebbero essere applicate delle tariffe se non disponi di un &lt;b&gt;piano dati illimitato&lt;/b&gt;.&lt;br/&gt; Se non sai bene quale piano dati è in uso, ti consigliamo di trovare una connessione Wi-Fi per avviare il download automaticamente.&lt;br/&gt; &lt;br/&gt; Suggerimento. Puoi scaricare e rimuovere i dizionari passando a &lt;b&gt;Lingue e immissione&lt;/b&gt; nel menu &lt;b&gt;Impostazioni&lt;/b&gt; del tuo dispositivo mobile."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Scarica ora (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Scarica tramite Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"È disponibile un dizionario per <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Premi per esaminare e scaricare"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"ברירת מחדל"</string>
<string name="language_settings" msgid="1671153053201809031">"שפה וקלט"</string>
<string name="select_input_method" msgid="4301602374609275003">"בחירת שיטת קלט"</string>
<string name="app_name" msgid="1017058186322714405">"ספק המילון"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"ספק המילון"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"שירות מילון"</string>
<string name="download_description" msgid="3274861514695032954">"פרטי עדכון מילון"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"הוספת מילונים"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"מילון זמין"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"הגדרות עבור מילונים"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"מילוני משתמש"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"מילון משתמש"</string>
<string name="dictionary_available" msgid="3192920608520618083">"מילון זמין"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"מוריד כעת"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"מותקן"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"מותקן, מושבת"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"בעיה בהתחברות לשירות המילון"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"אין מילונים זמינים"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"רענן"</string>
<string name="last_update" msgid="3101549719827600346">"עודכן לאחרונה"</string>
<string name="message_updating" msgid="820186276704134720">"מחפש עדכונים"</string>
<string name="message_loading" msgid="8611339149825047446">"טוען..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"מילון ראשי"</string>
<string name="cancel" msgid="5586531736609183304">"ביטול"</string>
<string name="install_dict" msgid="5525005524697607865">"התקן"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"בטל הורדה"</string>
<string name="disable_dict" msgid="7685810040236497700">"השבת"</string>
<string name="enable_dict" msgid="3848179784702473680">"הפוך לפעיל"</string>
<string name="delete_dict" msgid="5817159290503843766">"מחק"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"לשפה הנבחרת במכשיר הנייד שלך יש מילון זמין.&lt;br/&gt; אנו ממליצים &lt;b&gt;להוריד&lt;/b&gt; את המילון ב<xliff:g id="LANGUAGE">%1$s</xliff:g> כדי לשפר את חוויית ההקלדה.&lt;br/&gt; &lt;br/&gt; ההורדה עשויה לארוך דקה או שתיים ב-3G. ייתכן שתחויב אם אין לך &lt;b&gt;תוכנית נתונים בלתי מוגבלת&lt;/b&gt;.&lt;br/&gt; אם אינך בטוח איזו תוכנית נתונים יש לך, אנו ממליצים לחפש חיבור Wi-Fi כדי להתחיל בהורדה באופן אוטומטי.&lt;br/&gt; &lt;br/&gt; טיפ: ניתן להוריד ולהסיר מילונים ב&lt;b&gt;שפה וקלט&lt;/b&gt; בתפריט &lt;b&gt;הגדרות&lt;/b&gt; של המכשיר הנייד שלך."</string>
<string name="download_over_metered" msgid="4024013764937850061">"הורד עכשיו (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"הורד באמצעות Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"יש מילון זמין עבור <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"לחץ כדי לעיין ולהוריד"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"デフォルト"</string>
<string name="language_settings" msgid="1671153053201809031">"言語と入力"</string>
<string name="select_input_method" msgid="4301602374609275003">"入力方法の選択"</string>
<string name="app_name" msgid="1017058186322714405">"辞書提供元"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"辞書提供元"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"辞書"</string>
<string name="download_description" msgid="3274861514695032954">"辞書のアップデート情報"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"アドオン辞書"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"辞書を利用できます"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"辞書の設定"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"単語リスト"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"単語リスト"</string>
<string name="dictionary_available" msgid="3192920608520618083">"辞書を利用できます"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"ダウンロードしています"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"インストール済み"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"インストール済み、無効"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"辞書に接続できません"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"辞書はありません"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"更新"</string>
<string name="last_update" msgid="3101549719827600346">"最終更新日"</string>
<string name="message_updating" msgid="820186276704134720">"アップデートを確認しています"</string>
<string name="message_loading" msgid="8611339149825047446">"読み込み中..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"メイン辞書"</string>
<string name="cancel" msgid="5586531736609183304">"キャンセル"</string>
<string name="install_dict" msgid="5525005524697607865">"インストール"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"ダウンロードをキャンセル"</string>
<string name="disable_dict" msgid="7685810040236497700">"無効にする"</string>
<string name="enable_dict" msgid="3848179784702473680">"有効にする"</string>
<string name="delete_dict" msgid="5817159290503843766">"削除"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g>の辞書を利用できます"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"押すと確認/ダウンロードできます"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"기본값"</string>
<string name="language_settings" msgid="1671153053201809031">"언어 및 키보드"</string>
<string name="select_input_method" msgid="4301602374609275003">"입력 방법 선택"</string>
<string name="app_name" msgid="1017058186322714405">"사전 제공업체"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"사전 제공업체"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"사전 서비스"</string>
<string name="download_description" msgid="3274861514695032954">"사전 업데이트 정보"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"사전 추가"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"사전 사용 가능"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"사전 설정"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"사용자 사전"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"사용자 사전"</string>
<string name="dictionary_available" msgid="3192920608520618083">"사전 사용 가능"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"현재 다운로드 중"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"설치됨"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"설치 완료되었으나 사용 중지됨"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"사전 서비스에 연결하는 동안 문제가 발생했습니다."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"사용할 수 있는 사전이 없습니다."</string>
<string name="check_for_updates_now" msgid="642057986127624986">"새로고침"</string>
<string name="last_update" msgid="3101549719827600346">"최근 업데이트"</string>
<string name="message_updating" msgid="820186276704134720">"업데이트를 확인하는 중"</string>
<string name="message_loading" msgid="8611339149825047446">"로드 중..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"기본 사전"</string>
<string name="cancel" msgid="5586531736609183304">"취소"</string>
<string name="install_dict" msgid="5525005524697607865">"설치"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"다운로드 취소"</string>
<string name="disable_dict" msgid="7685810040236497700">"사용 중지"</string>
<string name="enable_dict" msgid="3848179784702473680">"사용"</string>
<string name="delete_dict" msgid="5817159290503843766">"삭제"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g> 사전을 사용할 수 있습니다."</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"검토하고 다운로드하려면 누르세요."</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Numatytieji"</string>
<string name="language_settings" msgid="1671153053201809031">"Kalba ir įvestis"</string>
<string name="select_input_method" msgid="4301602374609275003">"Pasirinkite įvesties metodą"</string>
<string name="app_name" msgid="1017058186322714405">"Žodyno teikėjas"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Žodyno teikėjas"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Žodyno paslauga"</string>
<string name="download_description" msgid="3274861514695032954">"Žodyno atnaujinimo informacija"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Papildomi žodynai"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Žodynas galimas"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Žodynų nustatymai"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Naudotojo žodynai"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Naudotojo žodynas"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Žodynas galimas"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Šiuo metu atsisiunčiama"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Įdiegta"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Įdiegta, neleidžiama"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Prisijungimo prie žodyno paslaugos problema"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Nėra galimų žodynų"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Atnaujinti"</string>
<string name="last_update" msgid="3101549719827600346">"Paskutinį kartą atnaujinta"</string>
<string name="message_updating" msgid="820186276704134720">"Ieškoma naujinių"</string>
<string name="message_loading" msgid="8611339149825047446">"Įkeliama..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Pagrindinis žodynas"</string>
<string name="cancel" msgid="5586531736609183304">"Atšaukti"</string>
<string name="install_dict" msgid="5525005524697607865">"Įdiegti"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Atšaukti atsisiuntimą"</string>
<string name="disable_dict" msgid="7685810040236497700">"Neleisti"</string>
<string name="enable_dict" msgid="3848179784702473680">"Įgalinti"</string>
<string name="delete_dict" msgid="5817159290503843766">"Ištrinti"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Galimas <xliff:g id="LANGUAGE">%1$s</xliff:g> žodynas"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Paspauskite, kad peržiūrėtumėte ir atsisiųstumėte"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Noklusējums"</string>
<string name="language_settings" msgid="1671153053201809031">"Valoda un ievade"</string>
<string name="select_input_method" msgid="4301602374609275003">"Ievades metodes izvēle"</string>
<string name="app_name" msgid="1017058186322714405">"Vārdnīcas nodrošinātājs"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Vārdnīcas nodrošinātājs"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Vārdnīcas pakalpojums"</string>
<string name="download_description" msgid="3274861514695032954">"Vārdnīcas atjauninājuma informācija"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Pievienojumvārdnīcas"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Vārdnīca ir pieejama"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Vārdnīcu iestatījumi"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Lietotāja vārdnīcas"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Lietotāja vārdnīca"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Vārdnīca ir pieejama."</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Notiek lejupielāde."</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalēta"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalēta, atspējota"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problēma, savien. ar vārdn. pak."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Vārdn. nav pieejamas"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Atsvaidzināt"</string>
<string name="last_update" msgid="3101549719827600346">"Pēdējo reizi atjaunināts"</string>
<string name="message_updating" msgid="820186276704134720">"Notiek pārbaude, vai ir pieejami atjauninājumi."</string>
<string name="message_loading" msgid="8611339149825047446">"Notiek ielāde..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Galvenā vārdnīca"</string>
<string name="cancel" msgid="5586531736609183304">"Atcelt"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalēt"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Atcelt lejupielādi"</string>
<string name="disable_dict" msgid="7685810040236497700">"Atspējot"</string>
<string name="enable_dict" msgid="3848179784702473680">"Iespējot"</string>
<string name="delete_dict" msgid="5817159290503843766">"Dzēst"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Mobilajā ierīcē atlasītajai valodai ir pieejama vārdnīca.&lt;br/&gt; Ieteicams &lt;b&gt;lejupielādēt&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g> vārdnīcu, lai uzlabotu rakstīšanas iespējas.&lt;br/&gt; &lt;br/&gt; Lejupielāde, izmantojot 3G, aizņems dažas minūtes. Ja nelietojat &lt;b&gt;neierobežotu datu plānu&lt;/b&gt;, var tikt piemērota maksa.&lt;br/&gt; Ja nezināt, kādu datu plānu lietojat, atrodiet Wi-Fi savienojumu, lai automātiski sāktu lejupielādi.&lt;br/&gt; &lt;br/&gt; Padoms: vārdnīcas var lejupielādēt un noņemt mobilās ierīces izvēlnes &lt;b&gt;Iestatījumi&lt;/b&gt; sadaļā &lt;b&gt;Valodas ievade&lt;/b&gt;."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Lejupielādēt tūlīt (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Lejupielādēt, izmantojot Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Ir pieejama vārdnīca šādai valodai: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Nospiediet, lai pārskatītu un lejupielādētu"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Lalai"</string>
<string name="language_settings" msgid="1671153053201809031">"Bahasa &amp; input"</string>
<string name="select_input_method" msgid="4301602374609275003">"Pilih kaedah input"</string>
<string name="app_name" msgid="1017058186322714405">"Pembekal Kamus"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Pembekal Kamus"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Perkhidmatan Kamus"</string>
<string name="download_description" msgid="3274861514695032954">"Maklumat kemas kini kamus"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Kamus tambahan"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Kamus tersedia"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Tetapan untuk kamus"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Kamus pengguna"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Kamus pengguna"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Kamus tersedia"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Sedang memuat turun"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Dipasang"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Dipasang, dilumpuhkan"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Masalah menyambung kepada perkhidmatan kamus"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Tiada kamus tersedia"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Muat semula"</string>
<string name="last_update" msgid="3101549719827600346">"Kali terakhir dikemas kini"</string>
<string name="message_updating" msgid="820186276704134720">"Menyemak kemas kini"</string>
<string name="message_loading" msgid="8611339149825047446">"Memuatkan..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Kamus utama"</string>
<string name="cancel" msgid="5586531736609183304">"Batal"</string>
<string name="install_dict" msgid="5525005524697607865">"Pasang"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Batalkan muat turun"</string>
<string name="disable_dict" msgid="7685810040236497700">"Lumpuhkan"</string>
<string name="enable_dict" msgid="3848179784702473680">"Dayakan"</string>
<string name="delete_dict" msgid="5817159290503843766">"Padam"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Kamus tersedia untuk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Tekan untuk mengulas dan memuat turun"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Standard"</string>
<string name="language_settings" msgid="1671153053201809031">"Språk og inndata"</string>
<string name="select_input_method" msgid="4301602374609275003">"Velg inndatametode"</string>
<string name="app_name" msgid="1017058186322714405">"Ordlisteleverandør"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Ordlisteleverandør"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Ordboktjeneste"</string>
<string name="download_description" msgid="3274861514695032954">"Oppdateringsinformasjon for ordliste"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Tilleggsordlister"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Ordliste er tilgjengelig"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Innstillinger for ordlister"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Brukerordlister"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Brukerordliste"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Ordliste er tilgjengelig"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Laster ned nå"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installert"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installert, deaktivert"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Kan ikke koble til ordlistetjenesten"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Fant ingen ordliste"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Last inn på nytt"</string>
<string name="last_update" msgid="3101549719827600346">"Sist oppdatert"</string>
<string name="message_updating" msgid="820186276704134720">"Ser etter oppdateringer"</string>
<string name="message_loading" msgid="8611339149825047446">"Laster inn …"</string>
<string name="main_dict_description" msgid="1679964306980098570">"Hovedordliste"</string>
<string name="cancel" msgid="5586531736609183304">"Avbryt"</string>
<string name="install_dict" msgid="5525005524697607865">"Installer"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Avbryt nedlastingen"</string>
<string name="disable_dict" msgid="7685810040236497700">"Deaktiver"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktiver"</string>
<string name="delete_dict" msgid="5817159290503843766">"Slett"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Det valgte språket på mobileneheten din har en tilgjengelig ordliste.&lt;br/&gt; Vi anbefaler å &lt;b&gt;laste ned&lt;/b&gt; ordlisten for <xliff:g id="LANGUAGE">%1$s</xliff:g>. Dette forbedrer skriveopplevelsen din.&lt;br/&gt; &lt;br/&gt; Nedlastingen kan ta fra ett til to minutter via 3G. Belastninger kan påløpe hvis du ikke har et abonnement med &lt;b&gt;ubegrenset databruk&lt;/b&gt;.&lt;br/&gt; Hvis du er usikker på hvilken abonnementstype du har, anbefaler vi deg å finne en Wi-Fi-tilkobling for å starte nedlastingen automatisk.&lt;br/&gt; &lt;br/&gt; Tips: Du kan laste ned og fjerne ordlister ved å gå til &lt;b&gt;Språk og inndata&lt;/b&gt; i menyen for &lt;b&gt;Innstillinger&lt;/b&gt; på mobilenheten din."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Last ned nå (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Last ned via Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"En ordliste er tilgjengelig for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Trykk for å se gjennom og laste ned"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Standaard"</string>
<string name="language_settings" msgid="1671153053201809031">"Taal en invoer"</string>
<string name="select_input_method" msgid="4301602374609275003">"Invoermethode selecteren"</string>
<string name="app_name" msgid="1017058186322714405">"Woordenboekleverancier"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Woordenboekleverancier"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Woordenboekservice"</string>
<string name="download_description" msgid="3274861514695032954">"Informatie over woordenboekupdate"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Woordenboeken toevoegen"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Woordenboek beschikbaar"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Instellingen voor woordenboeken"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Gebruikerswoordenboeken"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Gebruikerswoordenboek"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Woordenboek beschikbaar"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Wordt gedownload"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Geïnstalleerd"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Geïnstalleerd, uitgeschakeld"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Verbindingsprobleem woordenboekservice"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Geen woordenboeken"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Vernieuwen"</string>
<string name="last_update" msgid="3101549719827600346">"Laatst bijgewerkt"</string>
<string name="message_updating" msgid="820186276704134720">"Controleren op updates"</string>
<string name="message_loading" msgid="8611339149825047446">"Laden..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Algemeen woordenboek"</string>
<string name="cancel" msgid="5586531736609183304">"Annuleren"</string>
<string name="install_dict" msgid="5525005524697607865">"Installeren"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Download annuleren"</string>
<string name="disable_dict" msgid="7685810040236497700">"Uitschakelen"</string>
<string name="enable_dict" msgid="3848179784702473680">"Inschakelen"</string>
<string name="delete_dict" msgid="5817159290503843766">"Verwijderen"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Er is een woordenboek beschikbaar voor de geselecteerde taal op uw mobiele apparaat.&lt;br/&gt; We raden u aan het woordenboek voor het <xliff:g id="LANGUAGE">%1$s</xliff:g> te &lt;b&gt;downloaden&lt;/b&gt; om uw typevaardigheid te verbeteren.&lt;br/&gt; &lt;br/&gt; De download kan een of twee minuten duren via 3G. Er kunnen kosten worden berekend als u geen &lt;b&gt;onbeperkt gegevensabonnement&lt;/b&gt; heeft.&lt;br/&gt; Als u niet zeker weet welk gegevensabonnement u heeft, raden we u aan een wifi-verbinding te zoeken om de download automatisch te starten.&lt;br/&gt; &lt;br/&gt; Tip: u kunt woordenboeken downloaden en verwijderen door naar &lt;b&gt;Taal en invoer&lt;/b&gt; in het menu &lt;b&gt;Instellingen&lt;/b&gt; van uw mobiele apparaat te gaan."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Nu downloaden (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Downloaden via wifi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Er is een woordenboek beschikbaar voor <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Druk om te controleren en te downloaden"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Domyślne"</string>
<string name="language_settings" msgid="1671153053201809031">"Język, klawiatura, głos"</string>
<string name="select_input_method" msgid="4301602374609275003">"Wybierz metodę wprowadzania"</string>
<string name="app_name" msgid="1017058186322714405">"Dostawca słownika"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Dostawca słownika"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Usługa słownika"</string>
<string name="download_description" msgid="3274861514695032954">"Informacje o aktualizacji słownika"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Słowniki dodatkowe"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dostępny słownik"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Ustawienia słowników"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Słowniki użytkownika"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Słownik użytkownika"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Słownik jest dostępny"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Aktualnie pobierany"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Zainstalowany"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Zainstalowany, wyłączony"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problem z połączeniem z usługą słownika"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Brak słowników"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Odśwież"</string>
<string name="last_update" msgid="3101549719827600346">"Ostatnia aktualizacja"</string>
<string name="message_updating" msgid="820186276704134720">"Sprawdzanie dostępności aktualizacji"</string>
<string name="message_loading" msgid="8611339149825047446">"Wczytywanie..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Słownik główny"</string>
<string name="cancel" msgid="5586531736609183304">"Anuluj"</string>
<string name="install_dict" msgid="5525005524697607865">"Zainstaluj"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Anuluj pobieranie"</string>
<string name="disable_dict" msgid="7685810040236497700">"Wyłącz"</string>
<string name="enable_dict" msgid="3848179784702473680">"Włącz"</string>
<string name="delete_dict" msgid="5817159290503843766">"Usuń"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Dla języka: <xliff:g id="LANGUAGE">%1$s</xliff:g> jest dostępny słownik"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Naciśnij, aby sprawdzić i pobrać"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Predefinido"</string>
<string name="language_settings" msgid="1671153053201809031">"Idioma e entrada de som"</string>
<string name="select_input_method" msgid="4301602374609275003">"Escolher o método de entrada"</string>
<string name="app_name" msgid="1017058186322714405">"Fornecedor de Dicionário"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Fornecedor de Dicionário"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Serviço de Dicionário"</string>
<string name="download_description" msgid="3274861514695032954">"Informações de atualização do dicionário"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dicionários suplementares"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dicionário disponível"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Definições dos dicionários"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Dicionários do utilizador"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Dicionário do utilizador"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dicionário disponível"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Transferência em curso"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalado"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalado, desativado"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problema ao ligar ao serviço de dicionário"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Sem dicionários disponíveis"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Atualizar"</string>
<string name="last_update" msgid="3101549719827600346">"Última atualização"</string>
<string name="message_updating" msgid="820186276704134720">"A verificar existência de atualizações"</string>
<string name="message_loading" msgid="8611339149825047446">"A carregar..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Dicionário principal"</string>
<string name="cancel" msgid="5586531736609183304">"Cancelar"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalar"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancelar transferência"</string>
<string name="disable_dict" msgid="7685810040236497700">"Desativar"</string>
<string name="enable_dict" msgid="3848179784702473680">"Ativar"</string>
<string name="delete_dict" msgid="5817159290503843766">"Eliminar"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"O idioma selecionado no seu dispositivo móvel tem um dicionário disponível.&lt;br/&gt; Recomendamos que &lt;b&gt;transfira&lt;/b&gt; o dicionário <xliff:g id="LANGUAGE">%1$s</xliff:g> para melhorar a sua experiência de introdução.&lt;br/&gt; &lt;br/&gt; A transferência pode demorar um ou dois minutos via 3G. Poderão ser aplicadas taxas se não tiver um &lt;b&gt;plano de dados ilimitado&lt;/b&gt;.&lt;br/&gt; Se não tiver a certeza do plano de dados que tem, recomendamos que localize uma ligação Wi-Fi para começar a transferência automaticamente.&lt;br/&gt; &lt;br/&gt; Sugestão: pode transferir e remover dicionários acedendo a &lt;b&gt;Idioma e introdução&lt;/b&gt; no menu &lt;b&gt;Definições&lt;/b&gt; do seu dispositivo móvel."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Transferir agora (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Transferir via Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Está disponível um dicionário para <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Prima para consultar e transferir"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Padrão"</string>
<string name="language_settings" msgid="1671153053201809031">"Idioma e entrada"</string>
<string name="select_input_method" msgid="4301602374609275003">"Selecione o método de entrada"</string>
<string name="app_name" msgid="1017058186322714405">"Provedor de dicionário"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Provedor de dicionário"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Serviço de dicionário"</string>
<string name="download_description" msgid="3274861514695032954">"Informações de atualização do dicionário"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dicionários complementares"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dicionário disponível"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Configurações dos dicionários"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Dicionário do usuário"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Dicionário do usuário"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dicionário disponível"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Download em andamento"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalado"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalado, desativado"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Prob. de conexão c/ dic. de serv."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Nenhum dicionário disponível"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Atualizar"</string>
<string name="last_update" msgid="3101549719827600346">"Última atualização"</string>
<string name="message_updating" msgid="820186276704134720">"Verificando atualizações"</string>
<string name="message_loading" msgid="8611339149825047446">"Carregando..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Dicionário principal"</string>
<string name="cancel" msgid="5586531736609183304">"Cancelar"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalar"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Cancelar download"</string>
<string name="disable_dict" msgid="7685810040236497700">"Desativar"</string>
<string name="enable_dict" msgid="3848179784702473680">"Permitir"</string>
<string name="delete_dict" msgid="5817159290503843766">"Excluir"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Há um dicionário disponível para <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pressione para consultar e fazer o download"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Prestabilit"</string>
<string name="language_settings" msgid="1671153053201809031">"Limbă și introducere de text"</string>
<string name="select_input_method" msgid="4301602374609275003">"Alegeți metoda de introducere de text"</string>
<string name="app_name" msgid="1017058186322714405">"Furnizorul dicţionarului"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Furnizorul dicţionarului"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Serviciul Dicţionar"</string>
<string name="download_description" msgid="3274861514695032954">"Informaţii privind actualizarea dicţionarului"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dicţionare suplimentare"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Dicţionar disponibil"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Setări pentru dicţionare"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Dicţionarele utilizatorului"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Dicţionarul utilizatorului"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Dicţionar disponibil"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Se descarcă acum"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Instalat"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Instalat, dezactivat"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Nu se conect. dicţ."</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Niciun dicţionar"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Actualizaţi"</string>
<string name="last_update" msgid="3101549719827600346">"Data ultimei modificări"</string>
<string name="message_updating" msgid="820186276704134720">"Se verifică existenţa actualizărilor"</string>
<string name="message_loading" msgid="8611339149825047446">"Se încarcă..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Dicţionar principal"</string>
<string name="cancel" msgid="5586531736609183304">"Anulaţi"</string>
<string name="install_dict" msgid="5525005524697607865">"Instalaţi"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Anulaţi descărcarea"</string>
<string name="disable_dict" msgid="7685810040236497700">"Dezactivaţi"</string>
<string name="enable_dict" msgid="3848179784702473680">"Activaţi"</string>
<string name="delete_dict" msgid="5817159290503843766">"Ştergeţi"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Este disponibil un dicţionar pentru <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Apăsaţi pentru a examina şi pentru a descărca"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"По умолчанию"</string>
<string name="language_settings" msgid="1671153053201809031">"Язык и ввод"</string>
<string name="select_input_method" msgid="4301602374609275003">"Выберите способ ввода"</string>
<string name="app_name" msgid="1017058186322714405">"Поставщик словарей"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Поставщик словарей"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Служба словарей"</string>
<string name="download_description" msgid="3274861514695032954">"Обновления словаря"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Дополнительные словари"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Словарь доступен"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Настройки словарей"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Пользовательские словари"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Пользовательский словарь"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Словарь доступен"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Загрузка..."</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Установлен"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Установлен, отключен"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Ошибка подключения"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Словари недоступны"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Обновить"</string>
<string name="last_update" msgid="3101549719827600346">"Последнее обновление"</string>
<string name="message_updating" msgid="820186276704134720">"Проверка обновлений..."</string>
<string name="message_loading" msgid="8611339149825047446">"Загрузка..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Основной словарь"</string>
<string name="cancel" msgid="5586531736609183304">"Отмена"</string>
<string name="install_dict" msgid="5525005524697607865">"Установить"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Отменить загрузку"</string>
<string name="disable_dict" msgid="7685810040236497700">"Отключить"</string>
<string name="enable_dict" msgid="3848179784702473680">"Включить"</string>
<string name="delete_dict" msgid="5817159290503843766">"Удалить"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Доступен словарь: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Нажмите, чтобы просмотреть и загрузить"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Predvolené"</string>
<string name="language_settings" msgid="1671153053201809031">"Jazyk &amp; vstup"</string>
<string name="select_input_method" msgid="4301602374609275003">"Zvoliť metódu vstupu"</string>
<string name="app_name" msgid="1017058186322714405">"Poskytovateľ slovníka"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Poskytovateľ slovníka"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Služba slovníka"</string>
<string name="download_description" msgid="3274861514695032954">"Informácie aktualizácie slovníka"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Doplnkové slovníky"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"K dispozícii je slovník"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Nastavenia pre slovníky"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Používateľské slovníky"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Používateľský slovník"</string>
<string name="dictionary_available" msgid="3192920608520618083">"K dispozícii je slovník"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Aktuálne preberanie"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Nainštalovaný"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Nainštalovaný, zakázaný"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problém s pripojením k službe slovníka"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Slovníky nedostupné"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Obnoviť"</string>
<string name="last_update" msgid="3101549719827600346">"Posledná aktualizácia"</string>
<string name="message_updating" msgid="820186276704134720">"Prebieha kontrola aktualizácií"</string>
<string name="message_loading" msgid="8611339149825047446">"Prebieha načítavanie..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Hlavný slovník"</string>
<string name="cancel" msgid="5586531736609183304">"Zrušiť"</string>
<string name="install_dict" msgid="5525005524697607865">"Inštalovať"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Zrušiť preberanie"</string>
<string name="disable_dict" msgid="7685810040236497700">"Zakázať"</string>
<string name="enable_dict" msgid="3848179784702473680">"Povoliť"</string>
<string name="delete_dict" msgid="5817159290503843766">"Odstrániť"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"K dispozícii je slovník pre jazyk <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Stlačením skontrolujete a prevezmete"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Privzeto"</string>
<string name="language_settings" msgid="1671153053201809031">"Jezik in vnos"</string>
<string name="select_input_method" msgid="4301602374609275003">"Izbira načina vnosa"</string>
<string name="app_name" msgid="1017058186322714405">"Slovar"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Storitev slovarja"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Slovar"</string>
<string name="download_description" msgid="3274861514695032954">"Podatki o posodobitvi slovarja"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Dodatni slovarji"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Slovar je na voljo"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Nastavitve za slovarje"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Uporabniški slovar"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Uporabniški slovar"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Slovar je na voljo"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Trenutno se prenaša"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Nameščeno"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Nameščen, onemogočen"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Težava s povezavo"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Ni slovarjev"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Osveži"</string>
<string name="last_update" msgid="3101549719827600346">"Nazadnje posodobljeno"</string>
<string name="message_updating" msgid="820186276704134720">"Preverjanje, ali so na voljo posodobitve"</string>
<string name="message_loading" msgid="8611339149825047446">"Nalaganje ..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Glavni slovar"</string>
<string name="cancel" msgid="5586531736609183304">"Prekliči"</string>
<string name="install_dict" msgid="5525005524697607865">"Namesti"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Prekliči prenos"</string>
<string name="disable_dict" msgid="7685810040236497700">"Onemogoči"</string>
<string name="enable_dict" msgid="3848179784702473680">"Omogoči"</string>
<string name="delete_dict" msgid="5817159290503843766">"Izbriši"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Za izbran jezik v mob. napravi je na voljo slovar.&lt;br/&gt; Za izboljšano izkušnjo tipkanja priporočamo, da &lt;b&gt;prenesete&lt;/b&gt; slovar za ta jezik: <xliff:g id="LANGUAGE">%1$s</xliff:g>.&lt;br/&gt; &lt;br/&gt; Prenos prek povezave UMTS lahko traja minuto ali dve. Če nimate &lt;b&gt;neomejen. prenosa podatkov&lt;/b&gt;.&lt;br/&gt;, ga boste morda morali plačati. Če ne veste, kateri pod. paket imate, poiščite omrežje Wi-Fi, da prenos začnete samodejno.&lt;br/&gt; &lt;br/&gt; Nasvet: Slovarje lahko prenesete in odstranite tako, da v meniju &lt;b&gt;Nastavitve&lt;/b&gt; v napravi odprete &lt;b&gt;Jezik in vnos&lt;/b&gt;."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Prenesi zdaj (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Prenos prek povezave Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"Slovar je na voljo za jezik <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pritisnite za pregled in prenos"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Подразумевано"</string>
<string name="language_settings" msgid="1671153053201809031">"Језик и унос"</string>
<string name="select_input_method" msgid="4301602374609275003">"Избор метода уноса"</string>
<string name="app_name" msgid="1017058186322714405">"Добављач речника"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Добављач речника"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Услуга речника"</string>
<string name="download_description" msgid="3274861514695032954">"Информације о ажурирању речника"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Помоћни речници"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Речник је доступан"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Подешавања за речнике"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Кориснички речници"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Кориснички речник"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Речник је доступан"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Тренутно се преузима"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Инсталирано"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Инсталиран, онемогућен"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Нема услуге речника"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Нема доступних речника"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Освежи"</string>
<string name="last_update" msgid="3101549719827600346">"Последње ажурирање"</string>
<string name="message_updating" msgid="820186276704134720">"Тражење ажурирања"</string>
<string name="message_loading" msgid="8611339149825047446">"Учитавање..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Главни речник"</string>
<string name="cancel" msgid="5586531736609183304">"Откажи"</string>
<string name="install_dict" msgid="5525005524697607865">"Инсталирај"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Откажи преузимање"</string>
<string name="disable_dict" msgid="7685810040236497700">"Онемогући"</string>
<string name="enable_dict" msgid="3848179784702473680">"Омогући"</string>
<string name="delete_dict" msgid="5817159290503843766">"Избриши"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Речник је доступан за <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Притисните за преглед и преузимање"</string>
</resources>

View File

@ -170,4 +170,36 @@
<string name="button_default" msgid="3988017840431881491">"Standard"</string>
<string name="language_settings" msgid="1671153053201809031">"Språk &amp; inmatning"</string>
<string name="select_input_method" msgid="4301602374609275003">"Välj inmatningsmetod"</string>
<string name="app_name" msgid="1017058186322714405">"Dictionary Provider"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Dictionary Provider"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Ordbokstjänst"</string>
<string name="download_description" msgid="3274861514695032954">"Uppdateringsinformation för ordlista"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Tilläggsordlistor"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"En ordlista är tillgänglig"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Inställningar för ordlistor"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Egna ordlistor"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Egen ordlista"</string>
<string name="dictionary_available" msgid="3192920608520618083">"En ordlista är tillgänglig"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Hämtas för närvarande"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Installerad"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Installerad, inaktiverad"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problem med att ansluta till ordlistetjänsten"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Det finns inga ordböcker"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Uppdatera"</string>
<string name="last_update" msgid="3101549719827600346">"Senast uppdaterad"</string>
<string name="message_updating" msgid="820186276704134720">"Söker efter uppdateringar"</string>
<string name="message_loading" msgid="8611339149825047446">"Läser in ..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Huvudordlistan"</string>
<string name="cancel" msgid="5586531736609183304">"Avbryt"</string>
<string name="install_dict" msgid="5525005524697607865">"Installera"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Avbryt hämtning"</string>
<string name="disable_dict" msgid="7685810040236497700">"Inaktivera"</string>
<string name="enable_dict" msgid="3848179784702473680">"Aktivera"</string>
<string name="delete_dict" msgid="5817159290503843766">"Ta bort"</string>
<string name="should_download_over_metered_prompt" msgid="4965264849057656521">"Det finns en ordlista för språket du har valt i enheten.&lt;br/&gt; Vi rekommenderar att du &lt;b&gt;hämtar&lt;/b&gt; den <xliff:g id="LANGUAGE">%1$s</xliff:g> ordlistan.&lt;br/&gt; &lt;br/&gt; Det kan ta någon minut att hämta den över 3G. Avgifter kan tillkomma om du inte har ett abonnemang med &lt;b&gt;obegränsad datatrafik&lt;/b&gt;.&lt;br/&gt; Om du är osäker på ditt abonnemang rekommenderar vi att du ansluter till ett Wi-Fi-nätverk och hämtar ordlistan automatiskt.&lt;br/&gt; &lt;br/&gt; Tips! Du kan hämta och ta bort ordlistor under &lt;b&gt;Språk och inmatning&lt;/b&gt; i menyn &lt;b&gt;Inställningar&lt;/b&gt; på enheten."</string>
<string name="download_over_metered" msgid="4024013764937850061">"Hämta nu (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
<string name="do_not_download_over_metered" msgid="6963770885033765378">"Hämta över Wi-Fi"</string>
<string name="dict_available_notification_title" msgid="4560576379680660047">"En ordlista är tillgänglig för <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Tryck om du vill granska och hämta"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Chaguo-msingi"</string>
<string name="language_settings" msgid="1671153053201809031">"Lugha na uingizaji"</string>
<string name="select_input_method" msgid="4301602374609275003">"Chagua mbinu ya kuingiza data"</string>
<string name="app_name" msgid="1017058186322714405">"Mtoaji Kamusi"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Mtoaji Kamusi"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Huduma ya Kamusi"</string>
<string name="download_description" msgid="3274861514695032954">"Maelezo ya kusasisha kamusi"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Nyongeza za kamusi"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Kamusi inapatikana"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Mipangilio ya kamusi"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Kamusi ya mtumiaji"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Kamusi ya mtumiaji"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Kamusi inapatikana"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Inapakua sasa"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Imesakinishwa"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"kusakinisha, imelemazwa"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Tatizo kuunganisha kwa huduma ya kamusi"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Hakuna kamusi inapatikana"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Zimua"</string>
<string name="last_update" msgid="3101549719827600346">"Mara ya mwisho kusasishwa"</string>
<string name="message_updating" msgid="820186276704134720">"Inatafuta visasishi..."</string>
<string name="message_loading" msgid="8611339149825047446">"Inapakia..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Kamusi kuu"</string>
<string name="cancel" msgid="5586531736609183304">"Katisha"</string>
<string name="install_dict" msgid="5525005524697607865">"Sakinisha"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Ghairi kupakua"</string>
<string name="disable_dict" msgid="7685810040236497700">"Lemaza"</string>
<string name="enable_dict" msgid="3848179784702473680">"Wezesha"</string>
<string name="delete_dict" msgid="5817159290503843766">"Futa"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Kamusi inapatikana ya <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Bonyeza ili kukagua na kupakua"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"ค่าเริ่มต้น"</string>
<string name="language_settings" msgid="1671153053201809031">"ภาษาและการป้อนข้อมูล"</string>
<string name="select_input_method" msgid="4301602374609275003">"เลือกวิธีการป้อนข้อมูล"</string>
<string name="app_name" msgid="1017058186322714405">"ผู้ให้บริการพจนานุกรม"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"ผู้ให้บริการพจนานุกรม"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"บริการพจนานุกรม"</string>
<string name="download_description" msgid="3274861514695032954">"ข้อมูลอัปเดตสำหรับพจนานุกรม"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"พจนานุกรม Add-On"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"มีพจนานุกรมให้ใช้งาน"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"การตั้งค่าสำหรับพจนานุกรม"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"พจนานุกรมผู้ใช้"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"พจนานุกรมผู้ใช้"</string>
<string name="dictionary_available" msgid="3192920608520618083">"มีพจนานุกรมให้ใช้งาน"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"กำลังดาวน์โหลดอยู่"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"ติดตั้งแล้ว"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"ติดตั้งแล้วแต่ปิดใช้งาน"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"พบปัญหาขณะเชื่อมต่อ"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"ไม่มีพจนานุกรม"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"รีเฟรช"</string>
<string name="last_update" msgid="3101549719827600346">"ปรับปรุงล่าสุดเมื่อ"</string>
<string name="message_updating" msgid="820186276704134720">"กำลังตรวจสอบการอัปเดต..."</string>
<string name="message_loading" msgid="8611339149825047446">"กำลังโหลด..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"พจนานุกรมหลัก"</string>
<string name="cancel" msgid="5586531736609183304">"ยกเลิก"</string>
<string name="install_dict" msgid="5525005524697607865">"ติดตั้ง"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"ยกเลิกการดาวน์โหลด"</string>
<string name="disable_dict" msgid="7685810040236497700">"ปิดใช้งาน"</string>
<string name="enable_dict" msgid="3848179784702473680">"เปิดใช้งาน"</string>
<string name="delete_dict" msgid="5817159290503843766">"ลบ"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"มีพจนานุกรมให้ใช้งานในภาษา <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"กดเพื่อตรวจสอบและดาวน์โหลด"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Default"</string>
<string name="language_settings" msgid="1671153053201809031">"Wika at input"</string>
<string name="select_input_method" msgid="4301602374609275003">"Pumili ng pamamaraan ng pag-input"</string>
<string name="app_name" msgid="1017058186322714405">"Provider ng Diksyunaryo"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Provider ng Diksyunaryo"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Serbisyo ng Diksyunaryo"</string>
<string name="download_description" msgid="3274861514695032954">"Impormasyon ng pag-update sa diksyunaryo"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Mga diksyunaryo na add-on"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Available ang diksyunaryo"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Mga setting para sa mga diksyunaryo"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Mga diksyunaryo ng user"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Diksyunaryo ng user"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Available ang diksyunaryo"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Kasalukuyang nagda-download"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Naka-install"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Naka-install, hindi pinagana"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Problema sa pagkonekta sa serbisyo ng diksyunaryo"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Walang available na mga diksyunaryo"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"I-refresh"</string>
<string name="last_update" msgid="3101549719827600346">"Huling na-update"</string>
<string name="message_updating" msgid="820186276704134720">"Tumitingin ng mga update"</string>
<string name="message_loading" msgid="8611339149825047446">"Naglo-load..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Pangunahing diksyunaryo"</string>
<string name="cancel" msgid="5586531736609183304">"Kanselahin"</string>
<string name="install_dict" msgid="5525005524697607865">"I-install"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Kanselahin ang pag-download"</string>
<string name="disable_dict" msgid="7685810040236497700">"Huwag paganahin"</string>
<string name="enable_dict" msgid="3848179784702473680">"Paganahin"</string>
<string name="delete_dict" msgid="5817159290503843766">"Tanggalin"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Available ang isang diksyunaryo para sa <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Pindutin upang suriin at i-download"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Varsayılan"</string>
<string name="language_settings" msgid="1671153053201809031">"Dil ve giriş"</string>
<string name="select_input_method" msgid="4301602374609275003">"Giriş yöntemini seçin"</string>
<string name="app_name" msgid="1017058186322714405">"Sözlük Sağlayıcı"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Sözlük Sağlayıcı"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Sözlük Hizmeti"</string>
<string name="download_description" msgid="3274861514695032954">"Sözlük güncelleme bilgileri"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Ekli sözlükler"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Kullanılabilecek sözlük var"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Sözlükler için ayarlar"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Kullanıcı sözlükleri"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Kullanıcı sözlüğü"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Sözlük kullanılabilir"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Şu anda indiriliyor"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Yüklendi"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Yüklendi, devre dışı"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Sözlük hizmetine bağlantı yok"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Kullanılabilir sözlük yok"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Yenile"</string>
<string name="last_update" msgid="3101549719827600346">"Son güncelleme tarihi"</string>
<string name="message_updating" msgid="820186276704134720">"Güncellemeler denetleniyor..."</string>
<string name="message_loading" msgid="8611339149825047446">"Yükleniyor..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Ana sözlük"</string>
<string name="cancel" msgid="5586531736609183304">"İptal"</string>
<string name="install_dict" msgid="5525005524697607865">"Yükle"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"İndirmeyi iptal et"</string>
<string name="disable_dict" msgid="7685810040236497700">"Devre dışı bırak"</string>
<string name="enable_dict" msgid="3848179784702473680">"Etkinleştir"</string>
<string name="delete_dict" msgid="5817159290503843766">"Sil"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g> için kullanılabilecek bir sözlük var"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"İncelemek ve indirmek için tıklayın"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"За умовчанням"</string>
<string name="language_settings" msgid="1671153053201809031">"Мова та введення"</string>
<string name="select_input_method" msgid="4301602374609275003">"Вибрати метод введення"</string>
<string name="app_name" msgid="1017058186322714405">"Постачальник словника"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Постачальник словника"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Послуга словника"</string>
<string name="download_description" msgid="3274861514695032954">"Інформація про оновлення словника"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Додаткові словники"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Словник доступний"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Налаштування для словників"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Словники користувача"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Словник користувача"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Словник доступний"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Зараз завантажується"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Установлено"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Установлено, вимкнено"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Нема з’єднання зі словником"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Словники недоступні"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Оновити"</string>
<string name="last_update" msgid="3101549719827600346">"Останнє оновлення"</string>
<string name="message_updating" msgid="820186276704134720">"Перевірка наявності оновлень"</string>
<string name="message_loading" msgid="8611339149825047446">"Завантаження..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Основний словник"</string>
<string name="cancel" msgid="5586531736609183304">"Скасувати"</string>
<string name="install_dict" msgid="5525005524697607865">"Установити"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Скасувати завантаження"</string>
<string name="disable_dict" msgid="7685810040236497700">"Вимкнути"</string>
<string name="enable_dict" msgid="3848179784702473680">"Увімкнути"</string>
<string name="delete_dict" msgid="5817159290503843766">"Видалити"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Доступний словник для такої мови: <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Натисніть, щоб переглянути та завантажити"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Mặc định"</string>
<string name="language_settings" msgid="1671153053201809031">"Ngôn ngữ và phương thức nhập"</string>
<string name="select_input_method" msgid="4301602374609275003">"Chọn phương thức nhập"</string>
<string name="app_name" msgid="1017058186322714405">"Nhà cung cấp từ điển"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Nhà cung cấp từ điển"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Dịch vụ từ điển"</string>
<string name="download_description" msgid="3274861514695032954">"Thông tin cập nhật từ điển"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Từ điển phụ trợ"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Có sẵn từ điển"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Cài đặt dành cho từ điển"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Từ điển người dùng"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Từ điển người dùng"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Có sẵn từ điển"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Hiện đang tải xuống"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Đã cài đặt"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Đã cài đặt, bị tắt"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Lỗi knối d.vụ t.điển"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Không có từ điển nào"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Làm mới"</string>
<string name="last_update" msgid="3101549719827600346">"Cập nhật lần cuối"</string>
<string name="message_updating" msgid="820186276704134720">"Đang kiểm tra cập nhật"</string>
<string name="message_loading" msgid="8611339149825047446">"Đang tải..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Từ điển chính"</string>
<string name="cancel" msgid="5586531736609183304">"Hủy"</string>
<string name="install_dict" msgid="5525005524697607865">"Cài đặt"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Hủy tải xuống"</string>
<string name="disable_dict" msgid="7685810040236497700">"Tắt"</string>
<string name="enable_dict" msgid="3848179784702473680">"Bật"</string>
<string name="delete_dict" msgid="5817159290503843766">"Xóa"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Có sẵn từ điển cho <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Nhấn để xem lại và tải xuống"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"默认"</string>
<string name="language_settings" msgid="1671153053201809031">"语言和输入法"</string>
<string name="select_input_method" msgid="4301602374609275003">"选择输入法"</string>
<string name="app_name" msgid="1017058186322714405">"词典大全"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"词典大全"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"字典服务"</string>
<string name="download_description" msgid="3274861514695032954">"词典更新信息"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"附加词典"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"词典可供下载"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"词典设置"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"用户词典"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"用户词典"</string>
<string name="dictionary_available" msgid="3192920608520618083">"词典可供下载"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"目前正在下载"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"已安装"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"已安装,已停用"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"连接到词典服务时发生问题"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"没有可用的词典"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"刷新"</string>
<string name="last_update" msgid="3101549719827600346">"最后更新时间"</string>
<string name="message_updating" msgid="820186276704134720">"正在检查更新"</string>
<string name="message_loading" msgid="8611339149825047446">"正在加载..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"主词典"</string>
<string name="cancel" msgid="5586531736609183304">"取消"</string>
<string name="install_dict" msgid="5525005524697607865">"安装"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"取消下载"</string>
<string name="disable_dict" msgid="7685810040236497700">"停用"</string>
<string name="enable_dict" msgid="3848179784702473680">"启用"</string>
<string name="delete_dict" msgid="5817159290503843766">"删除"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"<xliff:g id="LANGUAGE">%1$s</xliff:g>词典可供下载"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"按此通知即可查看和下载"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"預設"</string>
<string name="language_settings" msgid="1671153053201809031">"語言與輸入設定"</string>
<string name="select_input_method" msgid="4301602374609275003">"選擇輸入法"</string>
<string name="app_name" msgid="1017058186322714405">"字典提供者"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"字典提供者"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"字典服務"</string>
<string name="download_description" msgid="3274861514695032954">"字典更新資訊"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"外掛字典"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"字典可供下載"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"字典設定"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"使用者字典"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"使用者字典"</string>
<string name="dictionary_available" msgid="3192920608520618083">"可用的字典"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"目前正在下載"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"已安裝"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"已安裝但目前停用"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"連線至字典服務時發生問題"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"沒有可用的字典"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"重新整理"</string>
<string name="last_update" msgid="3101549719827600346">"上次更新時間"</string>
<string name="message_updating" msgid="820186276704134720">"正在檢查更新"</string>
<string name="message_loading" msgid="8611339149825047446">"載入中..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"主要字典"</string>
<string name="cancel" msgid="5586531736609183304">"取消"</string>
<string name="install_dict" msgid="5525005524697607865">"安裝"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"取消下載"</string>
<string name="disable_dict" msgid="7685810040236497700">"停用"</string>
<string name="enable_dict" msgid="3848179784702473680">"啟用"</string>
<string name="delete_dict" msgid="5817159290503843766">"刪除"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"支援「<xliff:g id="LANGUAGE">%1$s</xliff:g>」字典"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"按下即可查看並下載"</string>
</resources>

View File

@ -170,4 +170,39 @@
<string name="button_default" msgid="3988017840431881491">"Okuzenzakalelayo"</string>
<string name="language_settings" msgid="1671153053201809031">"Ulimi nokokufakwayo"</string>
<string name="select_input_method" msgid="4301602374609275003">"Khetha indlela yokufaka"</string>
<string name="app_name" msgid="1017058186322714405">"Umhlinzeki Wesichazamazwi"</string>
<string name="dictionary_provider_name" msgid="7710415599371161092">"Umhlinzeki Wesichazamazwi"</string>
<string name="dictionary_service_name" msgid="551650697348202056">"Insiza yesichazamazwi"</string>
<string name="download_description" msgid="3274861514695032954">"Ulwazi lokubuyekeza isichazamazwi"</string>
<string name="dictionary_settings_title" msgid="7243930967845020407">"Faka izichazamazwi"</string>
<string name="dictionary_install_over_metered_network_prompt" msgid="3642634623465349716">"Isichazamazwi siyatholakala"</string>
<string name="dictionary_settings_summary" msgid="8599679434799749053">"Izilungiselelo zezichazamazwi"</string>
<string name="user_dictionaries" msgid="7519736232423929124">"Sebenzisa isichazamazwi"</string>
<string name="default_user_dict_pref_name" msgid="522125152757607790">"Isichazamazwi Somsebenzisi"</string>
<string name="dictionary_available" msgid="3192920608520618083">"Isichazamazwi siyatholakala"</string>
<string name="dictionary_downloading" msgid="859497476266309596">"Okwamanje iyalayisha"</string>
<string name="dictionary_installed" msgid="6425586899671378160">"Kufakiwe"</string>
<string name="dictionary_disabled" msgid="3448571280490746032">"Kufakiwe, kumisiwe"</string>
<string name="cannot_connect_to_dict_service" msgid="6875665494726300427">"Inkinga yokuxhumaniseka esevisini yesichazamazwi"</string>
<string name="no_dictionaries_available" msgid="5206225077945637810">"Azikho izachazimazwi ezikhona"</string>
<string name="check_for_updates_now" msgid="642057986127624986">"Vuselela"</string>
<string name="last_update" msgid="3101549719827600346">"Igcine ukulungiswa:"</string>
<string name="message_updating" msgid="820186276704134720">"Ihlola izibuyekezo..."</string>
<string name="message_loading" msgid="8611339149825047446">"Kuyalayisha..."</string>
<string name="main_dict_description" msgid="1679964306980098570">"Isichazimazwi sakho ngqangi"</string>
<string name="cancel" msgid="5586531736609183304">"Khansela"</string>
<string name="install_dict" msgid="5525005524697607865">"Faka"</string>
<string name="cancel_download_dict" msgid="7163173650298838367">"Khansela ukulayisha"</string>
<string name="disable_dict" msgid="7685810040236497700">"Yenza kungasebenzi"</string>
<string name="enable_dict" msgid="3848179784702473680">"Vumela"</string>
<string name="delete_dict" msgid="5817159290503843766">"Susa"</string>
<!-- no translation found for should_download_over_metered_prompt (4965264849057656521) -->
<skip />
<!-- no translation found for download_over_metered (4024013764937850061) -->
<skip />
<!-- no translation found for do_not_download_over_metered (6963770885033765378) -->
<skip />
<string name="dict_available_notification_title" msgid="4560576379680660047">"Isichazamazwi se-<xliff:g id="LANGUAGE">%1$s</xliff:g> siyatholakala"</string>
<string name="dict_available_notification_description" msgid="355515381285317832">"Cindezela ukuze ubuyekeze bese ulanda"</string>
</resources>

View File

@ -123,4 +123,13 @@
4 = ?
-->
<integer name="log_screen_metrics">0</integer>
<!-- Settings for the dictionary pack -->
<bool name="allow_over_metered">false</bool>
<bool name="allow_over_roaming">false</bool>
<bool name="dict_downloads_visible_in_download_UI">false</bool>
<bool name="metadata_downloads_visible_in_download_UI">false</bool>
<bool name="display_notification_for_auto_update">false</bool>
<bool name="display_notification_for_user_requested_update">false</bool>
</resources>

View File

@ -210,8 +210,12 @@
<item>qwerty</item>
</string-array>
<!-- dictionary pack package name /settings activity (for shared prefs and settings) -->
<string name="dictionary_pack_package_name">com.google.android.inputmethod.latin.dictionarypack</string>
<string name="dictionary_pack_settings_activity">com.google.android.inputmethod.latin.dictionarypack.DictionarySettingsActivity</string>
<string name="settings_warning_researcher_mode">Attention! You are using the special keyboard for research purposes.</string>
<!-- dictionary pack settings -->
<string name="dictionary_pack_settings_activity">com.android.inputmethod.dictionarypack.DictionarySettingsActivity</string>
<string name="authority">com.android.inputmethod.dictionarypack.aosp</string>
<string name="default_metadata_uri"></string>
<string name="local_metadata_filename">metadata.json</string>
</resources>

View File

@ -459,4 +459,78 @@
<string name="select_input_method">Choose input method</string>
<!-- Option to show setup wizard icon. [CHAR LIMIT=30]-->
<string name="show_setup_wizard_icon" translatable="false">Show setup wizard icon</string>
<!-- The dictionary provider application name. Visible in Settings/Applications/Manage applications. -->
<string name="app_name">Dictionary Provider</string>
<!-- The dictionary provider ContentProvider name. Visible in Settings/Applications/Running services. -->
<string name="dictionary_provider_name">Dictionary Provider</string>
<!-- The dictionary provider Service name. Visible in Settings/Applications/Running services. -->
<string name="dictionary_service_name">Dictionary Service</string>
<!-- Downloadable dictionaries will get update information through the network. This describes the associated download. -->
<string name="download_description">Dictionary update information</string>
<!-- Title and summary of the dictionary settings screen -->
<string name="dictionary_settings_title">Add-on dictionaries</string>
<!-- Title for the prompt dialog which informs the user that a dictionary is available for the current language and asks to decide whether to download it over 3g -->
<string name="dictionary_install_over_metered_network_prompt">Dictionary available</string>
<string name="dictionary_settings_summary">Settings for dictionaries</string>
<!-- Name of the user dictionaries settings category -->
<string name="user_dictionaries">User dictionaries</string>
<!-- Name for the "user dictionary" preference item when there is only one -->
<string name="default_user_dict_pref_name">User dictionary</string>
<!-- Message about some dictionary indicating it can be downloaded, but hasn't been yet -->
<string name="dictionary_available">Dictionary available</string>
<!-- Message about some dictionary indicating it is downloading and should be available soon -->
<string name="dictionary_downloading">Currently downloading</string>
<!-- Message about some dictionary indicating it is already installed -->
<string name="dictionary_installed">Installed</string>
<!-- Message about some dictionary indicating the file is installed, but the dictionary is disabled -->
<string name="dictionary_disabled">Installed, disabled</string>
<!-- Message to display in the dictionaries setting screen when some error prevented us to list installed dictionaries [CHAR LIMIT=20] -->
<string name="cannot_connect_to_dict_service">Problem connecting to dictionary service</string>
<!-- Message to display in the dictionaries setting screen when we found that no dictionaries are available [CHAR LIMIT=20]-->
<string name="no_dictionaries_available">No dictionaries available</string>
<!-- Title of the options to press to refresh the list (as in, check for updates now) [CHAR_LIMIT=50] -->
<string name="check_for_updates_now">Refresh</string>
<!-- Hint to tell when the data was last updated. Usage : "Last updated [date]", may contain a : or so. [CHAR LIMIT=45] -->
<string name="last_update">Last updated</string>
<!-- Message to display in a dialog box while we are actively updating the word list [CHAR LIMIT=60] -->
<string name="message_updating">Checking for updates</string>
<!-- Message to display while the add-on dictionary list is updating [no space constraints on this, there is plenty of space but shorter is better because it's only on the screen for a second] -->
<string name="message_loading">Loading...</string>
<!-- String to explain this dictionary is the main dictionary for this language [CHAR_LIMIT=30] -->
<string name="main_dict_description">Main dictionary</string>
<!-- Standard message to dismiss a dialog box -->
<string name="cancel">Cancel</string>
<!-- Action to download and install a dictionary [CHAR_LIMIT=15] -->
<string name="install_dict">Install</string>
<!-- Action to cancel the ongoing download of a dictionary file [CHAR_LIMIT=25] -->
<string name="cancel_download_dict">Cancel</string>
<!-- Action to delete a dictionary file [CHAR_LIMIT=15] -->
<string name="delete_dict">Delete</string>
<!-- Message in the popup informing the user a dictionary is available for their language, and asking for a decision to download over their mobile data plan or not. The reason we ask for this is, the data is large and may be downloaded over a paid-per-megabyte connection but a dictionary is also essential to type comfortably, so we ask the user. This only pops in selected cases, when there is no dictionary at all currently, and the only available network seems to be metered. The "Language & input" part should be set to the actual name of the option (message ID 5292716747264442359 in the translation console). [CHAR_LIMIT=700] -->
<string name="should_download_over_metered_prompt">The selected language on your mobile device has an available dictionary.&lt;br/>
We recommend &lt;b>downloading&lt;/b> the <xliff:g id="language" example="English">%1$s</xliff:g> dictionary to improve your typing experience.&lt;br/>
&lt;br/>
The download could take a minute or two over 3G. Charges may apply if you don\'t have an &lt;b>unlimited data plan&lt;/b>.&lt;br/>
If you are not sure which data plan you have, we recommend finding a Wi-Fi connection to start the download automatically.&lt;br/>
&lt;br/>
Tip: You can download and remove dictionaries by going to &lt;b>Language &amp; input&lt;/b> in the &lt;b>Settings&lt;/b> menu of your mobile device.</string>
<string name="download_over_metered">Download now (<xliff:g id="size_in_megabytes" example="0.7">%1$.1f</xliff:g>MB)</string>
<string name="do_not_download_over_metered">Download over Wi-Fi</string>
<!-- The text of the "dictionary available" notification. -->
<string name="dict_available_notification_title">A dictionary is available for <xliff:g id="language" example="English">%1$s</xliff:g></string>
<!-- The small subtext in the "dictionary available" notification. -->
<string name="dict_available_notification_description">Press to review and download</string>
<!-- The text of the toast warning a download is starting automatically to enable suggestions for the selected language [CHAR LIMIT=100] -->
<string name="toast_downloading_suggestions">Downloading: suggestions for <xliff:g id="language" example="English">%1$s</xliff:g> will be ready soon.</string>
</resources>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/dictionary_settings_title"
android:summary="@string/dictionary_settings_summary">
</PreferenceScreen>

View File

@ -59,7 +59,6 @@
android:title="@string/configure_dictionaries_title">
<intent
android:action="android.intent.action.MAIN"
android:targetPackage="@string/dictionary_pack_package_name"
android:targetClass="@string/dictionary_pack_settings_activity">
<extra
android:name="clientId"

View File

@ -0,0 +1,36 @@
/*
* 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.compat;
import android.net.ConnectivityManager;
import java.lang.reflect.Method;
public final class ConnectivityManagerCompatUtils {
// ConnectivityManager#isActiveNetworkMetered() has been introduced
// in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
private static final Method METHOD_isActiveNetworkMetered = CompatUtils.getMethod(
ConnectivityManager.class, "isActiveNetworkMetered");
public static boolean isActiveNetworkMetered(final ConnectivityManager manager) {
return (Boolean)CompatUtils.invoke(manager,
// If the API telling whether the network is metered or not is not available,
// then the closest thing is "if it's a mobile connection".
manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_MOBILE,
METHOD_isActiveNetworkMetered);
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.compat;
import android.app.DownloadManager;
import java.lang.reflect.Method;
public final class DownloadManagerCompatUtils {
// DownloadManager.Request#setAllowedOverMetered() has been introduced
// in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
private static final Method METHOD_setAllowedOverMetered = CompatUtils.getMethod(
DownloadManager.Request.class, "setAllowedOverMetered", Boolean.TYPE);
public static DownloadManager.Request setAllowedOverMetered(
final DownloadManager.Request request, final boolean allowOverMetered) {
return (DownloadManager.Request)CompatUtils.invoke(request,
request /* default return value */, METHOD_setAllowedOverMetered, allowOverMetered);
}
public static final boolean hasSetAllowedOverMetered() {
return null != METHOD_setAllowedOverMetered;
}
}

View File

@ -0,0 +1,641 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.compat.DownloadManagerCompatUtils;
import com.android.inputmethod.latin.R;
import java.util.LinkedList;
import java.util.Queue;
/**
* Object representing an upgrade from one state to another.
*
* This implementation basically encapsulates a list of Runnable objects. In the future
* it may manage dependencies between them. Concretely, it does not use Runnable because the
* actions need an argument.
*/
/*
The state of a word list follows the following scheme.
| ^
MakeAvailable |
| .------------Forget--------'
V |
STATUS_AVAILABLE <-------------------------.
| |
StartDownloadAction FinishDeleteAction
| |
V |
STATUS_DOWNLOADING EnableAction-- STATUS_DELETING
| | ^
InstallAfterDownloadAction | |
| .---------------' StartDeleteAction
| | |
V V |
STATUS_INSTALLED <--EnableAction-- STATUS_DISABLED
--DisableAction-->
It may also be possible that DisableAction or StartDeleteAction or
DownloadAction run when the file is still downloading. This cancels
the download and returns to STATUS_AVAILABLE.
Also, an UpdateDataAction may apply in any state. It does not affect
the state in any way (nor type, local filename, id or version) but
may update other attributes like description or remote filename.
Forget is an DB maintenance action that removes the entry if it is not installed or disabled.
This happens when the word list information disappeared from the server, or when a new version
is available and we should forget about the old one.
*/
public final class ActionBatch {
/**
* A piece of update.
*
* Action is basically like a Runnable that takes an argument.
*/
public interface Action {
/**
* Execute this action NOW.
* @param context the context to get system services, resources, databases
*/
public void execute(final Context context);
}
/**
* An action that starts downloading an available word list.
*/
public static final class StartDownloadAction implements Action {
static final String TAG = "DictionaryProvider:" + StartDownloadAction.class.getSimpleName();
private final String mClientId;
// The data to download. May not be null.
final WordListMetadata mWordList;
final boolean mForceStartNow;
public StartDownloadAction(final String clientId,
final WordListMetadata wordList, final boolean forceStartNow) {
Utils.l("New download action for client ", clientId, " : ", wordList);
mClientId = clientId;
mWordList = wordList;
mForceStartNow = forceStartNow;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "UpdateAction with a null parameter!");
return;
}
Utils.l("Downloading word list");
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
final DownloadManager manager =
(DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (MetadataDbHelper.STATUS_DOWNLOADING == status) {
// The word list is still downloading. Cancel the download and revert the
// word list status to "available".
if (null != manager) {
// DownloadManager is disabled (or not installed?). We can't cancel - there
// is nothing we can do. We still need to mark the entry as available.
manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
}
MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
} else if (MetadataDbHelper.STATUS_AVAILABLE != status) {
// Should never happen
Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : " + status
+ " for an upgrade action. Fall back to download.");
}
// Download it.
Utils.l("Upgrade word list, downloading", mWordList.mRemoteFilename);
// TODO: if DownloadManager is disabled or not installed, download by ourselves
if (null == manager) return;
// This is an upgraded word list: we should download it.
final Uri uri = Uri.parse(mWordList.mRemoteFilename);
final Request request = new Request(uri);
final Resources res = context.getResources();
if (!mForceStartNow) {
if (DownloadManagerCompatUtils.hasSetAllowedOverMetered()) {
final boolean allowOverMetered;
switch (UpdateHandler.getDownloadOverMeteredSetting(context)) {
case UpdateHandler.DOWNLOAD_OVER_METERED_DISALLOWED:
// User said no: don't allow.
allowOverMetered = false;
break;
case UpdateHandler.DOWNLOAD_OVER_METERED_ALLOWED:
// User said yes: allow.
allowOverMetered = true;
break;
default: // UpdateHandler.DOWNLOAD_OVER_METERED_SETTING_UNKNOWN
// Don't know: use the default value from configuration.
allowOverMetered = res.getBoolean(R.bool.allow_over_metered);
}
DownloadManagerCompatUtils.setAllowedOverMetered(request, allowOverMetered);
} else {
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
}
request.setAllowedOverRoaming(res.getBoolean(R.bool.allow_over_roaming));
} // if mForceStartNow, then allow all network types and roaming, which is the default.
request.setTitle(mWordList.mDescription);
request.setNotificationVisibility(
res.getBoolean(R.bool.display_notification_for_auto_update)
? Request.VISIBILITY_VISIBLE : Request.VISIBILITY_HIDDEN);
request.setVisibleInDownloadsUi(
res.getBoolean(R.bool.dict_downloads_visible_in_download_UI));
final long downloadId = UpdateHandler.registerDownloadRequest(manager, request, db,
mWordList.mId, mWordList.mVersion);
Utils.l("Starting download of", uri, "with id", downloadId);
PrivateLog.log("Starting download of " + uri + ", id : " + downloadId, context);
}
}
/**
* An action that updates the database to reflect the status of a newly installed word list.
*/
public static final class InstallAfterDownloadAction implements Action {
static final String TAG = "DictionaryProvider:"
+ InstallAfterDownloadAction.class.getSimpleName();
private final String mClientId;
// The state to upgrade from. May not be null.
final ContentValues mWordListValues;
public InstallAfterDownloadAction(final String clientId,
final ContentValues wordListValues) {
Utils.l("New InstallAfterDownloadAction for client ", clientId, " : ", wordListValues);
mClientId = clientId;
mWordListValues = wordListValues;
}
@Override
public void execute(final Context context) {
if (null == mWordListValues) {
Log.e(TAG, "InstallAfterDownloadAction with a null parameter!");
return;
}
final int status = mWordListValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
final String id = mWordListValues.getAsString(MetadataDbHelper.WORDLISTID_COLUMN);
Log.e(TAG, "Unexpected state of the word list '" + id + "' : " + status
+ " for an InstallAfterDownload action. Bailing out.");
return;
}
Utils.l("Setting word list as installed");
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
MetadataDbHelper.markEntryAsFinishedDownloadingAndInstalled(db, mWordListValues);
}
}
/**
* An action that enables an existing word list.
*/
public static final class EnableAction implements Action {
static final String TAG = "DictionaryProvider:" + EnableAction.class.getSimpleName();
private final String mClientId;
// The state to upgrade from. May not be null.
final WordListMetadata mWordList;
public EnableAction(final String clientId, final WordListMetadata wordList) {
Utils.l("New EnableAction for client ", clientId, " : ", wordList);
mClientId = clientId;
mWordList = wordList;
}
@Override
public void execute(final Context context) {
if (null == mWordList) {
Log.e(TAG, "EnableAction with a null parameter!");
return;
}
Utils.l("Enabling word list");
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_DISABLED != status
&& MetadataDbHelper.STATUS_DELETING != status) {
Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + " : " + status
+ " for an enable action. Cancelling");
return;
}
MetadataDbHelper.markEntryAsEnabled(db, mWordList.mId, mWordList.mVersion);
}
}
/**
* An action that disables a word list.
*/
public static final class DisableAction implements Action {
static final String TAG = "DictionaryProvider:" + DisableAction.class.getSimpleName();
private final String mClientId;
// The word list to disable. May not be null.
final WordListMetadata mWordList;
public DisableAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New Disable action for client ", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "DisableAction with a null word list!");
return;
}
Utils.l("Disabling word list : " + mWordList);
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_INSTALLED == status) {
// Disabling an installed word list
MetadataDbHelper.markEntryAsDisabled(db, mWordList.mId, mWordList.mVersion);
} else {
if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : "
+ status + " for a disable action. Fall back to marking as available.");
}
// The word list is still downloading. Cancel the download and revert the
// word list status to "available".
final DownloadManager manager =
(DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (null != manager) {
// If we can't cancel the download because DownloadManager is not available,
// we still need to mark the entry as available.
manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
}
MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
}
}
}
/**
* An action that makes a word list available.
*/
public static final class MakeAvailableAction implements Action {
static final String TAG = "DictionaryProvider:" + MakeAvailableAction.class.getSimpleName();
private final String mClientId;
// The word list to make available. May not be null.
final WordListMetadata mWordList;
public MakeAvailableAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New MakeAvailable action", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "MakeAvailableAction with a null word list!");
return;
}
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
if (null != MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion)) {
Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
+ " for a makeavailable action. Marking as available anyway.");
}
Utils.l("Making word list available : " + mWordList);
// If mLocalFilename is null, then it's a remote file that hasn't been downloaded
// yet, so we set the local filename to the empty string.
final ContentValues values = MetadataDbHelper.makeContentValues(0,
MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_AVAILABLE,
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
null == mWordList.mLocalFilename ? "" : mWordList.mLocalFilename,
mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
PrivateLog.log("Insert 'available' record for " + mWordList.mDescription
+ " and locale " + mWordList.mLocale, context);
db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
}
}
/**
* An action that marks a word list as pre-installed.
*
* This is almost the same as MakeAvailableAction, as it only inserts a line with parameters
* received from outside.
* Unlike MakeAvailableAction, the parameters are not received from a downloaded metadata file
* but from the client directly; it marks a word list as being "installed" and not "available".
* It also explicitly sets the filename to the empty string, so that we don't try to open
* it on our side.
*/
public static final class MarkPreInstalledAction implements Action {
static final String TAG = "DictionaryProvider:"
+ MarkPreInstalledAction.class.getSimpleName();
private final String mClientId;
// The word list to mark pre-installed. May not be null.
final WordListMetadata mWordList;
public MarkPreInstalledAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New MarkPreInstalled action", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "MarkPreInstalledAction with a null word list!");
return;
}
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
if (null != MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion)) {
Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
+ " for a markpreinstalled action. Marking as preinstalled anyway.");
}
Utils.l("Marking word list preinstalled : " + mWordList);
// This word list is pre-installed : we don't have its file. We should reset
// the local file name to the empty string so that we don't try to open it
// accidentally. The remote filename may be set by the application if it so wishes.
final ContentValues values = MetadataDbHelper.makeContentValues(0,
MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED,
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
"", mWordList.mRemoteFilename, mWordList.mLastUpdate,
mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion,
mWordList.mFormatVersion);
PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription
+ " and locale " + mWordList.mLocale, context);
db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
}
}
/**
* An action that updates information about a word list - description, locale etc
*/
public static final class UpdateDataAction implements Action {
static final String TAG = "DictionaryProvider:" + UpdateDataAction.class.getSimpleName();
private final String mClientId;
final WordListMetadata mWordList;
public UpdateDataAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New UpdateData action for client ", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "UpdateDataAction with a null word list!");
return;
}
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
ContentValues oldValues = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
if (null == oldValues) {
Log.e(TAG, "Trying to update data about a non-existing word list. Bailing out.");
return;
}
Utils.l("Updating data about a word list : " + mWordList);
final ContentValues values = MetadataDbHelper.makeContentValues(
oldValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN),
oldValues.getAsInteger(MetadataDbHelper.TYPE_COLUMN),
oldValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN),
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
oldValues.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN),
mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
PrivateLog.log("Updating record for " + mWordList.mDescription
+ " and locale " + mWordList.mLocale, context);
db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
+ MetadataDbHelper.VERSION_COLUMN + " = ?",
new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
}
}
/**
* An action that deletes the metadata about a word list if possible.
*
* This is triggered when a specific word list disappeared from the server, or when a fresher
* word list is available and the old one was not installed.
* If the word list has not been installed, it's possible to delete its associated metadata.
* Otherwise, the settings are retained so that the user can still administrate it.
*/
public static final class ForgetAction implements Action {
static final String TAG = "DictionaryProvider:" + ForgetAction.class.getSimpleName();
private final String mClientId;
// The word list to remove. May not be null.
final WordListMetadata mWordList;
final boolean mHasNewerVersion;
public ForgetAction(final String clientId, final WordListMetadata wordlist,
final boolean hasNewerVersion) {
Utils.l("New TryRemove action for client ", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
mHasNewerVersion = hasNewerVersion;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "TryRemoveAction with a null word list!");
return;
}
Utils.l("Trying to remove word list : " + mWordList);
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
if (null == values) {
Log.e(TAG, "Trying to update the metadata of a non-existing wordlist. Cancelling.");
return;
}
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (mHasNewerVersion && MetadataDbHelper.STATUS_AVAILABLE != status) {
// If we have a newer version of this word list, we should be here ONLY if it was
// not installed - else we should be upgrading it.
Log.e(TAG, "Unexpected status for forgetting a word list info : " + status
+ ", removing URL to prevent re-download");
}
if (MetadataDbHelper.STATUS_INSTALLED == status
|| MetadataDbHelper.STATUS_DISABLED == status
|| MetadataDbHelper.STATUS_DELETING == status) {
// If it is installed or disabled, then we cannot remove the entry lest the user
// lose the ability to delete the file or otherwise administrate it. We will thus
// leave it as is, but remove the URI from the database since it is not supposed to
// be accessible any more.
// If it is deleting and we don't have a new version, then we have to wait until
// Android Keyboard actually has deleted it before we can remove its metadata.
values.put(MetadataDbHelper.REMOTE_FILENAME_COLUMN, "");
db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
+ MetadataDbHelper.VERSION_COLUMN + " = ?",
new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
} else {
// If it's AVAILABLE or DOWNLOADING or even UNKNOWN, delete the entry.
db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
+ MetadataDbHelper.VERSION_COLUMN + " = ?",
new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
}
}
}
/**
* An action that sets the word list for deletion as soon as possible.
*
* This is triggered when the user requests deletion of a word list. This will mark it as
* deleted in the database, and fire an intent for Android Keyboard to take notice and
* reload its dictionaries right away if it is up. If it is not up now, then it will
* delete the actual file the next time it gets up.
* A file marked as deleted causes the content provider to supply a zero-sized file to
* Android Keyboard, which will overwrite any existing file and provide no words for this
* word list. This is not exactly a "deletion", since there is an actual file which takes up
* a few bytes on the disk, but this allows to override a default dictionary with an empty
* dictionary. This way, there is no need for the user to make a distinction between
* dictionaries installed by default and add-on dictionaries.
*/
public static final class StartDeleteAction implements Action {
static final String TAG = "DictionaryProvider:" + StartDeleteAction.class.getSimpleName();
private final String mClientId;
// The word list to delete. May not be null.
final WordListMetadata mWordList;
public StartDeleteAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New StartDelete action for client ", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "StartDeleteAction with a null word list!");
return;
}
Utils.l("Trying to delete word list : " + mWordList);
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
if (null == values) {
Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
return;
}
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_DISABLED != status) {
Log.e(TAG, "Unexpected status for deleting a word list info : " + status);
}
MetadataDbHelper.markEntryAsDeleting(db, mWordList.mId, mWordList.mVersion);
}
}
/**
* An action that validates a word list as deleted.
*
* This will restore the word list as available if it still is, or remove the entry if
* it is not any more.
*/
public static final class FinishDeleteAction implements Action {
static final String TAG = "DictionaryProvider:" + FinishDeleteAction.class.getSimpleName();
private final String mClientId;
// The word list to delete. May not be null.
final WordListMetadata mWordList;
public FinishDeleteAction(final String clientId, final WordListMetadata wordlist) {
Utils.l("New FinishDelete action for client", clientId, " : ", wordlist);
mClientId = clientId;
mWordList = wordlist;
}
@Override
public void execute(final Context context) {
if (null == mWordList) { // This should never happen
Log.e(TAG, "FinishDeleteAction with a null word list!");
return;
}
Utils.l("Trying to delete word list : " + mWordList);
final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
mWordList.mId, mWordList.mVersion);
if (null == values) {
Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
return;
}
final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_DELETING != status) {
Log.e(TAG, "Unexpected status for finish-deleting a word list info : " + status);
}
final String remoteFilename =
values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
// If there isn't a remote filename any more, then we don't know where to get the file
// from any more, so we remove the entry entirely. As a matter of fact, if the file was
// marked DELETING but disappeared from the metadata on the server, it ended up
// this way.
if (TextUtils.isEmpty(remoteFilename)) {
db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
+ MetadataDbHelper.VERSION_COLUMN + " = ?",
new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
} else {
MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
}
}
}
// An action batch consists of an ordered queue of Actions that can execute.
private final Queue<Action> mActions;
public ActionBatch() {
mActions = new LinkedList<Action>();
}
public void add(final Action a) {
mActions.add(a);
}
/**
* Append all the actions of another action batch.
* @param that the upgrade to merge into this one.
*/
public void append(final ActionBatch that) {
for (final Action a : that.mActions) {
add(a);
}
}
/**
* Execute this batch.
*
* @param context the context for getting resources, databases, system services.
* @param reporter a Reporter to send errors to.
*/
public void execute(final Context context, final ProblemReporter reporter) {
Utils.l("Executing a batch of actions");
Queue<Action> remainingActions = mActions;
while (!remainingActions.isEmpty()) {
final Action a = remainingActions.poll();
try {
a.execute(context);
} catch (Exception e) {
if (null != reporter)
reporter.report(e);
}
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import java.io.File;
/**
* Immutable class to hold the address of an asset.
* As opposed to a normal file, an asset is usually represented as a contiguous byte array in
* the package file. Open it correctly thus requires the name of the package it is in, but
* also the offset in the file and the length of this data. This class encapsulates these three.
*/
final class AssetFileAddress {
public final String mFilename;
public final long mOffset;
public final long mLength;
public AssetFileAddress(final String filename, final long offset, final long length) {
mFilename = filename;
mOffset = offset;
mLength = length;
}
/**
* Makes an AssetFileAddress. This may return null.
*
* @param filename the filename.
* @return the address, or null if the file does not exist or the parameters are not valid.
*/
public static AssetFileAddress makeFromFileName(final String filename) {
if (null == filename) return null;
final File f = new File(filename);
if (!f.isFile()) return null;
return new AssetFileAddress(filename, 0l, f.length());
}
/**
* Makes an AssetFileAddress. This may return null.
*
* @param filename the filename.
* @param offset the offset.
* @param length the length.
* @return the address, or null if the file does not exist or the parameters are not valid.
*/
public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
final long offset, final long length) {
if (null == filename) return null;
final File f = new File(filename);
if (!f.isFile()) return null;
return new AssetFileAddress(filename, offset, length);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
/**
* Exception thrown when the metadata for the dictionary does not comply to a known format.
*/
public final class BadFormatException extends Exception {
public BadFormatException() {
super();
}
public BadFormatException(final String message) {
super(message);
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.Context;
import android.content.SharedPreferences;
public final class CommonPreferences {
private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
public static SharedPreferences getCommonPreferences(final Context context) {
return context.getSharedPreferences(COMMON_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
}
public static void enable(final SharedPreferences pref, final String id) {
final SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(id, true);
editor.apply();
}
public static void disable(final SharedPreferences pref, final String id) {
final SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(id, false);
editor.apply();
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.dictionarypack;
import android.app.DownloadManager;
/**
* Struct class to encapsulate the result of a completed download.
*/
public class CompletedDownloadInfo {
final String mUri;
final long mDownloadId;
final int mStatus;
public CompletedDownloadInfo(final String uri, final long downloadId, final int status) {
mUri = uri;
mDownloadId = downloadId;
mStatus = status;
}
public boolean wasSuccessful() {
return DownloadManager.STATUS_SUCCESSFUL == mStatus;
}
}

View File

@ -0,0 +1,533 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.res.AssetFileDescriptor;
import android.database.AbstractCursor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.latin.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
/**
* Provider for dictionaries.
*
* This class is a ContentProvider exposing all available dictionary data as managed by
* the dictionary pack.
*/
public final class DictionaryProvider extends ContentProvider {
private static final String TAG = DictionaryProvider.class.getSimpleName();
public static final boolean DEBUG = false;
// Authority and URI matching for the ContentProvider protocol.
// TODO: find some way to factorize this string with the one in the resources
public static final String AUTHORITY = "com.android.inputmethod.dictionarypack.aosp";
public static final Uri CONTENT_URI =
Uri.parse(ContentResolver.SCHEME_CONTENT + "://" + AUTHORITY);
private static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt";
private static final String QUERY_PARAMETER_TRUE = "true";
private static final String QUERY_PARAMETER_DELETE_RESULT = "result";
private static final String QUERY_PARAMETER_SUCCESS = "success";
private static final String QUERY_PARAMETER_FAILURE = "failure";
public static final String QUERY_PARAMETER_PROTOCOL_VERSION = "protocol";
private static final int NO_MATCH = 0;
private static final int DICTIONARY_V1_WHOLE_LIST = 1;
private static final int DICTIONARY_V1_DICT_INFO = 2;
private static final int DICTIONARY_V2_METADATA = 3;
private static final int DICTIONARY_V2_WHOLE_LIST = 4;
private static final int DICTIONARY_V2_DICT_INFO = 5;
private static final int DICTIONARY_V2_DATAFILE = 6;
private static final UriMatcher sUriMatcherV1 = new UriMatcher(NO_MATCH);
private static final UriMatcher sUriMatcherV2 = new UriMatcher(NO_MATCH);
static
{
sUriMatcherV1.addURI(AUTHORITY, "list", DICTIONARY_V1_WHOLE_LIST);
sUriMatcherV1.addURI(AUTHORITY, "*", DICTIONARY_V1_DICT_INFO);
sUriMatcherV2.addURI(AUTHORITY, "*/metadata", DICTIONARY_V2_METADATA);
sUriMatcherV2.addURI(AUTHORITY, "*/list", DICTIONARY_V2_WHOLE_LIST);
sUriMatcherV2.addURI(AUTHORITY, "*/dict/*", DICTIONARY_V2_DICT_INFO);
sUriMatcherV2.addURI(AUTHORITY, "*/datafile/*", DICTIONARY_V2_DATAFILE);
}
// MIME types for dictionary and dictionary list, as required by ContentProvider contract.
public static final String DICT_LIST_MIME_TYPE =
"vnd.android.cursor.item/vnd.google.dictionarylist";
public static final String DICT_DATAFILE_MIME_TYPE =
"vnd.android.cursor.item/vnd.google.dictionary";
public static final String ID_CATEGORY_SEPARATOR = ":";
private static final class WordListInfo {
public final String mId;
public final String mLocale;
public final int mMatchLevel;
public WordListInfo(final String id, final String locale, final int matchLevel) {
mId = id;
mLocale = locale;
mMatchLevel = matchLevel;
}
}
/**
* A cursor for returning a list of file ids from a List of strings.
*
* This simulates only the necessary methods. It has no error handling to speak of,
* and does not support everything a database does, only a few select necessary methods.
*/
private static final class ResourcePathCursor extends AbstractCursor {
// Column names for the cursor returned by this content provider.
static private final String[] columnNames = { "id", "locale" };
// The list of word lists served by this provider that match the client request.
final WordListInfo[] mWordLists;
// Note : the cursor also uses mPos, which is defined in AbstractCursor.
public ResourcePathCursor(final Collection<WordListInfo> wordLists) {
// Allocating a 0-size WordListInfo here allows the toArray() method
// to ensure we have a strongly-typed array. It's thrown out. That's
// what the documentation of #toArray says to do in order to get a
// new strongly typed array of the correct size.
mWordLists = wordLists.toArray(new WordListInfo[0]);
mPos = 0;
}
@Override
public String[] getColumnNames() {
return columnNames;
}
@Override
public int getCount() {
return mWordLists.length;
}
@Override public double getDouble(int column) { return 0; }
@Override public float getFloat(int column) { return 0; }
@Override public int getInt(int column) { return 0; }
@Override public short getShort(int column) { return 0; }
@Override public long getLong(int column) { return 0; }
@Override public String getString(final int column) {
switch (column) {
case 0: return mWordLists[mPos].mId;
case 1: return mWordLists[mPos].mLocale;
default : return null;
}
}
@Override
public boolean isNull(final int column) {
if (mPos >= mWordLists.length) return true;
return column != 0;
}
}
@Override
public boolean onCreate() {
return true;
}
private static int matchUri(final Uri uri) {
int protocolVersion = 1;
final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION);
if ("2".equals(protocolVersionArg)) protocolVersion = 2;
switch (protocolVersion) {
case 1: return sUriMatcherV1.match(uri);
case 2: return sUriMatcherV2.match(uri);
default: return NO_MATCH;
}
}
private static String getClientId(final Uri uri) {
int protocolVersion = 1;
final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION);
if ("2".equals(protocolVersionArg)) protocolVersion = 2;
switch (protocolVersion) {
case 1: return null; // In protocol 1, the client ID is always null.
case 2: return uri.getPathSegments().get(0);
default: return null;
}
}
/**
* Returns the MIME type of the content associated with an Uri
*
* @see android.content.ContentProvider#getType(android.net.Uri)
*
* @param uri the URI of the content the type of which should be returned.
* @return the MIME type, or null if the URL is not recognized.
*/
@Override
public String getType(final Uri uri) {
PrivateLog.log("Asked for type of : " + uri, this);
final int match = matchUri(uri);
switch (match) {
case NO_MATCH: return null;
case DICTIONARY_V1_WHOLE_LIST:
case DICTIONARY_V1_DICT_INFO:
case DICTIONARY_V2_WHOLE_LIST:
case DICTIONARY_V2_DICT_INFO: return DICT_LIST_MIME_TYPE;
case DICTIONARY_V2_DATAFILE: return DICT_DATAFILE_MIME_TYPE;
default: return null;
}
}
/**
* Query the provider for dictionary files.
*
* This version dispatches the query according to the protocol version found in the
* ?protocol= query parameter. If absent or not well-formed, it defaults to 1.
* @see android.content.ContentProvider#query(Uri, String[], String, String[], String)
*
* @param uri a content uri (see sUriMatcherV{1,2} at the top of this file for format)
* @param projection ignored. All columns are always returned.
* @param selection ignored.
* @param selectionArgs ignored.
* @param sortOrder ignored. The results are always returned in no particular order.
* @return a cursor matching the uri, or null if the URI was not recognized.
*/
@Override
public Cursor query(final Uri uri, final String[] projection, final String selection,
final String[] selectionArgs, final String sortOrder) {
Utils.l("Uri =", uri);
PrivateLog.log("Query : " + uri, this);
final String clientId = getClientId(uri);
final int match = matchUri(uri);
switch (match) {
case DICTIONARY_V1_WHOLE_LIST:
case DICTIONARY_V2_WHOLE_LIST:
final Cursor c = MetadataDbHelper.queryDictionaries(getContext(), clientId);
Utils.l("List of dictionaries with count", c.getCount());
PrivateLog.log("Returned a list of " + c.getCount() + " items", this);
return c;
case DICTIONARY_V2_DICT_INFO:
// In protocol version 2, we return null if the client is unknown. Otherwise
// we behave exactly like for protocol 1.
if (!MetadataDbHelper.isClientKnown(getContext(), clientId)) return null;
// Fall through
case DICTIONARY_V1_DICT_INFO:
final String locale = uri.getLastPathSegment();
// If LatinIME does not have a dictionary for this locale at all, it will
// send us true for this value. In this case, we may prompt the user for
// a decision about downloading a dictionary even over a metered connection.
final String mayPromptValue =
uri.getQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER);
final boolean mayPrompt = QUERY_PARAMETER_TRUE.equals(mayPromptValue);
final Collection<WordListInfo> dictFiles =
getDictionaryWordListsForLocale(clientId, locale, mayPrompt);
// TODO: pass clientId to the following function
DictionaryService.updateNowIfNotUpdatedInAVeryLongTime(getContext());
if (null != dictFiles && dictFiles.size() > 0) {
PrivateLog.log("Returned " + dictFiles.size() + " files", this);
return new ResourcePathCursor(dictFiles);
} else {
PrivateLog.log("No dictionary files for this URL", this);
return new ResourcePathCursor(Collections.<WordListInfo>emptyList());
}
// V2_METADATA and V2_DATAFILE are not supported for query()
default:
return null;
}
}
/**
* Helper method to get the wordlist metadata associated with a wordlist ID.
*
* @param clientId the ID of the client
* @param wordlistId the ID of the wordlist for which to get the metadata.
* @return the metadata for this wordlist ID, or null if none could be found.
*/
private ContentValues getWordlistMetadataForWordlistId(final String clientId,
final String wordlistId) {
final Context context = getContext();
if (TextUtils.isEmpty(wordlistId)) return null;
final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
return MetadataDbHelper.getInstalledOrDeletingWordListContentValuesByWordListId(
db, wordlistId);
}
/**
* Opens an asset file for an URI.
*
* Called by {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} or
* {@link android.content.ContentResolver#openInputStream(Uri)} from a client requesting a
* dictionary.
* @see android.content.ContentProvider#openAssetFile(Uri, String)
*
* @param uri the URI the file is for.
* @param mode the mode to read the file. MUST be "r" for readonly.
* @return the descriptor, or null if the file is not found or if mode is not equals to "r".
*/
@Override
public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) {
if (null == mode || !"r".equals(mode)) return null;
final int match = matchUri(uri);
if (DICTIONARY_V1_DICT_INFO != match && DICTIONARY_V2_DATAFILE != match) {
// Unsupported URI for openAssetFile
Log.w(TAG, "Unsupported URI for openAssetFile : " + uri);
return null;
}
final String wordlistId = uri.getLastPathSegment();
final String clientId = getClientId(uri);
final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId);
if (null == wordList) return null;
try {
final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
if (MetadataDbHelper.STATUS_DELETING == status) {
// This will return an empty file (R.raw.empty points at an empty dictionary)
// This is how we "delete" the files. It allows Android Keyboard to fake deleting
// a default dictionary - which is actually in its assets and can't be really
// deleted.
final AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(
R.raw.empty);
return afd;
} else {
final String localFilename =
wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final File f = getContext().getFileStreamPath(localFilename);
final ParcelFileDescriptor pfd =
ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
return new AssetFileDescriptor(pfd, 0, pfd.getStatSize());
}
} catch (FileNotFoundException e) {
// No file : fall through and return null
}
return null;
}
/**
* Reads the metadata and returns the collection of dictionaries for a given locale.
*
* Word list IDs are expected to be in the form category:manual_id. This method
* will select only one word list for each category: the one with the most specific
* locale matching the locale specified in the URI. The manual id serves only to
* distinguish a word list from another for the purpose of updating, and is arbitrary
* but may not contain a colon.
*
* @param clientId the ID of the client requesting the list
* @param locale the locale for which we want the list, as a String
* @param mayPrompt true if we are allowed to prompt the user for arbitration via notification
* @return a collection of ids. It is guaranteed to be non-null, but may be empty.
*/
private Collection<WordListInfo> getDictionaryWordListsForLocale(final String clientId,
final String locale, final boolean mayPrompt) {
final Context context = getContext();
final Cursor results =
MetadataDbHelper.queryInstalledOrDeletingOrAvailableDictionaryMetadata(context,
clientId);
if (null == results) {
return Collections.<WordListInfo>emptyList();
} else {
final HashMap<String, WordListInfo> dicts = new HashMap<String, WordListInfo>();
final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
final int localFileNameIndex =
results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final int statusIndex = results.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
if (results.moveToFirst()) {
do {
final String wordListId = results.getString(idIndex);
if (TextUtils.isEmpty(wordListId)) continue;
final String[] wordListIdArray =
TextUtils.split(wordListId, ID_CATEGORY_SEPARATOR);
final String wordListCategory;
if (2 == wordListIdArray.length) {
// This is at the category:manual_id format.
wordListCategory = wordListIdArray[0];
// We don't need to read wordListIdArray[1] here, because it's irrelevant to
// word list selection - it's just a name we use to identify which data file
// is a newer version of which word list. We do however return the full id
// string for each selected word list, so in this sense we are 'using' it.
} else {
// This does not contain a colon, like the old format does. Old-format IDs
// always point to main dictionaries, so we force the main category upon it.
wordListCategory = UpdateHandler.MAIN_DICTIONARY_CATEGORY;
}
final String wordListLocale = results.getString(localeIndex);
final String wordListLocalFilename = results.getString(localFileNameIndex);
final int wordListStatus = results.getInt(statusIndex);
// Test the requested locale against this wordlist locale. The requested locale
// has to either match exactly or be more specific than the dictionary - a
// dictionary for "en" would match both a request for "en" or for "en_US", but a
// dictionary for "en_GB" would not match a request for "en_US". Thus if all
// three of "en" "en_US" and "en_GB" dictionaries are installed, a request for
// "en_US" would match "en" and "en_US", and a request for "en" only would only
// match the generic "en" dictionary. For more details, see the documentation
// for LocaleUtils#getMatchLevel.
final int matchLevel = LocaleUtils.getMatchLevel(wordListLocale, locale);
if (!LocaleUtils.isMatch(matchLevel)) {
// The locale of this wordlist does not match the required locale.
// Skip this wordlist and go to the next.
continue;
}
if (MetadataDbHelper.STATUS_INSTALLED == wordListStatus) {
// If the file does not exist, it has been deleted and the IME should
// already have it. Do not return it. However, this only applies if the
// word list is INSTALLED, for if it is DELETING we should return it always
// so that Android Keyboard can perform the actual deletion.
final File f = getContext().getFileStreamPath(wordListLocalFilename);
if (!f.isFile()) {
continue;
}
} else if (MetadataDbHelper.STATUS_AVAILABLE == wordListStatus) {
// The locale is the id for the main dictionary.
UpdateHandler.installIfNeverRequested(context, clientId, wordListId,
mayPrompt);
continue;
}
final WordListInfo currentBestMatch = dicts.get(wordListCategory);
if (null == currentBestMatch
|| currentBestMatch.mMatchLevel < matchLevel) {
dicts.put(wordListCategory,
new WordListInfo(wordListId, wordListLocale, matchLevel));
}
} while (results.moveToNext());
}
results.close();
return Collections.unmodifiableCollection(dicts.values());
}
}
/**
* Deletes the file pointed by Uri, as returned by openAssetFile.
*
* @param uri the URI the file is for.
* @param selection ignored
* @param selectionArgs ignored
* @return the number of files deleted (0 or 1 in the current implementation)
* @see android.content.ContentProvider#delete(Uri, String, String[])
*/
@Override
public int delete(final Uri uri, final String selection, final String[] selectionArgs)
throws UnsupportedOperationException {
final int match = matchUri(uri);
if (DICTIONARY_V1_DICT_INFO == match || DICTIONARY_V2_DATAFILE == match) {
return deleteDataFile(uri);
}
if (DICTIONARY_V2_METADATA == match) {
if (MetadataDbHelper.deleteClient(getContext(), getClientId(uri))) {
return 1;
}
return 0;
}
// Unsupported URI for delete
return 0;
}
private int deleteDataFile(final Uri uri) {
final String wordlistId = uri.getLastPathSegment();
final String clientId = getClientId(uri);
final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId);
if (null == wordList) return 0;
final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
final int version = wordList.getAsInteger(MetadataDbHelper.VERSION_COLUMN);
if (MetadataDbHelper.STATUS_DELETING == status) {
UpdateHandler.markAsDeleted(getContext(), clientId, wordlistId, version, status);
return 1;
} else if (MetadataDbHelper.STATUS_INSTALLED == status) {
final String result = uri.getQueryParameter(QUERY_PARAMETER_DELETE_RESULT);
if (QUERY_PARAMETER_FAILURE.equals(result)) {
UpdateHandler.markAsBroken(getContext(), clientId, wordlistId, version);
}
final String localFilename =
wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final File f = getContext().getFileStreamPath(localFilename);
// f.delete() returns true if the file was successfully deleted, false otherwise
if (f.delete()) {
return 1;
} else {
return 0;
}
} else {
Log.e(TAG, "Attempt to delete a file whose status is " + status);
return 0;
}
}
/**
* Insert data into the provider. May be either a metadata source URL or some dictionary info.
*
* @param uri the designated content URI. See sUriMatcherV{1,2} for available URIs.
* @param values the values to insert for this content uri
* @return the URI for the newly inserted item. May be null if arguments don't allow for insert
*/
@Override
public Uri insert(final Uri uri, final ContentValues values)
throws UnsupportedOperationException {
if (null == uri || null == values) return null; // Should never happen but let's be safe
PrivateLog.log("Insert, uri = " + uri.toString(), this);
final String clientId = getClientId(uri);
switch (matchUri(uri)) {
case DICTIONARY_V2_METADATA:
// The values should contain a valid client ID and a valid URI for the metadata.
// The client ID may not be null, nor may it be empty because the empty client ID
// is reserved for internal use.
// The metadata URI may not be null, but it may be empty if the client does not
// want the dictionary pack to update the metadata automatically.
MetadataDbHelper.updateClientInfo(getContext(), clientId, values);
break;
case DICTIONARY_V2_DICT_INFO:
try {
final WordListMetadata newDictionaryMetadata =
WordListMetadata.createFromContentValues(
MetadataDbHelper.completeWithDefaultValues(values));
new ActionBatch.MarkPreInstalledAction(clientId, newDictionaryMetadata)
.execute(getContext());
} catch (final BadFormatException e) {
Log.w(TAG, "Not enough information to insert this dictionary " + values, e);
}
break;
case DICTIONARY_V1_WHOLE_LIST:
case DICTIONARY_V1_DICT_INFO:
PrivateLog.log("Attempt to insert : " + uri, this);
throw new UnsupportedOperationException(
"Insertion in the dictionary is not supported in this version");
}
return uri;
}
/**
* Updating data is not supported, and will throw an exception.
* @see android.content.ContentProvider#update(Uri, ContentValues, String, String[])
* @see android.content.ContentProvider#insert(Uri, ContentValues)
*/
@Override
public int update(final Uri uri, final ContentValues values, final String selection,
final String[] selectionArgs) throws UnsupportedOperationException {
PrivateLog.log("Attempt to update : " + uri, this);
throw new UnsupportedOperationException("Updating dictionary words is not supported");
}
}

View File

@ -0,0 +1,242 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.text.format.DateUtils;
import android.util.Log;
import android.widget.Toast;
import com.android.inputmethod.latin.R;
import java.util.Locale;
import java.util.Random;
/**
* Service that handles background tasks for the dictionary provider.
*
* This service provides the context for the long-running operations done by the
* dictionary provider. Those include:
* - Checking for the last update date and scheduling the next update. This runs every
* day around midnight, upon reception of the DATE_CHANGED_INTENT_ACTION broadcast.
* Every four days, it schedules an update of the metadata with the alarm manager.
* - Issuing the order to update the metadata. This runs every four days, between 0 and
* 6, upon reception of the UPDATE_NOW_INTENT_ACTION broadcast sent by the alarm manager
* as a result of the above action.
* - Handling a download that just ended. These come in two flavors:
* - Metadata is finished downloading. We should check whether there are new dictionaries
* available, and download those that we need that have new versions.
* - A dictionary file finished downloading. We should put the file ready for a client IME
* to access, and mark the current state as such.
*/
public final class DictionaryService extends Service {
private static final String TAG = DictionaryService.class.getName();
/**
* The package name, to use in the intent actions.
*/
private static final String PACKAGE_NAME = "com.android.android.inputmethod.latin";
/**
* The action of the intent to tell the dictionary provider to update now.
*/
private static final String UPDATE_NOW_INTENT_ACTION = PACKAGE_NAME + ".UPDATE_NOW";
/**
* The action of the date changing, used to schedule a periodic freshness check
*/
private static final String DATE_CHANGED_INTENT_ACTION =
Intent.ACTION_DATE_CHANGED;
/**
* The action of displaying a toast to warn the user an automatic download is starting.
*/
/* package */ static final String SHOW_DOWNLOAD_TOAST_INTENT_ACTION =
PACKAGE_NAME + ".SHOW_DOWNLOAD_TOAST_INTENT_ACTION";
/**
* A locale argument, as a String.
*/
/* package */ static final String LOCALE_INTENT_ARGUMENT = "locale";
/**
* How often, in milliseconds, we want to update the metadata. This is a
* floor value; actually, it may happen several hours later, or even more.
*/
private static final long UPDATE_FREQUENCY = 4 * DateUtils.DAY_IN_MILLIS;
/**
* We are waked around midnight, local time. We want to wake between midnight and 6 am,
* roughly. So use a random time between 0 and this delay.
*/
private static final int MAX_ALARM_DELAY = 6 * ((int)AlarmManager.INTERVAL_HOUR);
/**
* How long we consider a "very long time". If no update took place in this time,
* the content provider will trigger an update in the background.
*/
private static final long VERY_LONG_TIME = 14 * DateUtils.DAY_IN_MILLIS;
/**
* The last seen start Id. This must be stored because we must only call stopSelfResult() with
* the last seen Id, or the service won't stop.
*/
private int mLastSeenStartId;
/**
* The command count. We need this because we need to not call stopSelfResult() while we still
* have commands running.
*/
private int mCommandCount;
@Override
public void onCreate() {
mLastSeenStartId = 0;
mCommandCount = 0;
}
@Override
public void onDestroy() {
}
@Override
public IBinder onBind(Intent intent) {
// This service cannot be bound
return null;
}
/**
* Executes an explicit command.
*
* This is the entry point for arbitrary commands that are executed upon reception of certain
* events that should be executed on the context of this service. The supported commands are:
* - Check last update time and possibly schedule an update of the data for later.
* This is triggered every day, upon reception of the DATE_CHANGED_INTENT_ACTION broadcast.
* - Update data NOW.
* This is normally received upon trigger of the scheduled update.
* - Handle a finished download.
* This executes the actions that must be taken after a file (metadata or dictionary data
* has been downloaded (or failed to download).
*/
@Override
public synchronized int onStartCommand(final Intent intent, final int flags,
final int startId) {
final DictionaryService self = this;
mLastSeenStartId = startId;
mCommandCount += 1;
if (SHOW_DOWNLOAD_TOAST_INTENT_ACTION.equals(intent.getAction())) {
// This is a UI action, it can't be run in another thread
showStartDownloadingToast(this, LocaleUtils.constructLocaleFromString(
intent.getStringExtra(LOCALE_INTENT_ARGUMENT)));
} else {
// If it's a command that does not require UI, create a thread to do the work
// and return right away. DATE_CHANGED or UPDATE_NOW are examples of such commands.
new Thread("updateOrFinishDownload") {
@Override
public void run() {
dispatchBroadcast(self, intent);
synchronized(self) {
if (--mCommandCount <= 0) {
if (!stopSelfResult(mLastSeenStartId)) {
Log.e(TAG, "Can't stop ourselves");
}
}
}
}
}.start();
}
return Service.START_REDELIVER_INTENT;
}
private static void dispatchBroadcast(final Context context, final Intent intent) {
if (DATE_CHANGED_INTENT_ACTION.equals(intent.getAction())) {
// This happens when the date of the device changes. This normally happens
// at midnight local time, but it may happen if the user changes the date
// by hand or something similar happens.
checkTimeAndMaybeSetupUpdateAlarm(context);
} else if (UPDATE_NOW_INTENT_ACTION.equals(intent.getAction())) {
// Intent to trigger an update now.
UpdateHandler.update(context, false);
} else {
UpdateHandler.downloadFinished(context, intent);
}
}
/**
* Setups an alarm to check for updates if an update is due.
*/
private static void checkTimeAndMaybeSetupUpdateAlarm(final Context context) {
// Of all clients, if the one that hasn't been updated for the longest
// is still more recent than UPDATE_FREQUENCY, do nothing.
if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY)) return;
PrivateLog.log("Date changed - registering alarm", context);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
// Best effort to wake between midnight and MAX_ALARM_DELAY in the morning.
// It doesn't matter too much if this is very inexact.
final long now = System.currentTimeMillis();
final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY);
final Intent updateIntent = new Intent(DictionaryService.UPDATE_NOW_INTENT_ACTION);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
updateIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// We set the alarm in the type that doesn't forcefully wake the device
// from sleep, but fires the next time the device actually wakes for any
// other reason.
if (null != alarmManager) alarmManager.set(AlarmManager.RTC, alarmTime, pendingIntent);
}
/**
* Utility method to decide whether the last update is older than a certain time.
*
* @return true if at least `time' milliseconds have elapsed since last update, false otherwise.
*/
private static boolean isLastUpdateAtLeastThisOld(final Context context, final long time) {
final long now = System.currentTimeMillis();
final long lastUpdate = MetadataDbHelper.getOldestUpdateTime(context);
PrivateLog.log("Last update was " + lastUpdate, context);
return lastUpdate + time < now;
}
/**
* Refreshes data if it hasn't been refreshed in a very long time.
*
* This will check the last update time, and if it's been more than VERY_LONG_TIME,
* update metadata now - and possibly take subsequent update actions.
*/
public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) {
if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return;
UpdateHandler.update(context, false);
}
/**
* Shows a toast informing the user that an automatic dictionary download is starting.
*/
private static void showStartDownloadingToast(final Context context, final Locale locale) {
final String toastText = String.format(
context.getString(R.string.toast_downloading_suggestions),
locale.getDisplayName());
Toast.makeText(context, toastText, Toast.LENGTH_LONG).show();
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
/**
* Preference screen.
*/
public final class DictionarySettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public Intent getIntent() {
final Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DictionarySettingsFragment.class.getName());
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;
}
}

View File

@ -0,0 +1,365 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.android.inputmethod.dictionarypack;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.animation.AnimationUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.android.inputmethod.latin.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.TreeMap;
/**
* Preference screen.
*/
public final class DictionarySettingsFragment extends PreferenceFragment
implements UpdateHandler.UpdateEventListener {
private static final String TAG = DictionarySettingsFragment.class.getSimpleName();
static final private String DICT_LIST_ID = "list";
static final public String DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT = "clientId";
static final private int MENU_UPDATE_NOW = Menu.FIRST;
private View mLoadingView;
private String mClientId;
private ConnectivityManager mConnectivityManager;
private MenuItem mUpdateNowMenu;
private boolean mChangedSettings;
private final BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
refreshNetworkState();
}
};
/**
* Empty constructor for fragment generation.
*/
public DictionarySettingsFragment() {
}
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.loading_page, container, true);
mLoadingView = v.findViewById(R.id.loading_container);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Activity activity = getActivity();
mClientId = activity.getIntent().getStringExtra(DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT);
mConnectivityManager =
(ConnectivityManager)activity.getSystemService(Context.CONNECTIVITY_SERVICE);
addPreferencesFromResource(R.xml.dictionary_settings);
refreshInterface();
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
mUpdateNowMenu = menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now);
mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
refreshNetworkState();
}
@Override
public void onResume() {
super.onResume();
mChangedSettings = false;
UpdateHandler.registerUpdateEventListener(this);
final IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
getActivity().registerReceiver(mConnectivityChangedReceiver, filter);
refreshNetworkState();
}
@Override
public void onPause() {
super.onPause();
final Activity activity = getActivity();
UpdateHandler.unregisterUpdateEventListener(this);
activity.unregisterReceiver(mConnectivityChangedReceiver);
if (mChangedSettings) {
final Intent newDictBroadcast = new Intent(UpdateHandler.NEW_DICTIONARY_INTENT_ACTION);
activity.sendBroadcast(newDictBroadcast);
mChangedSettings = false;
}
}
public void downloadedMetadata(final boolean succeeded) {
stopLoadingAnimation();
if (!succeeded) return; // If the download failed nothing changed, so no need to refresh
new Thread("refreshInterface") {
@Override
public void run() {
refreshInterface();
}
}.start();
}
public void wordListDownloadFinished(final String wordListId, final boolean succeeded) {
final WordListPreference pref = findWordListPreference(wordListId);
if (null == pref) return;
// TODO: Report to the user if !succeeded
final Activity activity = getActivity();
if (null == activity) return;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// We have to re-read the db in case the description has changed, and to
// find out what state it ended up if the download wasn't successful
// TODO: don't redo everything, only re-read and set this word list status
refreshInterface();
}
});
}
private WordListPreference findWordListPreference(final String id) {
final PreferenceGroup prefScreen = getPreferenceScreen();
if (null == prefScreen) {
Log.e(TAG, "Could not find the preference group");
return null;
}
for (int i = prefScreen.getPreferenceCount() - 1; i >= 0; --i) {
final Preference pref = prefScreen.getPreference(i);
if (pref instanceof WordListPreference) {
final WordListPreference wlPref = (WordListPreference)pref;
if (id.equals(wlPref.mWordlistId)) {
return wlPref;
}
}
}
Log.e(TAG, "Could not find the preference for a word list id " + id);
return null;
}
public void updateCycleCompleted() {}
private void refreshNetworkState() {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
boolean isConnected = null == info ? false : info.isConnected();
if (null != mUpdateNowMenu) mUpdateNowMenu.setEnabled(isConnected);
}
private void refreshInterface() {
final Activity activity = getActivity();
if (null == activity) return;
final long lastUpdateDate =
MetadataDbHelper.getLastUpdateDateForClient(getActivity(), mClientId);
final PreferenceGroup prefScreen = getPreferenceScreen();
final Collection<? extends Preference> prefList =
createInstalledDictSettingsCollection(mClientId);
final String updateNowSummary = getString(R.string.last_update) + " "
+ DateUtils.formatDateTime(activity, lastUpdateDate,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO: display this somewhere
// if (0 != lastUpdate) mUpdateNowPreference.setSummary(updateNowSummary);
refreshNetworkState();
removeAnyDictSettings(prefScreen);
for (Preference preference : prefList) {
prefScreen.addPreference(preference);
}
}
});
}
private Preference createErrorMessage(final Activity activity, final int messageResource) {
final Preference message = new Preference(activity);
message.setTitle(messageResource);
message.setEnabled(false);
return message;
}
private void removeAnyDictSettings(final PreferenceGroup prefGroup) {
for (int i = prefGroup.getPreferenceCount() - 1; i >= 0; --i) {
prefGroup.removePreference(prefGroup.getPreference(i));
}
}
/**
* Creates a WordListPreference list to be added to the screen.
*
* This method only creates the preferences but does not add them.
* Thus, it can be called on another thread.
*
* @param clientId the id of the client for which we want to display the dictionary list
* @return A collection of preferences ready to add to the interface.
*/
private Collection<? extends Preference> createInstalledDictSettingsCollection(
final String clientId) {
// This will directly contact the DictionaryProvider and request the list exactly like
// any regular client would do.
// Considering the respective value of the respective constants used here for each path,
// segment, the url generated by this is of the form (assuming "clientId" as a clientId)
// content://com.android.inputmethod.latin.dictionarypack/clientId/list?procotol=2
final Uri contentUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(getString(R.string.authority))
.appendPath(clientId)
.appendPath(DICT_LIST_ID)
// Need to use version 2 to get this client's list
.appendQueryParameter(DictionaryProvider.QUERY_PARAMETER_PROTOCOL_VERSION, "2")
.build();
final Activity activity = getActivity();
final Cursor cursor = null == activity ? null
: activity.getContentResolver().query(contentUri, null, null, null, null);
if (null == cursor) {
final ArrayList<Preference> result = new ArrayList<Preference>();
result.add(createErrorMessage(activity, R.string.cannot_connect_to_dict_service));
return result;
} else if (!cursor.moveToFirst()) {
final ArrayList<Preference> result = new ArrayList<Preference>();
result.add(createErrorMessage(activity, R.string.no_dictionaries_available));
return result;
} else {
final String systemLocaleString = Locale.getDefault().toString();
final TreeMap<String, WordListPreference> prefList =
new TreeMap<String, WordListPreference>();
final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
final int descriptionIndex = cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
do {
final String wordlistId = cursor.getString(idIndex);
final int version = cursor.getInt(versionIndex);
final String localeString = cursor.getString(localeIndex);
final Locale locale = new Locale(localeString);
final String description = cursor.getString(descriptionIndex);
final int status = cursor.getInt(statusIndex);
final int matchLevel = LocaleUtils.getMatchLevel(systemLocaleString, localeString);
final String matchLevelString = LocaleUtils.getMatchLevelSortedString(matchLevel);
// The key is sorted in lexicographic order, according to the match level, then
// the description.
final String key = matchLevelString + "." + description + "." + wordlistId;
final WordListPreference existingPref = prefList.get(key);
if (null == existingPref || hasPriority(status, existingPref.mStatus)) {
final WordListPreference pref = new WordListPreference(activity, mClientId,
wordlistId, version, locale, description, status);
prefList.put(key, pref);
}
} while (cursor.moveToNext());
return prefList.values();
}
}
/**
* Finds out if a given status has priority over another for display order.
*
* @param newStatus
* @param oldStatus
* @return whether newStatus has priority over oldStatus.
*/
private static boolean hasPriority(final int newStatus, final int oldStatus) {
// Both of these should be one of MetadataDbHelper.STATUS_*
return newStatus > oldStatus;
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case MENU_UPDATE_NOW:
if (View.GONE == mLoadingView.getVisibility()) {
startRefresh();
} else {
cancelRefresh();
}
return true;
}
return false;
}
private void startRefresh() {
startLoadingAnimation();
mChangedSettings = true;
UpdateHandler.registerUpdateEventListener(this);
final Activity activity = getActivity();
new Thread("updateByHand") {
@Override
public void run() {
UpdateHandler.update(activity, true);
}
}.start();
}
private void cancelRefresh() {
UpdateHandler.unregisterUpdateEventListener(this);
final Context context = getActivity();
UpdateHandler.cancelUpdate(context,
MetadataDbHelper.getMetadataUriAsString(context, mClientId));
stopLoadingAnimation();
}
private void startLoadingAnimation() {
mLoadingView.setVisibility(View.VISIBLE);
getView().setVisibility(View.GONE);
mUpdateNowMenu.setTitle(R.string.cancel);
}
private void stopLoadingAnimation() {
final View preferenceView = getView();
final Activity activity = getActivity();
if (null == activity) return;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
mLoadingView.setVisibility(View.GONE);
preferenceView.setVisibility(View.VISIBLE);
mLoadingView.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_out));
preferenceView.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_in));
mUpdateNowMenu.setTitle(R.string.check_for_updates_now);
}
});
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.inputmethod.latin.R;
import java.util.Locale;
/**
* This implements the dialog for asking the user whether it's okay to download dictionaries over
* a metered connection or not (e.g. their mobile data plan).
*/
public final class DownloadOverMeteredDialog extends Activity {
final public static String CLIENT_ID_KEY = "client_id";
final public static String WORDLIST_TO_DOWNLOAD_KEY = "wordlist_to_download";
final public static String SIZE_KEY = "size";
final public static String LOCALE_KEY = "locale";
private String mClientId;
private String mWordListToDownload;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
mClientId = intent.getStringExtra(CLIENT_ID_KEY);
mWordListToDownload = intent.getStringExtra(WORDLIST_TO_DOWNLOAD_KEY);
final String localeString = intent.getStringExtra(LOCALE_KEY);
final long size = intent.getIntExtra(SIZE_KEY, 0);
setContentView(R.layout.download_over_metered);
setTexts(localeString, size);
}
private void setTexts(final String localeString, final long size) {
final String promptFormat = getString(R.string.should_download_over_metered_prompt);
final String allowButtonFormat = getString(R.string.download_over_metered);
final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
final String language = (null == locale ? "" : locale.getDisplayLanguage());
final TextView prompt = (TextView)findViewById(R.id.download_over_metered_prompt);
prompt.setText(Html.fromHtml(String.format(promptFormat, language)));
final Button allowButton = (Button)findViewById(R.id.allow_button);
allowButton.setText(String.format(allowButtonFormat, ((float)size)/(1024*1024)));
}
public void onClickDeny(final View v) {
UpdateHandler.setDownloadOverMeteredSetting(this, false);
finish();
}
public void onClickAllow(final View v) {
UpdateHandler.setDownloadOverMeteredSetting(this, true);
UpdateHandler.installIfNeverRequested(this, mClientId, mWordListToDownload,
false /* mayPrompt */);
finish();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.ContentValues;
/**
* Struct class to encapsulate a client ID with content values about a download.
*/
public class DownloadRecord {
public final String mClientId;
// Only word lists have attributes, and the ContentValues should contain the same
// keys as they do for all MetadataDbHelper functions. Since only word lists have
// attributes, a null pointer here means this record represents metadata.
public final ContentValues mAttributes;
public DownloadRecord(final String clientId, final ContentValues attributes) {
mClientId = clientId;
mAttributes = attributes;
}
public boolean isMetadata() {
return null == mAttributes;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.R;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public final class EventHandler extends BroadcastReceiver {
private static final String TAG = EventHandler.class.getName();
/**
* Receives a intent broadcast.
*
* We receive every day a broadcast indicating that date changed.
* Then we wait a random amount of time before actually registering
* the download, to avoid concentrating too many accesses around
* midnight in more populated timezones.
* We receive all broadcasts here, so this can be either the DATE_CHANGED broadcast, the
* UPDATE_NOW private broadcast that we receive when the time-randomizing alarm triggers
* for regular update or from applications that want to test the dictionary pack, or a
* broadcast from DownloadManager telling that a download has finished.
* See inside of AndroidManifest.xml to see which events are caught.
* Also @see {@link BroadcastReceiver#onReceive(Context, Intent)}
*
* @param context the context of the application.
* @param intent the intent that was broadcast.
*/
@Override
public void onReceive(final Context context, final Intent intent) {
intent.setClass(context, DictionaryService.class);
context.startService(intent);
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.text.TextUtils;
import java.util.HashMap;
import java.util.Locale;
/**
* A class to help with handling Locales in string form.
*
* This file has the same meaning and features (and shares all of its code) with the one with the
* same name in Latin IME. They need to be kept synchronized; for any update/bugfix to
* this file, consider also updating/fixing the version in Latin IME.
*/
public final class LocaleUtils {
private LocaleUtils() {
// Intentional empty constructor for utility class.
}
// Locale match level constants.
// A higher level of match is guaranteed to have a higher numerical value.
// Some room is left within constants to add match cases that may arise necessary
// in the future, for example differentiating between the case where the countries
// are both present and different, and the case where one of the locales does not
// specify the countries. This difference is not needed now.
// Nothing matches.
public static final int LOCALE_NO_MATCH = 0;
// The languages matches, but the country are different. Or, the reference locale requires a
// country and the tested locale does not have one.
public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3;
// The languages and country match, but the variants are different. Or, the reference locale
// requires a variant and the tested locale does not have one.
public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6;
// The required locale is null or empty so it will accept anything, and the tested locale
// is non-null and non-empty.
public static final int LOCALE_ANY_MATCH = 10;
// The language matches, and the tested locale specifies a country but the reference locale
// does not require one.
public static final int LOCALE_LANGUAGE_MATCH = 15;
// The language and the country match, and the tested locale specifies a variant but the
// reference locale does not require one.
public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20;
// The compared locales are fully identical. This is the best match level.
public static final int LOCALE_FULL_MATCH = 30;
// The level at which a match is "normally" considered a locale match with standard algorithms.
// Don't use this directly, use #isMatch to test.
private static final int LOCALE_MATCH = LOCALE_ANY_MATCH;
// Make this match the maximum match level. If this evolves to have more than 2 digits
// when written in base 10, also adjust the getMatchLevelSortedString method.
private static final int MATCH_LEVEL_MAX = 30;
/**
* Return how well a tested locale matches a reference locale.
*
* This will check the tested locale against the reference locale and return a measure of how
* a well it matches the reference. The general idea is that the tested locale has to match
* every specified part of the required locale. A full match occur when they are equal, a
* partial match when the tested locale agrees with the reference locale but is more specific,
* and a difference when the tested locale does not comply with all requirements from the
* reference locale.
* In more detail, if the reference locale specifies at least a language and the testedLocale
* does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the
* reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH
* if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and
* tested locale agree on the language, but not on the country,
* LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country,
* and LOCALE_LANGUAGE_MATCH otherwise.
* If they agree on both the language and the country, but not on the variant,
* LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale
* specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches,
* LOCALE_FULL_MATCH is returned.
* Examples:
* en <=> en_US => LOCALE_LANGUAGE_MATCH
* en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
* en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
* en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH
* sp_US <=> en_US => LOCALE_NO_MATCH
* de <=> de => LOCALE_FULL_MATCH
* en_US <=> en_US => LOCALE_FULL_MATCH
* "" <=> en_US => LOCALE_ANY_MATCH
*
* @param referenceLocale the reference locale to test against.
* @param testedLocale the locale to test.
* @return a constant that measures how well the tested locale matches the reference locale.
*/
public static int getMatchLevel(final String referenceLocale, final String testedLocale) {
if (TextUtils.isEmpty(referenceLocale)) {
return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
}
if (null == testedLocale) return LOCALE_NO_MATCH;
final String[] referenceParams = referenceLocale.split("_", 3);
final String[] testedParams = testedLocale.split("_", 3);
// By spec of String#split, [0] cannot be null and length cannot be 0.
if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH;
switch (referenceParams.length) {
case 1:
return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH;
case 2:
if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
if (!referenceParams[1].equals(testedParams[1]))
return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH;
return LOCALE_FULL_MATCH;
case 3:
if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
if (!referenceParams[1].equals(testedParams[1]))
return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
if (!referenceParams[2].equals(testedParams[2]))
return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
return LOCALE_FULL_MATCH;
}
// It should be impossible to come here
return LOCALE_NO_MATCH;
}
/**
* Return a string that represents this match level, with better matches first.
*
* The strings are sorted in lexicographic order: a better match will always be less than
* a worse match when compared together.
*/
public static String getMatchLevelSortedString(final int matchLevel) {
// This works because the match levels are 0~99 (actually 0~30)
// Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel
return String.format("%02d", MATCH_LEVEL_MAX - matchLevel);
}
/**
* Find out whether a match level should be considered a match.
*
* This method takes a match level as returned by the #getMatchLevel method, and returns whether
* it should be considered a match in the usual sense with standard Locale functions.
*
* @param level the match level, as returned by getMatchLevel.
* @return whether this is a match or not.
*/
public static boolean isMatch(final int level) {
return LOCALE_MATCH <= level;
}
/**
* Sets the system locale for this process.
*
* @param res the resources to use. Pass current resources.
* @param newLocale the locale to change to.
* @return the old locale.
*/
public static Locale setSystemLocale(final Resources res, final Locale newLocale) {
final Configuration conf = res.getConfiguration();
final Locale saveLocale = conf.locale;
conf.locale = newLocale;
res.updateConfiguration(conf, res.getDisplayMetrics());
return saveLocale;
}
private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>();
/**
* Creates a locale from a string specification.
*/
public static Locale constructLocaleFromString(final String localeStr) {
if (localeStr == null)
return null;
synchronized (sLocaleCache) {
if (sLocaleCache.containsKey(localeStr))
return sLocaleCache.get(localeStr);
Locale retval = null;
String[] localeParams = localeStr.split("_", 3);
if (localeParams.length == 1) {
retval = new Locale(localeParams[0]);
} else if (localeParams.length == 2) {
retval = new Locale(localeParams[0], localeParams[1]);
} else if (localeParams.length == 3) {
retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
}
if (retval != null) {
sLocaleCache.put(localeStr, retval);
}
return retval;
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.util.Log;
/**
* A very simple problem reporter.
*/
final class LogProblemReporter implements ProblemReporter {
private final String TAG;
public LogProblemReporter(final String tag) {
TAG = tag;
}
public void report(final Exception e) {
Log.e(TAG, "Reporting problem : " + e);
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import java.io.InputStream;
import java.io.IOException;
import java.security.MessageDigest;
final class MD5Calculator {
private MD5Calculator() {} // This helper class is not instantiable
public static String checksum(final InputStream in) throws IOException {
// This code from the Android documentation for MessageDigest. Nearly verbatim.
MessageDigest digester;
try {
digester = MessageDigest.getInstance("MD5");
} catch (java.security.NoSuchAlgorithmException e) {
return null; // Platform does not support MD5 : can't check, so return null
}
final byte[] bytes = new byte[8192];
int byteCount;
while ((byteCount = in.read(bytes)) > 0) {
digester.update(bytes, 0, byteCount);
}
final byte[] digest = digester.digest();
final StringBuilder s = new StringBuilder();
for (int i = 0; i < digest.length; ++i) {
s.append(String.format("%1$02x", digest[i]));
}
return s.toString();
}
}

View File

@ -0,0 +1,978 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.latin.R;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
/**
* Various helper functions for the state database
*/
public class MetadataDbHelper extends SQLiteOpenHelper {
@SuppressWarnings("unused")
private static final String TAG = MetadataDbHelper.class.getSimpleName();
// This was the initial release version of the database. It should never be
// changed going forward.
private static final int METADATA_DATABASE_INITIAL_VERSION = 3;
// This is the first released version of the database that implements CLIENTID. It is
// used to identify the versions for upgrades. This should never change going forward.
private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 5;
// This is the current database version. It should be updated when the database schema
// gets updated. It is passed to the framework constructor of SQLiteOpenHelper, so
// that's what the framework uses to track our database version.
private static final int METADATA_DATABASE_VERSION = 5;
private final static long NOT_A_DOWNLOAD_ID = -1;
public static final String METADATA_TABLE_NAME = "pendingUpdates";
private static final String CLIENT_TABLE_NAME = "clients";
public static final String PENDINGID_COLUMN = "pendingid"; // Download Manager ID
public static final String TYPE_COLUMN = "type";
public static final String STATUS_COLUMN = "status";
public static final String LOCALE_COLUMN = "locale";
public static final String WORDLISTID_COLUMN = "id";
public static final String DESCRIPTION_COLUMN = "description";
public static final String LOCAL_FILENAME_COLUMN = "filename";
public static final String REMOTE_FILENAME_COLUMN = "url";
public static final String DATE_COLUMN = "date";
public static final String CHECKSUM_COLUMN = "checksum";
public static final String FILESIZE_COLUMN = "filesize";
public static final String VERSION_COLUMN = "version";
public static final String FORMATVERSION_COLUMN = "formatversion";
public static final String FLAGS_COLUMN = "flags";
public static final int COLUMN_COUNT = 13;
private static final String CLIENT_CLIENT_ID_COLUMN = "clientid";
private static final String CLIENT_METADATA_URI_COLUMN = "uri";
private static final String CLIENT_LAST_UPDATE_DATE_COLUMN = "lastupdate";
private static final String CLIENT_PENDINGID_COLUMN = "pendingid"; // Download Manager ID
public static final String METADATA_DATABASE_NAME_STEM = "pendingUpdates";
public static final String METADATA_UPDATE_DESCRIPTION = "metadata";
public static final String DICTIONARIES_ASSETS_PATH = "dictionaries";
// Statuses, for storing in the STATUS_COLUMN
// IMPORTANT: The following are used as index arrays in ../WordListPreference
// Do not change their values without updating the matched code.
// Unknown status: this should never happen.
public static final int STATUS_UNKNOWN = 0;
// Available: this word list is available, but it is not downloaded (not downloading), because
// it is set not to be used.
public static final int STATUS_AVAILABLE = 1;
// Downloading: this word list is being downloaded.
public static final int STATUS_DOWNLOADING = 2;
// Installed: this word list is installed and usable.
public static final int STATUS_INSTALLED = 3;
// Disabled: this word list is installed, but has been disabled by the user.
public static final int STATUS_DISABLED = 4;
// Deleting: the user marked this word list to be deleted, but it has not been yet because
// Latin IME is not up yet.
public static final int STATUS_DELETING = 5;
// Types, for storing in the TYPE_COLUMN
// This is metadata about what is available.
public static final int TYPE_METADATA = 1;
// This is a bulk file. It should replace older files.
public static final int TYPE_BULK = 2;
// This is an incremental update, expected to be small, and meaningless on its own.
public static final int TYPE_UPDATE = 3;
private static final String METADATA_TABLE_CREATE =
"CREATE TABLE " + METADATA_TABLE_NAME + " ("
+ PENDINGID_COLUMN + " INTEGER, "
+ TYPE_COLUMN + " INTEGER, "
+ STATUS_COLUMN + " INTEGER, "
+ WORDLISTID_COLUMN + " TEXT, "
+ LOCALE_COLUMN + " TEXT, "
+ DESCRIPTION_COLUMN + " TEXT, "
+ LOCAL_FILENAME_COLUMN + " TEXT, "
+ REMOTE_FILENAME_COLUMN + " TEXT, "
+ DATE_COLUMN + " INTEGER, "
+ CHECKSUM_COLUMN + " TEXT, "
+ FILESIZE_COLUMN + " INTEGER, "
+ VERSION_COLUMN + " INTEGER,"
+ FORMATVERSION_COLUMN + " INTEGER,"
+ FLAGS_COLUMN + " INTEGER,"
+ "PRIMARY KEY (" + WORDLISTID_COLUMN + "," + VERSION_COLUMN + "));";
private static final String METADATA_CREATE_CLIENT_TABLE =
"CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " ("
+ CLIENT_CLIENT_ID_COLUMN + " TEXT, "
+ CLIENT_METADATA_URI_COLUMN + " TEXT, "
+ CLIENT_LAST_UPDATE_DATE_COLUMN + " INTEGER NOT NULL DEFAULT 0, "
+ CLIENT_PENDINGID_COLUMN + " INTEGER, "
+ FLAGS_COLUMN + " INTEGER, "
+ "PRIMARY KEY (" + CLIENT_CLIENT_ID_COLUMN + "));";
// List of all metadata table columns.
static final String[] METADATA_TABLE_COLUMNS = { PENDINGID_COLUMN, TYPE_COLUMN,
STATUS_COLUMN, WORDLISTID_COLUMN, LOCALE_COLUMN, DESCRIPTION_COLUMN,
LOCAL_FILENAME_COLUMN, REMOTE_FILENAME_COLUMN, DATE_COLUMN, CHECKSUM_COLUMN,
FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN };
// List of all client table columns.
static final String[] CLIENT_TABLE_COLUMNS = { CLIENT_CLIENT_ID_COLUMN,
CLIENT_METADATA_URI_COLUMN, CLIENT_PENDINGID_COLUMN, FLAGS_COLUMN };
// List of public columns returned to clients. Everything that is not in this list is
// private and implementation-dependent.
static final String[] DICTIONARIES_LIST_PUBLIC_COLUMNS = { STATUS_COLUMN, WORDLISTID_COLUMN,
LOCALE_COLUMN, DESCRIPTION_COLUMN, DATE_COLUMN, FILESIZE_COLUMN, VERSION_COLUMN };
// This class exhibits a singleton-like behavior by client ID, so it is getInstance'd
// and has a private c'tor.
private static TreeMap<String, MetadataDbHelper> sInstanceMap = null;
public static synchronized MetadataDbHelper getInstance(final Context context,
final String clientIdOrNull) {
// As a backward compatibility feature, null can be passed here to retrieve the "default"
// database. Before multi-client support, the dictionary packed used only one database
// and would not be able to handle several dictionary sets. Passing null here retrieves
// this legacy database. New clients should make sure to always pass a client ID so as
// to avoid conflicts.
final String clientId = null != clientIdOrNull ? clientIdOrNull : "";
if (null == sInstanceMap) sInstanceMap = new TreeMap<String, MetadataDbHelper>();
MetadataDbHelper helper = sInstanceMap.get(clientId);
if (null == helper) {
helper = new MetadataDbHelper(context, clientId);
sInstanceMap.put(clientId, helper);
}
return helper;
}
private MetadataDbHelper(final Context context, final String clientId) {
super(context,
METADATA_DATABASE_NAME_STEM + (TextUtils.isEmpty(clientId) ? "" : "." + clientId),
null, METADATA_DATABASE_VERSION);
mContext = context;
mClientId = clientId;
}
private final Context mContext;
private final String mClientId;
/**
* Get the database itself. This always returns the same object for any client ID. If the
* client ID is null, a default database is returned for backward compatibility. Don't
* pass null for new calls.
*
* @param context the context to create the database from. This is ignored after the first call.
* @param clientId the client id to retrieve the database of. null for default (deprecated)
* @return the database.
*/
public static SQLiteDatabase getDb(final Context context, final String clientId) {
return getInstance(context, clientId).getWritableDatabase();
}
private void createClientTable(final SQLiteDatabase db) {
// The clients table only exists in the primary db, the one that has an empty client id
if (!TextUtils.isEmpty(mClientId)) return;
db.execSQL(METADATA_CREATE_CLIENT_TABLE);
final String defaultMetadataUri = mContext.getString(R.string.default_metadata_uri);
if (!TextUtils.isEmpty(defaultMetadataUri)) {
final ContentValues defaultMetadataValues = new ContentValues();
defaultMetadataValues.put(CLIENT_CLIENT_ID_COLUMN, "");
defaultMetadataValues.put(CLIENT_METADATA_URI_COLUMN, defaultMetadataUri);
db.insert(CLIENT_TABLE_NAME, null, defaultMetadataValues);
}
}
/**
* Create the table and populate it with the resources found inside the apk.
*
* @see SQLiteOpenHelper#onCreate(SQLiteDatabase)
*
* @param db the database to create and populate.
*/
@Override
public void onCreate(final SQLiteDatabase db) {
db.execSQL(METADATA_TABLE_CREATE);
createClientTable(db);
}
/**
* Upgrade the database. Upgrade from version 3 is supported.
*/
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
if (METADATA_DATABASE_INITIAL_VERSION == oldVersion
&& METADATA_DATABASE_VERSION_WITH_CLIENTID == newVersion) {
// Upgrade from version METADATA_DATABASE_INITIAL_VERSION to version
// METADATA_DATABASE_VERSION_WITH_CLIENT_ID
if (TextUtils.isEmpty(mClientId)) {
// Only the default database should contain the client table.
// Anyway in version 3 only the default table existed so the emptyness
// test should always be true, but better check to be sure.
createClientTable(db);
}
} else {
// Version 3 was the earliest version, so we should never come here. If we do, we
// have no idea what this database is, so we'd better wipe it off.
db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
onCreate(db);
}
}
/**
* Downgrade the database. This drops and recreates the table in all cases.
*/
@Override
public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
// No matter what the numerical values of oldVersion and newVersion are, we know this
// is a downgrade (newVersion < oldVersion). There is no way to know what the future
// databases will look like, but we know it's extremely likely that it's okay to just
// drop the tables and start from scratch. Hence, we ignore the versions and just wipe
// everything we want to use.
if (oldVersion <= newVersion) {
Log.e(TAG, "onDowngrade database but new version is higher? " + oldVersion + " <= "
+ newVersion);
}
db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
onCreate(db);
}
/**
* Given a client ID, returns whether this client exists.
*
* @param context a context to open the database
* @param clientId the client ID to check
* @return true if the client is known, false otherwise
*/
public static boolean isClientKnown(final Context context, final String clientId) {
// If the client is known, they'll have a non-null metadata URI. An empty string is
// allowed as a metadata URI, if the client doesn't want any updates to happen.
return null != getMetadataUriAsString(context, clientId);
}
/**
* Returns the metadata URI as a string.
*
* If the client is not known, this will return null. If it is known, it will return
* the URI as a string. Note that the empty string is a valid value.
*
* @param context a context instance to open the database on
* @param clientId the ID of the client we want the metadata URI of
* @return the string representation of the URI
*/
public static String getMetadataUriAsString(final Context context, final String clientId) {
SQLiteDatabase defaultDb = getDb(context, null);
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
new String[] { CLIENT_METADATA_URI_COLUMN },
CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId },
null, null, null, null);
try {
if (!cursor.moveToFirst()) return null;
return cursor.getString(0); // Only one column, return it
} finally {
cursor.close();
}
}
/**
* Update the last metadata update time for all clients using a particular URI.
*
* All clients using this metadata URI will be indicated as having been updated now.
* The current time is used as the latest update time. This saved date will be what
* is returned henceforth by {@link #getLastUpdateDateForClient(Context, String)},
* until this method is called again.
*
* @param context a context instance to open the database on
* @param uri the metadata URI we just downloaded
*/
public static void saveLastUpdateTimeOfUri(final Context context, final String uri) {
PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis(),
context);
final ContentValues values = new ContentValues();
values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
final SQLiteDatabase defaultDb = getDb(context, null);
defaultDb.update(CLIENT_TABLE_NAME, values,
CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri });
}
/**
* Retrieves the last date at which we updated the metadata for this client.
*
* The returned date is in milliseconds from the EPOCH; this is the same unit as
* returned by {@link System#currentTimeMillis()}.
*
* @param context a context instance to open the database on
* @param clientId the client ID to get the latest update date of
* @return the last date at which this client was updated, as a long.
*/
public static long getLastUpdateDateForClient(final Context context, final String clientId) {
SQLiteDatabase defaultDb = getDb(context, null);
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN },
CLIENT_CLIENT_ID_COLUMN + " = ?",
new String[] { null == clientId ? "" : clientId },
null, null, null, null);
try {
if (!cursor.moveToFirst()) return 0;
return cursor.getLong(0); // Only one column, return it
} finally {
cursor.close();
}
}
/**
* Get the metadata download ID for a client ID.
*
* This will retrieve the download ID for the metadata file associated with a client ID.
* If there is no metadata download in progress for this client, it will return NOT_AN_ID.
*
* @param context a context instance to open the database on
* @param clientId the client ID to retrieve the metadata download ID of
* @return the metadata download ID, or NOT_AN_ID if no download is in progress
*/
public static long getMetadataDownloadIdForClient(final Context context,
final String clientId) {
SQLiteDatabase defaultDb = getDb(context, null);
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
new String[] { CLIENT_PENDINGID_COLUMN },
CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId },
null, null, null, null);
try {
if (!cursor.moveToFirst()) return UpdateHandler.NOT_AN_ID;
return cursor.getInt(0); // Only one column, return it
} finally {
cursor.close();
}
}
public static long getOldestUpdateTime(final Context context) {
SQLiteDatabase defaultDb = getDb(context, null);
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN },
null, null, null, null, null);
try {
if (!cursor.moveToFirst()) return 0;
final int columnIndex = 0; // Only one column queried
// Initialize the earliestTime to the largest possible value.
long earliestTime = Long.MAX_VALUE; // Almost 300 million years in the future
do {
final long thisTime = cursor.getLong(columnIndex);
earliestTime = Math.min(thisTime, earliestTime);
} while (cursor.moveToNext());
return earliestTime;
} finally {
cursor.close();
}
}
/**
* Helper method to make content values to write into the database.
* @return content values with all the arguments put with the right column names.
*/
public static ContentValues makeContentValues(final int pendingId, final int type,
final int status, final String wordlistId, final String locale,
final String description, final String filename, final String url, final long date,
final String checksum, final long filesize, final int version,
final int formatVersion) {
final ContentValues result = new ContentValues(COLUMN_COUNT);
result.put(PENDINGID_COLUMN, pendingId);
result.put(TYPE_COLUMN, type);
result.put(WORDLISTID_COLUMN, wordlistId);
result.put(STATUS_COLUMN, status);
result.put(LOCALE_COLUMN, locale);
result.put(DESCRIPTION_COLUMN, description);
result.put(LOCAL_FILENAME_COLUMN, filename);
result.put(REMOTE_FILENAME_COLUMN, url);
result.put(DATE_COLUMN, date);
result.put(CHECKSUM_COLUMN, checksum);
result.put(FILESIZE_COLUMN, filesize);
result.put(VERSION_COLUMN, version);
result.put(FORMATVERSION_COLUMN, formatVersion);
result.put(FLAGS_COLUMN, 0);
return result;
}
/**
* Helper method to fill in an incomplete ContentValues with default values.
* A wordlist ID and a locale are required, otherwise BadFormatException is thrown.
* @return the same object that was passed in, completed with default values.
*/
public static ContentValues completeWithDefaultValues(final ContentValues result)
throws BadFormatException {
if (!result.containsKey(WORDLISTID_COLUMN) || !result.containsKey(LOCALE_COLUMN)) {
throw new BadFormatException();
}
// 0 for the pending id, because there is none
if (!result.containsKey(PENDINGID_COLUMN)) result.put(PENDINGID_COLUMN, 0);
// This is a binary blob of a dictionary
if (!result.containsKey(TYPE_COLUMN)) result.put(TYPE_COLUMN, TYPE_BULK);
// This word list is unknown, but it's present, else we wouldn't be here, so INSTALLED
if (!result.containsKey(STATUS_COLUMN)) result.put(STATUS_COLUMN, STATUS_INSTALLED);
// No description unless specified, because we can't guess it
if (!result.containsKey(DESCRIPTION_COLUMN)) result.put(DESCRIPTION_COLUMN, "");
// File name - this is an asset, so it works as an already deleted file.
// hence, we need to supply a non-existent file name. Anything will
// do as long as it returns false when tested with File#exist(), and
// the empty string does not, so it's set to "_".
if (!result.containsKey(LOCAL_FILENAME_COLUMN)) result.put(LOCAL_FILENAME_COLUMN, "_");
// No remote file name : this can't be downloaded. Unless specified.
if (!result.containsKey(REMOTE_FILENAME_COLUMN)) result.put(REMOTE_FILENAME_COLUMN, "");
// 0 for the update date : 1970/1/1. Unless specified.
if (!result.containsKey(DATE_COLUMN)) result.put(DATE_COLUMN, 0);
// Checksum unknown unless specified
if (!result.containsKey(CHECKSUM_COLUMN)) result.put(CHECKSUM_COLUMN, "");
// No filesize unless specified
if (!result.containsKey(FILESIZE_COLUMN)) result.put(FILESIZE_COLUMN, 0);
// Smallest possible version unless specified
if (!result.containsKey(VERSION_COLUMN)) result.put(VERSION_COLUMN, 1);
// Assume current format unless specified
if (!result.containsKey(FORMATVERSION_COLUMN))
result.put(FORMATVERSION_COLUMN, UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION);
// No flags unless specified
if (!result.containsKey(FLAGS_COLUMN)) result.put(FLAGS_COLUMN, 0);
return result;
}
/**
* Reads a column in a Cursor as a String and stores it in a ContentValues object.
* @param result the ContentValues object to store the result in.
* @param cursor the Cursor to read the column from.
* @param columnId the column ID to read.
*/
private static void putStringResult(ContentValues result, Cursor cursor, String columnId) {
result.put(columnId, cursor.getString(cursor.getColumnIndex(columnId)));
}
/**
* Reads a column in a Cursor as an int and stores it in a ContentValues object.
* @param result the ContentValues object to store the result in.
* @param cursor the Cursor to read the column from.
* @param columnId the column ID to read.
*/
private static void putIntResult(ContentValues result, Cursor cursor, String columnId) {
result.put(columnId, cursor.getInt(cursor.getColumnIndex(columnId)));
}
private static ContentValues getFirstLineAsContentValues(final Cursor cursor) {
final ContentValues result;
if (cursor.moveToFirst()) {
result = new ContentValues(COLUMN_COUNT);
putIntResult(result, cursor, PENDINGID_COLUMN);
putIntResult(result, cursor, TYPE_COLUMN);
putIntResult(result, cursor, STATUS_COLUMN);
putStringResult(result, cursor, WORDLISTID_COLUMN);
putStringResult(result, cursor, LOCALE_COLUMN);
putStringResult(result, cursor, DESCRIPTION_COLUMN);
putStringResult(result, cursor, LOCAL_FILENAME_COLUMN);
putStringResult(result, cursor, REMOTE_FILENAME_COLUMN);
putIntResult(result, cursor, DATE_COLUMN);
putStringResult(result, cursor, CHECKSUM_COLUMN);
putIntResult(result, cursor, FILESIZE_COLUMN);
putIntResult(result, cursor, VERSION_COLUMN);
putIntResult(result, cursor, FORMATVERSION_COLUMN);
putIntResult(result, cursor, FLAGS_COLUMN);
if (cursor.moveToNext()) {
// TODO: print the second level of the stack to the log so that we know
// in which code path the error happened
Log.e(TAG, "Several SQL results when we expected only one!");
}
} else {
result = null;
}
return result;
}
/**
* Gets the info about as specific download, indexed by its DownloadManager ID.
* @param db the database to get the information from.
* @param id the DownloadManager id.
* @return metadata about this download. This returns all columns in the database.
*/
public static ContentValues getContentValuesByPendingId(final SQLiteDatabase db,
final long id) {
final Cursor cursor = db.query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS,
PENDINGID_COLUMN + "= ?",
new String[] { Long.toString(id) },
null, null, null);
// There should never be more than one result. If because of some bug there are, returning
// only one result is the right thing to do, because we couldn't handle several anyway
// and we should still handle one.
final ContentValues result = getFirstLineAsContentValues(cursor);
cursor.close();
return result;
}
/**
* Gets the info about an installed OR deleting word list with a specified id.
*
* Basically, this is the word list that we want to return to Android Keyboard when
* it asks for a specific id.
*
* @param db the database to get the information from.
* @param id the word list ID.
* @return the metadata about this word list.
*/
public static ContentValues getInstalledOrDeletingWordListContentValuesByWordListId(
final SQLiteDatabase db, final String id) {
final Cursor cursor = db.query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS,
WORDLISTID_COLUMN + "=? AND (" + STATUS_COLUMN + "=? OR " + STATUS_COLUMN + "=?)",
new String[] { id, Integer.toString(STATUS_INSTALLED),
Integer.toString(STATUS_DELETING) },
null, null, null);
// There should only be one result, but if there are several, we can't tell which
// is the best, so we just return the first one.
final ContentValues result = getFirstLineAsContentValues(cursor);
cursor.close();
return result;
}
/**
* Given a specific download ID, return records for all pending downloads across all clients.
*
* If several clients use the same metadata URL, we know to only download it once, and
* dispatch the update process across all relevant clients when the download ends. This means
* several clients may share a single download ID if they share a metadata URI.
* The dispatching is done in {@link UpdateHandler#downloadFinished(Context, Intent)}, which
* finds out about the list of relevant clients by calling this method.
*
* @param context a context instance to open the databases
* @param downloadId the download ID to query about
* @return the list of records. Never null, but may be empty.
*/
public static ArrayList<DownloadRecord> getDownloadRecordsForDownloadId(final Context context,
final long downloadId) {
final SQLiteDatabase defaultDb = getDb(context, "");
final ArrayList<DownloadRecord> results = new ArrayList<DownloadRecord>();
final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, CLIENT_TABLE_COLUMNS,
null, null, null, null, null);
try {
if (!cursor.moveToFirst()) return results;
final int clientIdIndex = cursor.getColumnIndex(CLIENT_CLIENT_ID_COLUMN);
final int pendingIdColumn = cursor.getColumnIndex(CLIENT_PENDINGID_COLUMN);
do {
final long pendingId = cursor.getInt(pendingIdColumn);
final String clientId = cursor.getString(clientIdIndex);
if (pendingId == downloadId) {
results.add(new DownloadRecord(clientId, null));
}
final ContentValues valuesForThisClient =
getContentValuesByPendingId(getDb(context, clientId), downloadId);
if (null != valuesForThisClient) {
results.add(new DownloadRecord(clientId, valuesForThisClient));
}
} while (cursor.moveToNext());
} finally {
cursor.close();
}
return results;
}
/**
* Gets the info about a specific word list.
*
* @param db the database to get the information from.
* @param id the word list ID.
* @param version the word list version.
* @return the metadata about this word list.
*/
public static ContentValues getContentValuesByWordListId(final SQLiteDatabase db,
final String id, final int version) {
final Cursor cursor = db.query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS,
WORDLISTID_COLUMN + "= ? AND " + VERSION_COLUMN + "= ?",
new String[] { id, Integer.toString(version) }, null, null, null);
// This is a lookup by primary key, so there can't be more than one result.
final ContentValues result = getFirstLineAsContentValues(cursor);
cursor.close();
return result;
}
/**
* Gets the info about the latest word list with an id.
*
* @param db the database to get the information from.
* @param id the word list ID.
* @return the metadata about the word list with this id and the latest version number.
*/
public static ContentValues getContentValuesOfLatestAvailableWordlistById(
final SQLiteDatabase db, final String id) {
final Cursor cursor = db.query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS,
WORDLISTID_COLUMN + "= ?",
new String[] { id }, null, null, VERSION_COLUMN + " DESC", "1");
// This is a lookup by primary key, so there can't be more than one result.
final ContentValues result = getFirstLineAsContentValues(cursor);
cursor.close();
return result;
}
/**
* Gets the current metadata about INSTALLED, AVAILABLE or DELETING dictionaries.
*
* This odd method is tailored to the needs of
* DictionaryProvider#getDictionaryWordListsForContentUri, which needs the word list if
* it is:
* - INSTALLED: this should be returned to LatinIME if the file is still inside the dictionary
* pack, so that it can be copied. If the file is not there, it's been copied already and should
* not be returned, so getDictionaryWordListsForContentUri takes care of this.
* - DELETING: this should be returned to LatinIME so that it can actually delete the file.
* - AVAILABLE: this should not be returned, but should be checked for auto-installation.
*
* @param context the context for getting the database.
* @param clientId the client id for retrieving the database. null for default (deprecated)
* @return a cursor with metadata about usable dictionaries.
*/
public static Cursor queryInstalledOrDeletingOrAvailableDictionaryMetadata(
final Context context, final String clientId) {
// If clientId is null, we get the defaut DB (see #getInstance() for more about this)
final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS,
STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ?",
new String[] { Integer.toString(STATUS_INSTALLED),
Integer.toString(STATUS_DELETING),
Integer.toString(STATUS_AVAILABLE) },
null, null, LOCALE_COLUMN);
return results;
}
/**
* Gets the current metadata about all dictionaries.
*
* This will retrieve the metadata about all dictionaries, including
* older files, or files not yet downloaded.
*
* @param context the context for getting the database.
* @param clientId the client id for retrieving the database. null for default (deprecated)
* @return a cursor with metadata about usable dictionaries.
*/
public static Cursor queryCurrentMetadata(final Context context, final String clientId) {
// If clientId is null, we get the defaut DB (see #getInstance() for more about this)
final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
METADATA_TABLE_COLUMNS, null, null, null, null, LOCALE_COLUMN);
return results;
}
/**
* Gets the list of all dictionaries known to the dictionary provider, with only public columns.
*
* This will retrieve information about all known dictionaries, and their status. As such,
* it will also return information about dictionaries on the server that have not been
* downloaded yet, but may be requested.
* This only returns public columns. It does not populate internal columns in the returned
* cursor.
* The value returned by this method is intended to be good to be returned directly for a
* request of the list of dictionaries by a client.
*
* @param context the context to read the database from.
* @param clientId the client id for retrieving the database. null for default (deprecated)
* @return a cursor that lists all available dictionaries and their metadata.
*/
public static Cursor queryDictionaries(final Context context, final String clientId) {
// If clientId is null, we get the defaut DB (see #getInstance() for more about this)
final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
DICTIONARIES_LIST_PUBLIC_COLUMNS,
// Filter out empty locales so as not to return auxiliary data, like a
// data line for downloading metadata:
MetadataDbHelper.LOCALE_COLUMN + " != ?", new String[] {""},
// TODO: Reinstate the following code for bulk, then implement partial updates
/* MetadataDbHelper.TYPE_COLUMN + " = ?",
new String[] { Integer.toString(MetadataDbHelper.TYPE_BULK) }, */
null, null, LOCALE_COLUMN);
return results;
}
/**
* Deletes all data associated with a client.
*
* @param context the context for opening the database
* @param clientId the ID of the client to delete.
* @return true if the client was successfully deleted, false otherwise.
*/
public static boolean deleteClient(final Context context, final String clientId) {
// Remove all metadata associated with this client
final SQLiteDatabase db = getDb(context, clientId);
db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
db.execSQL(METADATA_TABLE_CREATE);
// Remove this client's entry in the clients table
final SQLiteDatabase defaultDb = getDb(context, "");
if (0 == defaultDb.delete(CLIENT_TABLE_NAME,
CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId })) {
return false;
}
return true;
}
/**
* Updates information relative to a specific client.
*
* Updatable information includes only the metadata URI, but may be expanded in the future.
* The passed values must include a client ID in the key CLIENT_CLIENT_ID_COLUMN, and it must
* be equal to the string passed as an argument for clientId.
* The passed values must also include a non-empty metadata URI in the
* CLIENT_METADATA_URI_COLUMN column.
* If any of the above is not complied with, this function returns without updating data.
*
* @param context the context, to open the database
* @param clientId the ID of the client to update
* @param values the values to update. Must conform to the protocol (see above)
*/
public static void updateClientInfo(final Context context, final String clientId,
final ContentValues values) {
// Sanity check the content values
final String valuesClientId = values.getAsString(CLIENT_CLIENT_ID_COLUMN);
final String valuesMetadataUri = values.getAsString(CLIENT_METADATA_URI_COLUMN);
// Empty string is a valid client ID, but external apps may not configure it.
// Empty string is a valid metadata URI if the client does not want updates.
if (TextUtils.isEmpty(valuesClientId) || null == valuesMetadataUri) {
// We need both these columns to be filled in
Utils.l("Missing parameter for updateClientInfo");
return;
}
if (!clientId.equals(valuesClientId)) {
// Mismatch! The client violates the protocol.
Utils.l("Received an updateClientInfo request for ", clientId, " but the values "
+ "contain a different ID : ", valuesClientId);
return;
}
final SQLiteDatabase defaultDb = getDb(context, "");
if (-1 == defaultDb.insert(CLIENT_TABLE_NAME, null, values)) {
defaultDb.update(CLIENT_TABLE_NAME, values,
CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
}
}
/**
* Retrieves the list of existing client IDs.
* @param context the context to open the database
* @return a cursor containing only one column, and one client ID per line.
*/
public static Cursor queryClientIds(final Context context) {
return getDb(context, null).query(CLIENT_TABLE_NAME,
new String[] { CLIENT_CLIENT_ID_COLUMN }, null, null, null, null, null);
}
/**
* Register a download ID for a specific metadata URI.
*
* This method should be called when a download for a metadata URI is starting. It will
* register the download ID for all clients using this metadata URI into the database
* for later retrieval by {@link #getDownloadRecordsForDownloadId(Context, long)}.
*
* @param context a context for opening databases
* @param uri the metadata URI
* @param downloadId the download ID
*/
public static void registerMetadataDownloadId(final Context context, final String uri,
final long downloadId) {
final ContentValues values = new ContentValues();
values.put(CLIENT_PENDINGID_COLUMN, downloadId);
final SQLiteDatabase defaultDb = getDb(context, "");
defaultDb.update(CLIENT_TABLE_NAME, values,
CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri });
}
/**
* Marks a downloading entry as having successfully downloaded and being installed.
*
* The metadata database contains information about ongoing processes, typically ongoing
* downloads. This marks such an entry as having finished and having installed successfully,
* so it becomes INSTALLED.
*
* @param db the metadata database.
* @param r content values about the entry to mark as processed.
*/
public static void markEntryAsFinishedDownloadingAndInstalled(final SQLiteDatabase db,
final ContentValues r) {
switch (r.getAsInteger(TYPE_COLUMN)) {
case TYPE_BULK:
Utils.l("Ended processing a wordlist");
// Updating a bulk word list is a three-step operation:
// - Add the new entry to the table
// - Remove the old entry from the table
// - Erase the old file
// We start by gathering the names of the files we should delete.
final List<String> filenames = new LinkedList<String>();
final Cursor c = db.query(METADATA_TABLE_NAME,
new String[] { LOCAL_FILENAME_COLUMN },
LOCALE_COLUMN + " = ? AND " +
WORDLISTID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?",
new String[] { r.getAsString(LOCALE_COLUMN),
r.getAsString(WORDLISTID_COLUMN),
Integer.toString(STATUS_INSTALLED) },
null, null, null);
if (c.moveToFirst()) {
// There should never be more than one file, but if there are, it's a bug
// and we should remove them all. I think it might happen if the power of the
// phone is suddenly cut during an update.
final int filenameIndex = c.getColumnIndex(LOCAL_FILENAME_COLUMN);
do {
Utils.l("Setting for removal", c.getString(filenameIndex));
filenames.add(c.getString(filenameIndex));
} while (c.moveToNext());
}
r.put(STATUS_COLUMN, STATUS_INSTALLED);
db.beginTransactionNonExclusive();
// Delete all old entries. There should never be any stalled entries, but if
// there are, this deletes them.
db.delete(METADATA_TABLE_NAME,
WORDLISTID_COLUMN + " = ?",
new String[] { r.getAsString(WORDLISTID_COLUMN) });
db.insert(METADATA_TABLE_NAME, null, r);
db.setTransactionSuccessful();
db.endTransaction();
for (String filename : filenames) {
try {
final File f = new File(filename);
f.delete();
} catch (SecurityException e) {
// No permissions to delete. Um. Can't do anything.
} // I don't think anything else can be thrown
}
break;
default:
// Unknown type: do nothing.
break;
}
}
/**
* Removes a downloading entry from the database.
*
* This is invoked when a download fails. Either we tried to download, but
* we received a permanent failure and we should remove it, or we got manually
* cancelled and we should leave it at that.
*
* @param db the metadata database.
* @param id the DownloadManager id of the file.
*/
public static void deleteDownloadingEntry(final SQLiteDatabase db, final long id) {
db.delete(METADATA_TABLE_NAME, PENDINGID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?",
new String[] { Long.toString(id), Integer.toString(STATUS_DOWNLOADING) });
}
/**
* Forcefully removes an entry from the database.
*
* This is invoked when a file is broken. The file has been downloaded, but Android
* Keyboard is telling us it could not open it.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
*/
public static void deleteEntry(final SQLiteDatabase db, final String id, final int version) {
db.delete(METADATA_TABLE_NAME, WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?",
new String[] { id, Integer.toString(version) });
}
/**
* Internal method that sets the current status of an entry of the database.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
* @param status the status to set the word list to.
* @param downloadId an optional download id to write, or NOT_A_DOWNLOAD_ID
*/
private static void markEntryAs(final SQLiteDatabase db, final String id,
final int version, final int status, final long downloadId) {
final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db, id, version);
values.put(STATUS_COLUMN, status);
if (NOT_A_DOWNLOAD_ID != downloadId) {
values.put(MetadataDbHelper.PENDINGID_COLUMN, downloadId);
}
db.update(METADATA_TABLE_NAME, values,
WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?",
new String[] { id, Integer.toString(version) });
}
/**
* Writes the status column for the wordlist with this id as enabled. Typically this
* means the word list is currently disabled and we want to set its status to INSTALLED.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
*/
public static void markEntryAsEnabled(final SQLiteDatabase db, final String id,
final int version) {
markEntryAs(db, id, version, STATUS_INSTALLED, NOT_A_DOWNLOAD_ID);
}
/**
* Writes the status column for the wordlist with this id as disabled. Typically this
* means the word list is currently installed and we want to set its status to DISABLED.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
*/
public static void markEntryAsDisabled(final SQLiteDatabase db, final String id,
final int version) {
markEntryAs(db, id, version, STATUS_DISABLED, NOT_A_DOWNLOAD_ID);
}
/**
* Writes the status column for the wordlist with this id as available. This happens for
* example when a word list has been deleted but can be downloaded again.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
*/
public static void markEntryAsAvailable(final SQLiteDatabase db, final String id,
final int version) {
markEntryAs(db, id, version, STATUS_AVAILABLE, NOT_A_DOWNLOAD_ID);
}
/**
* Writes the designated word list as downloadable, alongside with its download id.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
* @param downloadId the download id.
*/
public static void markEntryAsDownloading(final SQLiteDatabase db, final String id,
final int version, final long downloadId) {
markEntryAs(db, id, version, STATUS_DOWNLOADING, downloadId);
}
/**
* Writes the designated word list as deleting.
*
* @param db the metadata database.
* @param id the id of the word list.
* @param version the version of the word list.
*/
public static void markEntryAsDeleting(final SQLiteDatabase db, final String id,
final int version) {
markEntryAs(db, id, version, STATUS_DELETING, NOT_A_DOWNLOAD_ID);
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.Context;
import android.database.Cursor;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class to easy up manipulation of dictionary pack metadata.
*/
public class MetadataHandler {
@SuppressWarnings("unused")
private static final String TAG = "DictionaryProvider:" + MetadataHandler.class.getSimpleName();
// The canonical file name for metadata. This is not the name of a real file on the
// device, but a symbolic name used in the database and in metadata handling. It is never
// tested against, only used for human-readability as the file name for the metadata.
public final static String METADATA_FILENAME = "metadata.json";
/**
* Reads the data from the cursor and store it in metadata objects.
* @param results the cursor to read data from.
* @return the constructed list of wordlist metadata.
*/
private static List<WordListMetadata> makeMetadataObject(final Cursor results) {
final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<WordListMetadata>();
if (results.moveToFirst()) {
final int localeColumn = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
final int typeColumn = results.getColumnIndex(MetadataDbHelper.TYPE_COLUMN);
final int descriptionColumn =
results.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
final int updateIndex = results.getColumnIndex(MetadataDbHelper.DATE_COLUMN);
final int fileSizeIndex = results.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
final int checksumIndex = results.getColumnIndex(MetadataDbHelper.CHECKSUM_COLUMN);
final int localFilenameIndex =
results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final int remoteFilenameIndex =
results.getColumnIndex(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
final int versionIndex = results.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
final int formatVersionIndex =
results.getColumnIndex(MetadataDbHelper.FORMATVERSION_COLUMN);
do {
buildingMetadata.add(new WordListMetadata(results.getString(idIndex),
results.getInt(typeColumn),
results.getString(descriptionColumn),
results.getLong(updateIndex),
results.getLong(fileSizeIndex),
results.getString(checksumIndex),
results.getString(localFilenameIndex),
results.getString(remoteFilenameIndex),
results.getInt(versionIndex),
results.getInt(formatVersionIndex),
0, results.getString(localeColumn)));
} while (results.moveToNext());
results.close();
}
return Collections.unmodifiableList(buildingMetadata);
}
/**
* Gets the whole metadata, for installed and not installed dictionaries.
* @param context The context to open files over.
* @param clientId the client id for retrieving the database. null for default (deprecated)
* @return The current metadata.
*/
public static List<WordListMetadata> getCurrentMetadata(final Context context,
final String clientId) {
// If clientId is null, we get a cursor on the default database (see
// MetadataDbHelper#getInstance() for more on this)
final Cursor results = MetadataDbHelper.queryCurrentMetadata(context, clientId);
final List<WordListMetadata> resultList = makeMetadataObject(results);
results.close();
return resultList;
}
/**
* Read metadata from a stream.
* @param input The stream to read from.
* @return The read metadata.
* @throws IOException if the input stream cannot be read
* @throws BadFormatException if the stream is not in a known format
*/
public static List<WordListMetadata> readMetadata(final InputStreamReader input)
throws IOException, BadFormatException {
return MetadataParser.parseMetadata(input);
}
/**
* Finds a single WordListMetadata inside a whole metadata chunk.
*
* Searches through the whole passed metadata for the first WordListMetadata associated
* with the passed ID. If several metadata chunks with the same id are found, it will
* always return the one with the bigger FormatVersion that is less or equal than the
* maximum supported format version (as listed in UpdateHandler).
* This will NEVER return the metadata with a FormatVersion bigger than what is supported,
* even if it is the only word list with this ID.
*
* @param metadata the metadata to search into.
* @param id the word list ID of the metadata to find.
* @return the associated metadata, or null if not found.
*/
public static WordListMetadata findWordListById(final List<WordListMetadata> metadata,
final String id) {
WordListMetadata bestWordList = null;
int bestFormatVersion = Integer.MIN_VALUE; // To be sure we can't be inadvertently smaller
for (WordListMetadata wordList : metadata) {
if (id.equals(wordList.mId)
&& wordList.mFormatVersion <= UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION
&& wordList.mFormatVersion > bestFormatVersion) {
bestWordList = wordList;
bestFormatVersion = wordList.mFormatVersion;
}
}
// If we didn't find any match we'll return null.
return bestWordList;
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.text.TextUtils;
import android.util.JsonReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
/**
* Helper class containing functions to parse the dictionary metadata.
*/
public class MetadataParser {
// Name of the fields in the JSON-formatted file.
private static final String ID_FIELD_NAME = MetadataDbHelper.WORDLISTID_COLUMN;
private static final String LOCALE_FIELD_NAME = "locale";
private static final String DESCRIPTION_FIELD_NAME = MetadataDbHelper.DESCRIPTION_COLUMN;
private static final String UPDATE_FIELD_NAME = "update";
private static final String FILESIZE_FIELD_NAME = MetadataDbHelper.FILESIZE_COLUMN;
private static final String CHECKSUM_FIELD_NAME = MetadataDbHelper.CHECKSUM_COLUMN;
private static final String REMOTE_FILENAME_FIELD_NAME =
MetadataDbHelper.REMOTE_FILENAME_COLUMN;
private static final String VERSION_FIELD_NAME = MetadataDbHelper.VERSION_COLUMN;
private static final String FORMATVERSION_FIELD_NAME = MetadataDbHelper.FORMATVERSION_COLUMN;
/**
* Parse one JSON-formatted word list metadata.
* @param reader the reader containing the data.
* @return a WordListMetadata object from the parsed data.
* @throws IOException if the underlying reader throws IOException during reading.
*/
private static WordListMetadata parseOneWordList(final JsonReader reader)
throws IOException, BadFormatException {
final TreeMap<String, String> arguments = new TreeMap<String, String>();
reader.beginObject();
while (reader.hasNext()) {
final String name = reader.nextName();
if (!TextUtils.isEmpty(name)) {
arguments.put(name, reader.nextString());
}
}
reader.endObject();
if (TextUtils.isEmpty(arguments.get(ID_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(LOCALE_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(DESCRIPTION_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(UPDATE_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(FILESIZE_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(CHECKSUM_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(REMOTE_FILENAME_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(VERSION_FIELD_NAME))
|| TextUtils.isEmpty(arguments.get(FORMATVERSION_FIELD_NAME))) {
throw new BadFormatException(arguments.toString());
}
// TODO: need to find out whether it's bulk or update
// The null argument is the local file name, which is not known at this time and will
// be decided later.
return new WordListMetadata(
arguments.get(ID_FIELD_NAME),
MetadataDbHelper.TYPE_BULK,
arguments.get(DESCRIPTION_FIELD_NAME),
Long.parseLong(arguments.get(UPDATE_FIELD_NAME)),
Long.parseLong(arguments.get(FILESIZE_FIELD_NAME)),
arguments.get(CHECKSUM_FIELD_NAME),
null,
arguments.get(REMOTE_FILENAME_FIELD_NAME),
Integer.parseInt(arguments.get(VERSION_FIELD_NAME)),
Integer.parseInt(arguments.get(FORMATVERSION_FIELD_NAME)),
0, arguments.get(LOCALE_FIELD_NAME));
}
/**
* Parses metadata in the JSON format.
* @param input a stream reader expected to contain JSON formatted metadata.
* @return dictionary metadata, as an array of WordListMetadata objects.
* @throws IOException if the underlying reader throws IOException during reading.
* @throws BadFormatException if the data was not in the expected format.
*/
public static List<WordListMetadata> parseMetadata(final InputStreamReader input)
throws IOException, BadFormatException {
JsonReader reader = new JsonReader(input);
final ArrayList<WordListMetadata> readInfo = new ArrayList<WordListMetadata>();
reader.beginArray();
while (reader.hasNext()) {
final WordListMetadata thisMetadata = parseOneWordList(reader);
if (!TextUtils.isEmpty(thisMetadata.mLocale))
readInfo.add(thisMetadata);
}
return Collections.unmodifiableList(readInfo);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Class to keep long-term log. This is inactive in production, and is only for debug purposes.
*/
public class PrivateLog {
public static final boolean DEBUG = DictionaryProvider.DEBUG;
private static final String LOG_DATABASE_NAME = "log";
private static final String LOG_TABLE_NAME = "log";
private static final int LOG_DATABASE_VERSION = 1;
private static final String COLUMN_DATE = "date";
private static final String COLUMN_EVENT = "event";
private static final String LOG_TABLE_CREATE = "CREATE TABLE " + LOG_TABLE_NAME + " ("
+ COLUMN_DATE + " TEXT,"
+ COLUMN_EVENT + " TEXT);";
private static final SimpleDateFormat sDateFormat =
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
private static PrivateLog sInstance = new PrivateLog();
private static DebugHelper mDebugHelper = null;
private PrivateLog() {
}
public static synchronized PrivateLog getInstance(final Context context) {
if (!DEBUG) return sInstance;
synchronized(PrivateLog.class) {
if (sInstance.mDebugHelper == null) {
sInstance.mDebugHelper = new DebugHelper(context);
}
return sInstance;
}
}
private static class DebugHelper extends SQLiteOpenHelper {
private DebugHelper(final Context context) {
super(context, LOG_DATABASE_NAME, null, LOG_DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
if (!DEBUG) return;
db.execSQL(LOG_TABLE_CREATE);
insert(db, "Created table");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (!DEBUG) return;
// Remove all data.
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE_NAME);
onCreate(db);
insert(db, "Upgrade finished");
}
private static void insert(SQLiteDatabase db, String event) {
if (!DEBUG) return;
final ContentValues c = new ContentValues(2);
c.put(COLUMN_DATE, sDateFormat.format(new Date(System.currentTimeMillis())));
c.put(COLUMN_EVENT, event);
db.insert(LOG_TABLE_NAME, null, c);
}
}
public static void log(String event, Context context) {
if (!DEBUG) return;
final SQLiteDatabase l = getInstance(context).mDebugHelper.getWritableDatabase();
mDebugHelper.insert(l, event);
}
public static void log(String event, ContentProvider provider) {
if (!DEBUG) return;
final SQLiteDatabase l =
getInstance(provider.getContext()).mDebugHelper.getWritableDatabase();
mDebugHelper.insert(l, event);
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
/**
* A simple interface to report problems.
*/
public interface ProblemReporter {
public void report(Exception e);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.util.Log;
/**
* A class for various utility methods, especially debugging.
*/
public final class Utils {
private final static String TAG = Utils.class.getSimpleName() + ":DEBUG --";
private final static boolean DEBUG = DictionaryProvider.DEBUG;
/**
* Calls .toString() on its non-null argument or returns "null"
* @param o the object to convert to a string
* @return the result of .toString() or null
*/
public static String s(final Object o) {
return null == o ? "null" : o.toString();
}
/**
* Get the string representation of the current stack trace, for debugging purposes.
* @return a readable, carriage-return-separated string for the current stack trace.
*/
public static String getStackTrace() {
final StringBuilder sb = new StringBuilder();
try {
throw new RuntimeException();
} catch (RuntimeException e) {
StackTraceElement[] frames = e.getStackTrace();
// Start at 1 because the first frame is here and we don't care about it
for (int j = 1; j < frames.length; ++j) {
sb.append(frames[j].toString() + "\n");
}
}
return sb.toString();
}
/**
* Get the stack trace contained in an exception as a human-readable string.
* @param e the exception
* @return the human-readable stack trace
*/
public static String getStackTrace(final Exception e) {
final StringBuilder sb = new StringBuilder();
final StackTraceElement[] frames = e.getStackTrace();
for (int j = 0; j < frames.length; ++j) {
sb.append(frames[j].toString() + "\n");
}
return sb.toString();
}
/**
* Helper log method to ease null-checks and adding spaces.
*
* This sends all arguments to the log, separated by spaces. Any null argument is converted
* to the "null" string. It uses a very visible tag and log level for debugging purposes.
*
* @param args the stuff to send to the log
*/
public static void l(final Object... args) {
if (!DEBUG) return;
final StringBuilder sb = new StringBuilder();
for (final Object o : args) {
sb.append(s(o).toString());
sb.append(" ");
}
Log.e(TAG, sb.toString());
}
/**
* Helper log method to put stuff in red.
*
* This does the same as #l but prints in red
*
* @param args the stuff to send to the log
*/
public static void r(final Object... args) {
if (!DEBUG) return;
final StringBuilder sb = new StringBuilder("\u001B[31m");
for (final Object o : args) {
sb.append(s(o).toString());
sb.append(" ");
}
sb.append("\u001B[0m");
Log.e(TAG, sb.toString());
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.dictionarypack;
import android.content.ContentValues;
/**
* The metadata for a single word list.
*
* Instances of this class are always immutable.
*/
public class WordListMetadata {
public final String mId;
public final int mType; // Type, as of MetadataDbHelper#TYPE_*
public final String mDescription;
public final long mLastUpdate;
public final long mFileSize;
public final String mChecksum;
public final String mLocalFilename;
public final String mRemoteFilename;
public final int mVersion; // version of this word list
public final int mFlags; // Always 0 in this version, reserved for future use
// The locale is matched against the locale requested by the client. The matching algorithm
// is a standard locale matching with fallback; it is implemented in
// DictionaryProvider#getDictionaryFileForContentUri.
public final String mLocale;
// Version number of the format.
// This implementation of the DictionaryDataService knows how to handle format 1 only.
// This is only for forward compatibility, to be able to upgrade the format without
// breaking old implementations.
public final int mFormatVersion;
public WordListMetadata(final String id, final int type,
final String description, final long lastUpdate, final long fileSize,
final String checksum, final String localFilename, final String remoteFilename,
final int version, final int formatVersion, final int flags, final String locale) {
mId = id;
mType = type;
mDescription = description;
mLastUpdate = lastUpdate; // In milliseconds
mFileSize = fileSize;
mChecksum = checksum;
mLocalFilename = localFilename;
mRemoteFilename = remoteFilename;
mVersion = version;
mFormatVersion = formatVersion;
mFlags = flags;
mLocale = locale;
}
/**
* Create a WordListMetadata from the contents of a ContentValues.
*
* If this lacks any required field, IllegalArgumentException is thrown.
*/
public static WordListMetadata createFromContentValues(final ContentValues values) {
final String id = values.getAsString(MetadataDbHelper.WORDLISTID_COLUMN);
final Integer type = values.getAsInteger(MetadataDbHelper.TYPE_COLUMN);
final String description = values.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN);
final Long lastUpdate = values.getAsLong(MetadataDbHelper.DATE_COLUMN);
final Long fileSize = values.getAsLong(MetadataDbHelper.FILESIZE_COLUMN);
final String checksum = values.getAsString(MetadataDbHelper.CHECKSUM_COLUMN);
final String localFilename = values.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final String remoteFilename = values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
final Integer version = values.getAsInteger(MetadataDbHelper.VERSION_COLUMN);
final Integer formatVersion = values.getAsInteger(MetadataDbHelper.FORMATVERSION_COLUMN);
final Integer flags = values.getAsInteger(MetadataDbHelper.FLAGS_COLUMN);
final String locale = values.getAsString(MetadataDbHelper.LOCALE_COLUMN);
if (null == id
|| null == type
|| null == description
|| null == lastUpdate
|| null == fileSize
|| null == checksum
|| null == localFilename
|| null == remoteFilename
|| null == version
|| null == formatVersion
|| null == flags
|| null == locale) {
throw new IllegalArgumentException();
}
return new WordListMetadata(id, type, description, lastUpdate, fileSize, checksum,
localFilename, remoteFilename, version, formatVersion, flags, locale);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(WordListMetadata.class.getSimpleName());
sb.append(" : ").append(mId);
sb.append("\nType : ").append(mType);
sb.append("\nDescription : ").append(mDescription);
sb.append("\nLastUpdate : ").append(mLastUpdate);
sb.append("\nFileSize : ").append(mFileSize);
sb.append("\nChecksum : ").append(mChecksum);
sb.append("\nLocalFilename : ").append(mLocalFilename);
sb.append("\nRemoteFilename : ").append(mRemoteFilename);
sb.append("\nVersion : ").append(mVersion);
sb.append("\nFormatVersion : ").append(mFormatVersion);
sb.append("\nFlags : ").append(mFlags);
sb.append("\nLocale : ").append(mLocale);
return sb.toString();
}
}

View File

@ -0,0 +1,248 @@
/**
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.android.inputmethod.dictionarypack;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.DialogPreference;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.android.inputmethod.latin.R;
import java.util.Locale;
/**
* A preference for one word list.
*
* This preference refers to a single word list, as available in the dictionary
* pack. Upon being pressed, it displays a menu to allow the user to install, disable,
* enable or delete it as appropriate for the current state of the word list.
*/
public final class WordListPreference extends DialogPreference {
static final private String TAG = WordListPreference.class.getSimpleName();
// What to display in the "status" field when we receive unknown data as a status from
// the content provider. Empty string sounds sensible.
static final private String NO_STATUS_MESSAGE = "";
/// Actions
static final private int ACTION_UNKNOWN = 0;
static final private int ACTION_ENABLE_DICT = 1;
static final private int ACTION_DISABLE_DICT = 2;
static final private int ACTION_DELETE_DICT = 3;
// Members
// The context to get resources
final Context mContext;
// The id of the client for which this preference is.
final String mClientId;
// The metadata word list id and version of this word list.
public final String mWordlistId;
public final int mVersion;
// The status
public int mStatus;
// Animation directions
static final private int ANIMATION_IN = 1;
static final private int ANIMATION_OUT = 2;
private static Button sLastClickedActionButton = null;
private final OnWordListPreferenceClick mPreferenceClickHandler =
new OnWordListPreferenceClick();
private final OnActionButtonClick mActionButtonClickHandler =
new OnActionButtonClick();
public WordListPreference(final Context context, final String clientId, final String wordlistId,
final int version, final Locale locale, final String description, final int status) {
super(context, null);
mContext = context;
mClientId = clientId;
mVersion = version;
mWordlistId = wordlistId;
setLayoutResource(R.layout.dictionary_line);
setTitle(description);
setStatus(status);
setKey(wordlistId);
}
private void setStatus(final int status) {
if (status == mStatus) return;
mStatus = status;
setSummary(getSummary(status));
// If we are currently displaying the dialog, we should update it, or at least
// dismiss it.
final Dialog dialog = getDialog();
if (null != dialog) {
dialog.dismiss();
}
}
private String getSummary(final int status) {
switch (status) {
// If we are deleting the word list, for the user it's like it's already deleted.
// It should be reinstallable. Exposing to the user the whole complexity of
// the delayed deletion process between the dictionary pack and Android Keyboard
// would only be confusing.
case MetadataDbHelper.STATUS_DELETING:
case MetadataDbHelper.STATUS_AVAILABLE:
return mContext.getString(R.string.dictionary_available);
case MetadataDbHelper.STATUS_DOWNLOADING:
return mContext.getString(R.string.dictionary_downloading);
case MetadataDbHelper.STATUS_INSTALLED:
return mContext.getString(R.string.dictionary_installed);
case MetadataDbHelper.STATUS_DISABLED:
return mContext.getString(R.string.dictionary_disabled);
default:
return NO_STATUS_MESSAGE;
}
}
private static final int sStatusActionList[][] = {
// MetadataDbHelper.STATUS_UNKNOWN
{},
// MetadataDbHelper.STATUS_AVAILABLE
{ R.string.install_dict, ACTION_ENABLE_DICT },
// MetadataDbHelper.STATUS_DOWNLOADING
{ R.string.cancel_download_dict, ACTION_DISABLE_DICT },
// MetadataDbHelper.STATUS_INSTALLED
{ R.string.delete_dict, ACTION_DELETE_DICT },
// MetadataDbHelper.STATUS_DISABLED
{ R.string.delete_dict, ACTION_DELETE_DICT },
// MetadataDbHelper.STATUS_DELETING
// We show 'install' because the file is supposed to be deleted.
// The user may reinstall it.
{ R.string.install_dict, ACTION_ENABLE_DICT }
};
private CharSequence getButtonLabel(final int status) {
if (status >= sStatusActionList.length) {
Log.e(TAG, "Unknown status " + status);
return "";
}
return mContext.getString(sStatusActionList[status][0]);
}
private static int getActionIdFromStatusAndMenuEntry(final int status) {
if (status >= sStatusActionList.length) {
Log.e(TAG, "Unknown status " + status);
return ACTION_UNKNOWN;
}
return sStatusActionList[status][1];
}
private void disableDict() {
SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext);
CommonPreferences.disable(prefs, mWordlistId);
UpdateHandler.markAsUnused(mContext, mClientId, mWordlistId, mVersion, mStatus);
if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) {
setStatus(MetadataDbHelper.STATUS_AVAILABLE);
} else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) {
// Interface-wise, we should no longer be able to come here. However, this is still
// the right thing to do if we do come here.
setStatus(MetadataDbHelper.STATUS_DISABLED);
} else {
Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus);
}
}
private void enableDict() {
SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext);
CommonPreferences.enable(prefs, mWordlistId);
// Explicit enabling by the user : allow downloading on metered data connection.
UpdateHandler.markAsUsed(mContext, mClientId, mWordlistId, mVersion, mStatus, true);
if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) {
setStatus(MetadataDbHelper.STATUS_DOWNLOADING);
} else if (MetadataDbHelper.STATUS_DISABLED == mStatus
|| MetadataDbHelper.STATUS_DELETING == mStatus) {
// If the status is DELETING, it means Android Keyboard
// has not deleted the word list yet, so we can safely
// turn it to 'installed'. The status DISABLED is still supported internally to
// avoid breaking older installations and all but there should not be a way to
// disable a word list through the interface any more.
setStatus(MetadataDbHelper.STATUS_INSTALLED);
} else {
Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus);
}
}
private void deleteDict() {
SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext);
CommonPreferences.disable(prefs, mWordlistId);
setStatus(MetadataDbHelper.STATUS_DELETING);
UpdateHandler.markAsDeleting(mContext, mClientId, mWordlistId, mVersion, mStatus);
}
@Override
protected void onBindView(final View view) {
super.onBindView(view);
((ViewGroup)view).setLayoutTransition(null);
final Button button = (Button)view.findViewById(R.id.wordlist_button);
button.setText(getButtonLabel(mStatus));
button.setVisibility(View.INVISIBLE);
button.setOnClickListener(mActionButtonClickHandler);
view.setOnClickListener(mPreferenceClickHandler);
}
private class OnWordListPreferenceClick implements View.OnClickListener {
@Override
public void onClick(final View v) {
final Button button = (Button)v.findViewById(R.id.wordlist_button);
if (null != sLastClickedActionButton) {
animateButton(sLastClickedActionButton, ANIMATION_OUT);
}
animateButton(button, ANIMATION_IN);
sLastClickedActionButton = button;
}
}
private void animateButton(final Button button, final int direction) {
final float outerX = ((View)button.getParent()).getWidth();
final float innerX = button.getX() - button.getTranslationX();
if (View.INVISIBLE == button.getVisibility()) {
button.setTranslationX(outerX - innerX);
button.setVisibility(View.VISIBLE);
}
if (ANIMATION_IN == direction) {
button.animate().translationX(0);
} else {
button.animate().translationX(outerX - innerX);
}
}
private class OnActionButtonClick implements View.OnClickListener {
@Override
public void onClick(final View v) {
switch (getActionIdFromStatusAndMenuEntry(mStatus)) {
case ACTION_ENABLE_DICT:
enableDict();
break;
case ACTION_DISABLE_DICT:
disableDict();
break;
case ACTION_DELETE_DICT:
deleteDict();
break;
default:
Log.e(TAG, "Unknown menu item pressed");
}
}
}
}

View File

@ -19,6 +19,7 @@ package com.android.inputmethod.latin;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.inputmethod.dictionarypack.DictionaryProvider;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@ -31,8 +32,7 @@ import java.util.Locale;
*/
public final class BinaryDictionary extends Dictionary {
private static final String TAG = BinaryDictionary.class.getSimpleName();
public static final String DICTIONARY_PACK_AUTHORITY =
"com.android.inputmethod.latin.dictionarypack";
public static final String DICTIONARY_PACK_AUTHORITY = DictionaryProvider.AUTHORITY;
// Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
private static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;

View File

@ -96,18 +96,8 @@ final class BinaryDictionaryGetter {
private static final class DictPackSettings {
final SharedPreferences mDictPreferences;
public DictPackSettings(final Context context) {
Context dictPackContext = null;
try {
final String dictPackName =
context.getString(R.string.dictionary_pack_package_name);
dictPackContext = context.createPackageContext(dictPackName, 0);
} catch (NameNotFoundException e) {
// The dictionary pack is not installed...
// TODO: fallback on the built-in dict, see the TODO above
Log.e(TAG, "Could not find a dictionary pack");
}
mDictPreferences = null == dictPackContext ? null
: dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME,
mDictPreferences = null == context ? null
: context.getSharedPreferences(COMMON_PREFERENCES_NAME,
Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
}
public boolean isWordListActive(final String dictId) {

View File

@ -16,6 +16,8 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.dictionarypack.UpdateHandler;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -34,7 +36,7 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
* The action of the intent for publishing that new dictionary data is available.
*/
/* package */ static final String NEW_DICTIONARY_INTENT_ACTION =
"com.android.inputmethod.latin.dictionarypack.newdict";
UpdateHandler.NEW_DICTIONARY_INTENT_ACTION;
public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
mService = service;

View File

@ -30,6 +30,7 @@ import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.dictionarypack.DictionarySettingsActivity;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager;
import com.android.inputmethodcommon.InputMethodSettingsFragment;
@ -146,6 +147,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment
final PreferenceScreen dictionaryLink =
(PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY);
final Intent intent = dictionaryLink.getIntent();
intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName());
final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
// TODO: The development-only-diagnostic version is not supported by the Dictionary Pack
// Service yet

View File

@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
import android.os.Binder;
import android.text.TextUtils;
import android.util.Log;
import android.view.textservice.SentenceSuggestionsInfo;
@ -133,22 +134,27 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
@Override
public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit, boolean sequentialWords) {
final int length = textInfos.length;
final SuggestionsInfo[] retval = new SuggestionsInfo[length];
for (int i = 0; i < length; ++i) {
final String prevWord;
if (sequentialWords && i > 0) {
long ident = Binder.clearCallingIdentity();
try {
final int length = textInfos.length;
final SuggestionsInfo[] retval = new SuggestionsInfo[length];
for (int i = 0; i < length; ++i) {
final String prevWord;
if (sequentialWords && i > 0) {
final String prevWordCandidate = textInfos[i - 1].getText();
// Note that an empty string would be used to indicate the initial word
// in the future.
prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
} else {
prevWord = null;
} else {
prevWord = null;
}
retval[i] = onGetSuggestionsInternal(textInfos[i], prevWord, suggestionsLimit);
retval[i].setCookieAndSequence(textInfos[i].getCookie(),
textInfos[i].getSequence());
}
retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit);
retval[i].setCookieAndSequence(textInfos[i].getCookie(),
textInfos[i].getSequence());
return retval;
} finally {
Binder.restoreCallingIdentity(ident);
}
return retval;
}
}

View File

@ -18,6 +18,7 @@ package com.android.inputmethod.latin.spellcheck;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.os.Binder;
import android.provider.UserDictionary.Words;
import android.service.textservice.SpellCheckerService.Session;
import android.text.TextUtils;
@ -234,13 +235,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
* corrections for the text passed as an argument. It may split or group words, and
* even perform grammatical analysis.
*/
@Override
public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
private SuggestionsInfo onGetSuggestionsInternal(final TextInfo textInfo,
final int suggestionsLimit) {
return onGetSuggestions(textInfo, null, suggestionsLimit);
return onGetSuggestionsInternal(textInfo, null, suggestionsLimit);
}
protected SuggestionsInfo onGetSuggestions(
protected SuggestionsInfo onGetSuggestionsInternal(
final TextInfo textInfo, final String prevWord, final int suggestionsLimit) {
try {
final String inText = textInfo.getText();
@ -357,4 +357,22 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
}
}
}
/*
* The spell checker acts on its own behalf. That is needed, in particular, to be able to
* access the dictionary files, which the provider restricts to the identity of Latin IME.
* Since it's called externally by the application, the spell checker is using the identity
* of the application by default unless we clearCallingIdentity.
* That's what the following method does.
*/
@Override
public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
final int suggestionsLimit) {
long ident = Binder.clearCallingIdentity();
try {
return onGetSuggestionsInternal(textInfo, suggestionsLimit);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}