diff options
30 files changed, 925 insertions, 109 deletions
diff --git a/.github/workflows/make-apk.yml b/.github/workflows/make-apk.yml index c7c7c5b..26fd7fd 100644 --- a/.github/workflows/make-apk.yml +++ b/.github/workflows/make-apk.yml @@ -10,7 +10,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + submodules: true - name: Restore debug keystore from GitHub Secrets run: | # Check if exist and use the secret named DEBUG_KEYSTORE diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..364e005 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/cdict"] + path = vendor/cdict + url = https://github.com/Julow/cdict diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fb47a5f..95d80b9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,6 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:hardwareAccelerated="false"> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:label="@string/app_name" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:hardwareAccelerated="false"> <service android:name="juloo.keyboard2.Keyboard2" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD" android:exported="true" android:directBootAware="true"> @@ -23,6 +25,12 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + + <activity android:name="juloo.keyboard2.dict.DictionariesActivity" android:icon="@mipmap/ic_launcher" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> </application> <!-- To query enabled input methods for voice IME detection --> diff --git a/build.gradle.kts b/build.gradle.kts index e111ac8..782b8db 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,7 +27,7 @@ android { sourceSets { named("main") { manifest.srcFile("AndroidManifest.xml") - java.srcDirs("srcs/juloo.keyboard2") + java.srcDirs("srcs/juloo.keyboard2", "vendor/cdict/java/juloo.cdict") res.srcDirs("res", "build/generated-resources") assets.srcDirs("assets") } @@ -37,6 +37,12 @@ android { } } + externalNativeBuild { + ndkBuild { + path = file("vendor/Android.mk") + } + } + signingConfigs { // Debug builds will always be signed. If no environment variables are set, a default // keystore will be initialized by the task initDebugKeystore and used. This keystore @@ -116,6 +122,7 @@ val genLayoutsList by tasks.registering(Exec::class) { val genMethodXml by tasks.registering(Exec::class) { val out = projectDir.resolve("res/xml/method.xml") inputs.file(projectDir.resolve("gen_method_xml.py")) + inputs.file(projectDir.resolve("res/values/dictionaries.xml")) outputs.file(out) doFirst { println("\nGenerating res/xml/method.xml") } doFirst { standardOutput = FileOutputStream(out) } diff --git a/gen_method_xml.py b/gen_method_xml.py index 3b1962f..c01f212 100644 --- a/gen_method_xml.py +++ b/gen_method_xml.py @@ -1,3 +1,4 @@ +import xml.etree.ElementTree as ET import itertools as it # This script generates res/xml/method.xml. @@ -78,37 +79,69 @@ LOCALES = [ ] # The locale that is at the beginning of the list -DEFAULT_LOCALE = loc("en", "latin", "latn_qwerty_us", tag="en") +DEFAULT_LOCALE = loc("en", "latin", "latn_qwerty_us", tag="en", dictionary="en_US") -def loc_to_subtype(loc): +def parse_dictionaries(): + tree = ET.parse("res/values/dictionaries.xml") + root = tree.getroot() + return set(( it.text for it in root.findall('*[@name="dictionaries_locale"]/item') )) + +# Available dictionares of the form "de" or "de_CH". +available_dictionaries = parse_dictionaries() + +def subtype_elem(root, loc): tag = loc["tag"].replace("_", "-") extra_keys = ",extra_keys=" + loc["extra_keys"] if "extra_keys" in loc else "" - return f'<subtype android:label="%s" android:languageTag="{tag}" android:imeSubtypeLocale="{loc["name"]}" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script={loc["script"]},default_layout={loc["default_layout"]}{extra_keys}"/>' + dictionaries = ",dictionary=" + loc["dictionary"] if loc["dictionary"] != None else "" + extra_value = f'script={loc["script"]},default_layout={loc["default_layout"]}{dictionaries}{extra_keys}' + ET.SubElement(root, "subtype", attrib={ + "android:label": "%s", + "android:languageTag": tag, + "android:imeSubtypeLocale": loc["name"], + "android:imeSubtypeMode": "keyboard", + "android:isAsciiCapable": "true", + "android:imeSubtypeExtraValue": extra_value + }) -# Return locales in sorted order with the 'tag' item added. -def compute_tags(): +# Return locales in sorted order with the "tag" and "dictionary" attributes +# added. +def compute_attrs(): + locales_grouped = {} # Locales grouped by language tag def lang(loc): return loc["name"].split("_")[0] - locales_grouped = { k: list(v) for k, v in it.groupby(sorted(LOCALES, key=lang), lang) } + for loc in LOCALES: + locales_grouped.setdefault(lang(loc), []).append(loc) def tag(loc): if "tag" in loc: return loc["tag"] l = lang(loc) if loc["name"] == f"{l}_{l.upper()}": return l # Locales like "fr_FR" # Return a short tag when it's not shared between several locales return l if len(locales_grouped[l]) == 1 else loc["name"] - return [ dict(tag=tag(loc), **loc) for loc in LOCALES ] + def dictionary(loc): + if loc["name"] in available_dictionaries: return loc["name"] + l = lang(loc) + if l in available_dictionaries: return l + return None + def add_attrs(loc): + return dict(tag=tag(loc), dictionary=dictionary(loc), **loc) + return map(add_attrs, LOCALES) def gen(): - locales = compute_tags() - print(f"""<?xml version="1.0" encoding="utf-8"?> -<input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="juloo.keyboard2.SettingsActivity" android:supportsSwitchingToNextInputMethod="true"> - <!-- This file is automatically generated. DO NOT EDIT. + locales = compute_attrs() + root = ET.Element("input-method", attrib={ + "xmlns:android": "http://schemas.android.com/apk/res/android", + "android:settingsActivity": "juloo.keyboard2.SettingsActivity", + "android:supportsSwitchingToNextInputMethod": "true", + }) + root.append(ET.Comment(text=""" This file is automatically generated. DO NOT EDIT. Locales definitions should go into 'gen_method_xml.py'. Update this file with 'gradle test'. - --> - {loc_to_subtype(DEFAULT_LOCALE)} - {"\n ".join(sorted(map(loc_to_subtype, locales)))} -</input-method>""") + """)) + subtype_elem(root, DEFAULT_LOCALE) + for loc in sorted(locales, key=lambda loc: loc["name"]): + subtype_elem(root, loc) + ET.indent(root) + print(ET.tostring(root, encoding="utf-8", xml_declaration=True).decode("UTF-8")) gen() diff --git a/res/drawable/ic_delete.xml b/res/drawable/ic_delete.xml index 5b69d0b..08a5f71 100644 --- a/res/drawable/ic_delete.xml +++ b/res/drawable/ic_delete.xml @@ -1 +1 @@ -<!-- drawable/delete.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" /></vector>
\ No newline at end of file +<!-- drawable/delete.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" /></vector> diff --git a/res/drawable/ic_download.xml b/res/drawable/ic_download.xml new file mode 100644 index 0000000..1fb7e98 --- /dev/null +++ b/res/drawable/ic_download.xml @@ -0,0 +1,2 @@ +<!-- From https://pictogrammers.com/library/mdi/icon/download/ --> +<!-- drawable/download.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" /></vector> diff --git a/res/layout/candidates_status_no_dict.xml b/res/layout/candidates_status_no_dict.xml index 6ba20b9..8bd1e37 100644 --- a/res/layout/candidates_status_no_dict.xml +++ b/res/layout/candidates_status_no_dict.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/candidates_status"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/candidates_status_no_dict"/> - <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/candidates_status_install"/> + <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/candidates_status_install" android:onClick="launch_dictionaries_activity"/> </LinearLayout> diff --git a/res/layout/dictionaries_activity.xml b/res/layout/dictionaries_activity.xml new file mode 100644 index 0000000..96b1bcb --- /dev/null +++ b/res/layout/dictionaries_activity.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:fitsSystemWindows="true"> + <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> + <TextView style="@style/paragraph" android:text="@string/dictionaries_from_internet"/> + <juloo.keyboard2.dict.DictionaryListView style="@style/dictionary_list_view"/> + </LinearLayout> +</ScrollView> diff --git a/res/layout/dictionary_download_item.xml b/res/layout/dictionary_download_item.xml new file mode 100644 index 0000000..e6dd3c6 --- /dev/null +++ b/res/layout/dictionary_download_item.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/dictionary_download_item"> + <TextView android:id="@+id/dictionary_download_locale" style="@style/dictionary_download_locale"/> + <TextView android:id="@+id/dictionary_download_size" style="@style/dictionary_download_size"/> + <View android:id="@+id/dictionary_download_button" style="@style/dictionary_download_button" android:background="@drawable/ic_download"/> +</LinearLayout> diff --git a/res/layout/launcher_activity.xml b/res/layout/launcher_activity.xml index 20df09a..b4c23ab 100644 --- a/res/layout/launcher_activity.xml +++ b/res/layout/launcher_activity.xml @@ -4,6 +4,7 @@ <TextView style="@style/paragraph" android:text="@string/launcher_description"/> <Button style="@style/paragraph" android:text="@string/launcher_button_imesettings" android:onClick="launch_imesettings" android:layout_width="wrap_content"/> <Button style="@style/paragraph" android:text="@string/launcher_button_imepicker" android:onClick="launch_imepicker" android:layout_width="wrap_content"/> + <Button style="@style/paragraph" android:text="@string/launcher_button_dictionaries" android:onClick="launch_dictionaries_activity" android:layout_width="wrap_content"/> <LinearLayout style="@style/anim_box"> <ImageView style="@style/anim" android:id="@+id/launcher_anim_swipe" android:background="@drawable/doc_key_u" android:src="@drawable/doc_anim_swipe"/> <TextView style="@style/anim_text" android:text="@string/launcher_anim_7"/> diff --git a/res/values/dictionaries.xml b/res/values/dictionaries.xml new file mode 100644 index 0000000..f3e73fd --- /dev/null +++ b/res/values/dictionaries.xml @@ -0,0 +1,237 @@ +<?xml version='1.0' encoding='utf-8'?> +<resources> + <string name="dict_name_ar">Arabic</string> + <string name="dict_name_as">Assamese</string> + <string name="dict_name_be">Belarusian</string> + <string name="dict_name_bg">Bulgarian</string> + <string name="dict_name_bn">Bangla</string> + <string name="dict_name_bs">Bosnian</string> + <string name="dict_name_ca">Catalan</string> + <string name="dict_name_cs">Czech</string> + <string name="dict_name_da">Danish</string> + <string name="dict_name_de">German</string> + <string name="dict_name_de_ch">German (Switzerland)</string> + <string name="dict_name_el">Greek</string> + <string name="dict_name_en_au">English (Australia)</string> + <string name="dict_name_en_gb">English (United Kingdom)</string> + <string name="dict_name_en_us">English (United States)</string> + <string name="dict_name_es">Spanish</string> + <string name="dict_name_eu">Basque</string> + <string name="dict_name_fi">Finnish</string> + <string name="dict_name_fr">French</string> + <string name="dict_name_gl">Galician</string> + <string name="dict_name_gu">Gujarati</string> + <string name="dict_name_hi">Hindi</string> + <string name="dict_name_hr">Croatian</string> + <string name="dict_name_hu">Hungarian</string> + <string name="dict_name_hy">Armenian</string> + <string name="dict_name_it">Italian</string> + <string name="dict_name_iw">Hebrew</string> + <string name="dict_name_ka">Georgian</string> + <string name="dict_name_km">Khmer</string> + <string name="dict_name_kn">Kannada</string> + <string name="dict_name_lb">Luxembourgish</string> + <string name="dict_name_lt">Lithuanian</string> + <string name="dict_name_lv">Latvian</string> + <string name="dict_name_mai">Maithili</string> + <string name="dict_name_ml">Malayalam</string> + <string name="dict_name_mr">Marathi</string> + <string name="dict_name_nb">Norwegian Bokmål</string> + <string name="dict_name_nl">Dutch</string> + <string name="dict_name_or">Odia</string> + <string name="dict_name_pa">Punjabi</string> + <string name="dict_name_pl">Polish</string> + <string name="dict_name_pt_br">Portuguese (Brazil)</string> + <string name="dict_name_pt_pt">Portuguese (Portugal)</string> + <string name="dict_name_ro">Romanian</string> + <string name="dict_name_ru">Russian</string> + <string name="dict_name_sa">Sanskrit</string> + <string name="dict_name_sat">Santali</string> + <string name="dict_name_sd">Sindhi</string> + <string name="dict_name_sl">Slovenian</string> + <string name="dict_name_sr">Serbian</string> + <string name="dict_name_sv">Swedish</string> + <string name="dict_name_ta">Tamil</string> + <string name="dict_name_te">Telugu</string> + <string name="dict_name_tr">Turkish</string> + <string name="dict_name_uk">Ukrainian</string> + <string name="dict_name_ur">Urdu</string> + <string name="dict_name_zgh">Standard Moroccan Tamazight</string> + <string-array name="dictionaries_locale"> + <item>ar</item> + <item>as</item> + <item>be</item> + <item>bg</item> + <item>bn</item> + <item>bs</item> + <item>ca</item> + <item>cs</item> + <item>da</item> + <item>de</item> + <item>de_CH</item> + <item>el</item> + <item>en_AU</item> + <item>en_GB</item> + <item>en_US</item> + <item>es</item> + <item>eu</item> + <item>fi</item> + <item>fr</item> + <item>gl</item> + <item>gu</item> + <item>hi</item> + <item>hr</item> + <item>hu</item> + <item>hy</item> + <item>it</item> + <item>iw</item> + <item>ka</item> + <item>km</item> + <item>kn</item> + <item>lb</item> + <item>lt</item> + <item>lv</item> + <item>mai</item> + <item>ml</item> + <item>mr</item> + <item>nb</item> + <item>nl</item> + <item>or</item> + <item>pa</item> + <item>pl</item> + <item>pt_BR</item> + <item>pt_PT</item> + <item>ro</item> + <item>ru</item> + <item>sa</item> + <item>sat</item> + <item>sd</item> + <item>sl</item> + <item>sr</item> + <item>sv</item> + <item>ta</item> + <item>te</item> + <item>tr</item> + <item>uk</item> + <item>ur</item> + <item>zgh</item> + </string-array> + <string-array name="dictionaries_name"> + <item>@string/dict_name_ar</item> + <item>@string/dict_name_as</item> + <item>@string/dict_name_be</item> + <item>@string/dict_name_bg</item> + <item>@string/dict_name_bn</item> + <item>@string/dict_name_bs</item> + <item>@string/dict_name_ca</item> + <item>@string/dict_name_cs</item> + <item>@string/dict_name_da</item> + <item>@string/dict_name_de</item> + <item>@string/dict_name_de_ch</item> + <item>@string/dict_name_el</item> + <item>@string/dict_name_en_au</item> + <item>@string/dict_name_en_gb</item> + <item>@string/dict_name_en_us</item> + <item>@string/dict_name_es</item> + <item>@string/dict_name_eu</item> + <item>@string/dict_name_fi</item> + <item>@string/dict_name_fr</item> + <item>@string/dict_name_gl</item> + <item>@string/dict_name_gu</item> + <item>@string/dict_name_hi</item> + <item>@string/dict_name_hr</item> + <item>@string/dict_name_hu</item> + <item>@string/dict_name_hy</item> + <item>@string/dict_name_it</item> + <item>@string/dict_name_iw</item> + <item>@string/dict_name_ka</item> + <item>@string/dict_name_km</item> + <item>@string/dict_name_kn</item> + <item>@string/dict_name_lb</item> + <item>@string/dict_name_lt</item> + <item>@string/dict_name_lv</item> + <item>@string/dict_name_mai</item> + <item>@string/dict_name_ml</item> + <item>@string/dict_name_mr</item> + <item>@string/dict_name_nb</item> + <item>@string/dict_name_nl</item> + <item>@string/dict_name_or</item> + <item>@string/dict_name_pa</item> + <item>@string/dict_name_pl</item> + <item>@string/dict_name_pt_br</item> + <item>@string/dict_name_pt_pt</item> + <item>@string/dict_name_ro</item> + <item>@string/dict_name_ru</item> + <item>@string/dict_name_sa</item> + <item>@string/dict_name_sat</item> + <item>@string/dict_name_sd</item> + <item>@string/dict_name_sl</item> + <item>@string/dict_name_sr</item> + <item>@string/dict_name_sv</item> + <item>@string/dict_name_ta</item> + <item>@string/dict_name_te</item> + <item>@string/dict_name_tr</item> + <item>@string/dict_name_uk</item> + <item>@string/dict_name_ur</item> + <item>@string/dict_name_zgh</item> + </string-array> + <integer-array name="dictionaries_size"> + <item>455658</item> + <item>307451</item> + <item>1745968</item> + <item>347735</item> + <item>301586</item> + <item>481783</item> + <item>335099</item> + <item>657214</item> + <item>933060</item> + <item>1071580</item> + <item>1075177</item> + <item>964195</item> + <item>645068</item> + <item>644719</item> + <item>650605</item> + <item>635119</item> + <item>359849</item> + <item>1003794</item> + <item>871636</item> + <item>240926</item> + <item>279333</item> + <item>301928</item> + <item>727487</item> + <item>331818</item> + <item>948362</item> + <item>696974</item> + <item>352933</item> + <item>488139</item> + <item>263411</item> + <item>286588</item> + <item>331897</item> + <item>736799</item> + <item>854603</item> + <item>333644</item> + <item>354875</item> + <item>301259</item> + <item>776481</item> + <item>926933</item> + <item>357107</item> + <item>107120</item> + <item>740895</item> + <item>705536</item> + <item>996904</item> + <item>1421447</item> + <item>907625</item> + <item>342293</item> + <item>322250</item> + <item>256078</item> + <item>228729</item> + <item>740293</item> + <item>916845</item> + <item>297338</item> + <item>304295</item> + <item>690598</item> + <item>1179318</item> + <item>265324</item> + <item>628168</item> + </integer-array> +</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index e01e98f..d46b828 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -108,6 +108,7 @@ <string name="key_action_send">Send</string> <string name="launcher_button_imesettings">Enable keyboard</string> <string name="launcher_button_imepicker">Select keyboard</string> + <string name="launcher_button_dictionaries">Install dictionaries</string> <string name="launcher_description">This application is a virtual keyboard. Go to the system settings by clicking on the button below and enable Unexpected-Keyboard.</string> <string name="launcher_sourcecode">This is a free and open source application. You can find the source code or report bugs on GitHub.</string> <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> @@ -154,6 +155,9 @@ <string name="pref_clipboard_history_duration_30">At most 30 minutes</string> <string name="pref_clipboard_history_duration_stop">Until the app stops</string> <string name="pref_dialog_edit_text">Custom definition</string> + <string name="dictionaries_from_internet">Download a dictionary from the Internet</string> + <string name="dictionaries_download_success">Dictionary installed</string> + <string name="dictionaries_download_failed">Download failed</string> <string name="candidates_status_no_dict">No dictionary installed</string> <string name="candidates_status_install">Install</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 5defb7b..6977e00 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -64,7 +64,7 @@ <item name="android:layout_width">fill_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginVertical">8dp</item> - <item name="android:layout_marginHorizontal">16dp</item> + <item name="android:layout_marginHorizontal">24dp</item> <item name="android:layout_gravity">center</item> </style> <style name="anim"> @@ -90,6 +90,37 @@ <item name="android:layout_width">wrap_content</item> <item name="android:orientation">horizontal</item> </style> + <!-- Dictionaries activity --> + <style name="dictionary_list_view"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginVertical">24dp</item> + </style> + <style name="dictionary_download_item"> + <item name="android:orientation">horizontal</item> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginVertical">8dp</item> + <item name="android:gravity">center</item> + <item name="android:paddingHorizontal">48dp</item> + </style> + <style name="dictionary_download_locale"> + <item name="android:layout_weight">1</item> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textSize">16sp</item> + </style> + <style name="dictionary_download_size"> + <item name="android:layout_weight">0</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginRight">8dp</item> + <item name="android:textSize">12sp</item> + </style> + <style name="dictionary_download_button"> + <item name="android:layout_width">24dp</item> + <item name="android:layout_height">24dp</item> + </style> <style name="appTheme" parent="@android:style/Theme.Material.Light"/> <style name="settingsTheme" parent="appTheme"> <!-- Setting this in the activity theme so it propagate to nested diff --git a/res/xml/method.xml b/res/xml/method.xml index eb5b04b..4778722 100644 --- a/res/xml/method.xml +++ b/res/xml/method.xml @@ -1,74 +1,74 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version='1.0' encoding='utf-8'?> <input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="juloo.keyboard2.SettingsActivity" android:supportsSwitchingToNextInputMethod="true"> <!-- This file is automatically generated. DO NOT EDIT. Locales definitions should go into 'gen_method_xml.py'. Update this file with 'gradle test'. --> - <subtype android:label="%s" android:languageTag="en" android:imeSubtypeLocale="en" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="ar" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=arabic,default_layout=arab_pc_hindu"/> - <subtype android:label="%s" android:languageTag="ar-TN" android:imeSubtypeLocale="ar_TN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=arabic,default_layout=arab_pc"/> - <subtype android:label="%s" android:languageTag="ay" android:imeSubtypeLocale="ay_AM" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=armenian,default_layout=armenian_ph_am"/> - <subtype android:label="%s" android:languageTag="az" android:imeSubtypeLocale="az_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_az,extra_keys=accent_trema:ü:ö@w|accent_cedille:ç:ş@s|ğ@g|ı@k|ə@l"/> - <subtype android:label="%s" android:languageTag="be" android:imeSubtypeLocale="be_BY" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_ru,extra_keys=ґ|є|і|ї|ў"/> - <subtype android:label="%s" android:languageTag="bg" android:imeSubtypeLocale="bg_BG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_ueishsht,extra_keys=€"/> - <subtype android:label="%s" android:languageTag="bn" android:imeSubtypeLocale="bn_BD" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=৳"/> - <subtype android:label="%s" android:languageTag="cs" android:imeSubtypeLocale="cs_CZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_cz,extra_keys=accent_aigu:á:é:í:ó:ú:ý@d|accent_ring:ů@s|accent_caron:č:ě:ň:ř:š:ž:ď:ť@f"/> - <subtype android:label="%s" android:languageTag="cy" android:imeSubtypeLocale="cy_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_cy"/> - <subtype android:label="%s" android:languageTag="da" android:imeSubtypeLocale="da_DK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_da,extra_keys=€|æ|å|ø"/> - <subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_de,extra_keys=accent_trema:ä:ö:ü@u|ß|€"/> - <subtype android:label="%s" android:languageTag="de-BE" android:imeSubtypeLocale="de_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,extra_keys=accent_grave:è@f|accent_aigu:á:é:í:ó:ú:ý:j́@d|accent_circonflexe:ê@f|accent_cedille:ç@c|accent_trema@u|€"/> - <subtype android:label="%s" android:languageTag="el" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=grek_qwerty,extra_keys=£@l|€"/> - <subtype android:label="%s" android:languageTag="en-AU" android:imeSubtypeLocale="en_AU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="en-CA" android:imeSubtypeLocale="en_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="en-GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_gb,extra_keys=£@l"/> - <subtype android:label="%s" android:languageTag="en-IN" android:imeSubtypeLocale="en_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="en-NG" android:imeSubtypeLocale="en_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦"/> - <subtype android:label="%s" android:languageTag="en-US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_es,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_tilde:ñ@n|accent_grave@f|accent_trema@u|€"/> - <subtype android:label="%s" android:languageTag="et" android:imeSubtypeLocale="et_EE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_et,extra_keys=accent_trema:ä:ö:ü@u|accent_tilde:õ@o|accent_caron:š:ž@s|€"/> - <subtype android:label="%s" android:languageTag="fa" android:imeSubtypeLocale="fa_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=persian,default_layout=arab_pc_ir"/> - <subtype android:label="%s" android:languageTag="fr" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,extra_keys=accent_grave:à:è:ù@d|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@o|accent_cedille:ç@c|accent_trema:ë:ï:ü@l|€"/> - <subtype android:label="%s" android:languageTag="fr-BE" android:imeSubtypeLocale="fr_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:ê:û@f|accent_cedille:ç@c|accent_trema@u|€"/> - <subtype android:label="%s" android:languageTag="fr-CA" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@f|accent_cedille:ç@c|accent_trema:ë:ï:ü:ÿ@u"/> - <subtype android:label="%s" android:languageTag="fr-CH" android:imeSubtypeLocale="fr_CH" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_fr_ch,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@o|accent_cedille:ç@c|accent_trema:ë:ï:ü:ÿ@u|€"/> - <subtype android:label="%s" android:languageTag="ga" android:imeSubtypeLocale="ga_IE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_ga,extra_keys=accent_aigu:á:é:í:ó:ú@k|accent_dot_above@l"/> - <subtype android:label="%s" android:languageTag="ha" android:imeSubtypeLocale="ha_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ɓ|ɗ|ƙ|’|ƴ|r̃"/> - <subtype android:label="%s" android:languageTag="haw" android:imeSubtypeLocale="haw_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_haw,extra_keys=ʻ@l|accent_macron:ā:ē:ī:ō:ū@m"/> - <subtype android:label="%s" android:languageTag="he" android:imeSubtypeLocale="he_IL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hebrew,default_layout=default_layout=hebr_1_il,extra_keys=₪@r|€"/> - <subtype android:label="%s" android:languageTag="hi" android:imeSubtypeLocale="hi_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹"/> - <subtype android:label="%s" android:languageTag="hu" android:imeSubtypeLocale="hu_HU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_hu,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_trema:ö:ü@u|accent_ogonek@s|accent_double_aigu:ő:ű@k|€"/> - <subtype android:label="%s" android:languageTag="ig" android:imeSubtypeLocale="ig_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ṅ|ọ|ụ"/> - <subtype android:label="%s" android:languageTag="is" android:imeSubtypeLocale="is_IS" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_is,extra_keys=ð|þ|æ|accent_trema:ö@o|accent_aigu:á:é:í:ó:ú:ý@d|accent_circonflexe|accent_ring|accent_grave"/> - <subtype android:label="%s" android:languageTag="it" android:imeSubtypeLocale="it_IT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=accent_grave:à:è:ì:ò:ù@f|accent_aigu:é:ó@d|accent_circonflexe:î@f|€|ə"/> - <subtype android:label="%s" android:languageTag="ka" android:imeSubtypeLocale="ka_GE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=georgian_mes"/> - <subtype android:label="%s" android:languageTag="kk" android:imeSubtypeLocale="kk_KZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_kk"/> - <subtype android:label="%s" android:languageTag="kn" android:imeSubtypeLocale="kn_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=kannada,default_layout=kann_kannada"/> - <subtype android:label="%s" android:languageTag="ko" android:imeSubtypeLocale="ko_KR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hangul,default_layout=latn_qwerty_us"/> - <subtype android:label="%s" android:languageTag="lt" android:imeSubtypeLocale="lt_LT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_lt,extra_keys=accent_ogonek:ą:ę:į:ų@s|accent_caron:č:š:ž@f|accent_dot_above:ė@s|accent_macron:ū@o|€"/> - <subtype android:label="%s" android:languageTag="lv" android:imeSubtypeLocale="lv_LV" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_lv,extra_keys=accent_macron:ā:ē:ī:ū@o|accent_caron:č:š:ž@f|accent_ogonek:ķ:ļ:ņ@s|accent_cedille:ģ@c|€"/> - <subtype android:label="%s" android:languageTag="mk" android:imeSubtypeLocale="mk" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_lynyertdz_mk,extra_keys=ѕ|ѓ|ќ|ѝ|ѐ|љ|њ|џ|„|“|€"/> - <subtype android:label="%s" android:languageTag="mn" android:imeSubtypeLocale="mn_MN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_fcuzhen_mn,extra_keys=ү|ө"/> - <subtype android:label="%s" android:languageTag="mr" android:imeSubtypeLocale="mr_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹"/> - <subtype android:label="%s" android:languageTag="mt" android:imeSubtypeLocale="mt_MT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_mt,extra_keys=accent_grave:à:è:ì:ò:ù|accent_dot_above:ċ:ż:ġ|ħ"/> - <subtype android:label="%s" android:languageTag="ne" android:imeSubtypeLocale="ne_NE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹"/> - <subtype android:label="%s" android:languageTag="nl" android:imeSubtypeLocale="nl_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,extra_keys=accent_grave:è@f|accent_aigu:á:é:í:ó:ú:ý:j́@d|accent_circonflexe:ê@f|accent_cedille:ç@c|accent_trema@u|€"/> - <subtype android:label="%s" android:languageTag="no" android:imeSubtypeLocale="no_NO" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=€|æ@a|å@a|ø@o|accent_aigu:é:ó@d|accent_grave:è:ò:ù@f|accent_circonflexe:ê:ô@f"/> - <subtype android:label="%s" android:languageTag="pl" android:imeSubtypeLocale="pl_PL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_pl"/> - <subtype android:label="%s" android:languageTag="pt" android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_pt,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_cedille:ç@c|accent_circonflexe:â:ê:ô@f|accent_grave:à:ò@f|accent_tilde:ã:õ@n|€|ª|º"/> - <subtype android:label="%s" android:languageTag="ro" android:imeSubtypeLocale="ro_RO" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_ro,extra_keys=ă|â|î|ș|ț|€|$"/> - <subtype android:label="%s" android:languageTag="ru" android:imeSubtypeLocale="ru_RU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_ru"/> - <subtype android:label="%s" android:languageTag="si" android:imeSubtypeLocale="si_LK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=sinhala,default_layout=sinhala_phonetic,extra_keys=₨"/> - <subtype android:label="%s" android:languageTag="sk" android:imeSubtypeLocale="sk_SK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sk,extra_keys=accent_caron:ě:ř:ž:š:č:ň:ď:ľ:ť@f|accent_ring:ů@s|accent_circonflexe:ô@f|accent_trema:ä:ü:ö@u|accent_aigu:á:é:í:ó:ú:ŕ:ś:ĺ:ý@d"/> - <subtype android:label="%s" android:languageTag="sq" android:imeSubtypeLocale="sq_AL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sq"/> - <subtype android:label="%s" android:languageTag="sr" android:imeSubtypeLocale="sr_" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_lynyertz_sr"/> - <subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_se,extra_keys=accent_aigu:á@d|accent_trema:ä:ö@o|accent_ring:å@s|€"/> - <subtype android:label="%s" android:languageTag="ta" android:imeSubtypeLocale="ta_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=tamil,default_layout=tamil_default"/> - <subtype android:label="%s" android:languageTag="tly-AZ" android:imeSubtypeLocale="tly_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tly,extra_keys=á|ú|â|ê|ı|š|ž"/> - <subtype android:label="%s" android:languageTag="tly-IR" android:imeSubtypeLocale="tly_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=persian,default_layout=arab_hamvaj_tly"/> - <subtype android:label="%s" android:languageTag="tr" android:imeSubtypeLocale="tr_TR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tr,extra_keys=accent_cedille:ç:ş@c|accent_trema:ö:ü@u|accent_circonflexe:â:î:û@f|₺|ı|ğ"/> - <subtype android:label="%s" android:languageTag="uk" android:imeSubtypeLocale="uk_UA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_uk,extra_keys=ґ|є|і|ї|₴"/> - <subtype android:label="%s" android:languageTag="uz" android:imeSubtypeLocale="uz_UZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_uz,extra_keys=ʻ|ʼ"/> - <subtype android:label="%s" android:languageTag="vi" android:imeSubtypeLocale="vi_VN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_vi"/> - <subtype android:label="%s" android:languageTag="yo" android:imeSubtypeLocale="yo_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ẹ|ọ|ṣ"/> + <subtype android:label="%s" android:languageTag="en" android:imeSubtypeLocale="en" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,dictionary=en_US" /> + <subtype android:label="%s" android:languageTag="ar" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=arabic,default_layout=arab_pc_hindu,dictionary=ar" /> + <subtype android:label="%s" android:languageTag="ar-TN" android:imeSubtypeLocale="ar_TN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=arabic,default_layout=arab_pc,dictionary=ar" /> + <subtype android:label="%s" android:languageTag="ay" android:imeSubtypeLocale="ay_AM" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=armenian,default_layout=armenian_ph_am" /> + <subtype android:label="%s" android:languageTag="az" android:imeSubtypeLocale="az_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_az,extra_keys=accent_trema:ü:ö@w|accent_cedille:ç:ş@s|ğ@g|ı@k|ə@l" /> + <subtype android:label="%s" android:languageTag="be" android:imeSubtypeLocale="be_BY" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_ru,dictionary=be,extra_keys=ґ|є|і|ї|ў" /> + <subtype android:label="%s" android:languageTag="bg" android:imeSubtypeLocale="bg_BG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_ueishsht,dictionary=bg,extra_keys=€" /> + <subtype android:label="%s" android:languageTag="bn" android:imeSubtypeLocale="bn_BD" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,dictionary=bn,extra_keys=৳" /> + <subtype android:label="%s" android:languageTag="cs" android:imeSubtypeLocale="cs_CZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_cz,dictionary=cs,extra_keys=accent_aigu:á:é:í:ó:ú:ý@d|accent_ring:ů@s|accent_caron:č:ě:ň:ř:š:ž:ď:ť@f" /> + <subtype android:label="%s" android:languageTag="cy" android:imeSubtypeLocale="cy_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_cy" /> + <subtype android:label="%s" android:languageTag="da" android:imeSubtypeLocale="da_DK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_da,dictionary=da,extra_keys=€|æ|å|ø" /> + <subtype android:label="%s" android:languageTag="de-BE" android:imeSubtypeLocale="de_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,dictionary=de,extra_keys=accent_grave:è@f|accent_aigu:á:é:í:ó:ú:ý:j́@d|accent_circonflexe:ê@f|accent_cedille:ç@c|accent_trema@u|€" /> + <subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_de,dictionary=de,extra_keys=accent_trema:ä:ö:ü@u|ß|€" /> + <subtype android:label="%s" android:languageTag="el" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=grek_qwerty,dictionary=el,extra_keys=£@l|€" /> + <subtype android:label="%s" android:languageTag="en-AU" android:imeSubtypeLocale="en_AU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,dictionary=en_AU" /> + <subtype android:label="%s" android:languageTag="en-CA" android:imeSubtypeLocale="en_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us" /> + <subtype android:label="%s" android:languageTag="en-GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_gb,dictionary=en_GB,extra_keys=£@l" /> + <subtype android:label="%s" android:languageTag="en-IN" android:imeSubtypeLocale="en_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us" /> + <subtype android:label="%s" android:languageTag="en-NG" android:imeSubtypeLocale="en_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦" /> + <subtype android:label="%s" android:languageTag="en-US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,dictionary=en_US" /> + <subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_es,dictionary=es,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_tilde:ñ@n|accent_grave@f|accent_trema@u|€" /> + <subtype android:label="%s" android:languageTag="et" android:imeSubtypeLocale="et_EE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_et,extra_keys=accent_trema:ä:ö:ü@u|accent_tilde:õ@o|accent_caron:š:ž@s|€" /> + <subtype android:label="%s" android:languageTag="fa" android:imeSubtypeLocale="fa_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=persian,default_layout=arab_pc_ir" /> + <subtype android:label="%s" android:languageTag="fr-BE" android:imeSubtypeLocale="fr_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,dictionary=fr,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:ê:û@f|accent_cedille:ç@c|accent_trema@u|€" /> + <subtype android:label="%s" android:languageTag="fr-CA" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,dictionary=fr,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@f|accent_cedille:ç@c|accent_trema:ë:ï:ü:ÿ@u" /> + <subtype android:label="%s" android:languageTag="fr-CH" android:imeSubtypeLocale="fr_CH" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_fr_ch,dictionary=fr,extra_keys=accent_grave:à:è:ù@f|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@o|accent_cedille:ç@c|accent_trema:ë:ï:ü:ÿ@u|€" /> + <subtype android:label="%s" android:languageTag="fr" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,dictionary=fr,extra_keys=accent_grave:à:è:ù@d|accent_aigu:é@d|accent_circonflexe:â:ê:ô:û@o|accent_cedille:ç@c|accent_trema:ë:ï:ü@l|€" /> + <subtype android:label="%s" android:languageTag="ga" android:imeSubtypeLocale="ga_IE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_ga,extra_keys=accent_aigu:á:é:í:ó:ú@k|accent_dot_above@l" /> + <subtype android:label="%s" android:languageTag="ha" android:imeSubtypeLocale="ha_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ɓ|ɗ|ƙ|’|ƴ|r̃" /> + <subtype android:label="%s" android:languageTag="haw" android:imeSubtypeLocale="haw_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_haw,extra_keys=ʻ@l|accent_macron:ā:ē:ī:ō:ū@m" /> + <subtype android:label="%s" android:languageTag="he" android:imeSubtypeLocale="he_IL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hebrew,default_layout=default_layout=hebr_1_il,extra_keys=₪@r|€" /> + <subtype android:label="%s" android:languageTag="hi" android:imeSubtypeLocale="hi_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,dictionary=hi,extra_keys=₹" /> + <subtype android:label="%s" android:languageTag="hu" android:imeSubtypeLocale="hu_HU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_hu,dictionary=hu,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_trema:ö:ü@u|accent_ogonek@s|accent_double_aigu:ő:ű@k|€" /> + <subtype android:label="%s" android:languageTag="ig" android:imeSubtypeLocale="ig_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ṅ|ọ|ụ" /> + <subtype android:label="%s" android:languageTag="is" android:imeSubtypeLocale="is_IS" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_is,extra_keys=ð|þ|æ|accent_trema:ö@o|accent_aigu:á:é:í:ó:ú:ý@d|accent_circonflexe|accent_ring|accent_grave" /> + <subtype android:label="%s" android:languageTag="it" android:imeSubtypeLocale="it_IT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,dictionary=it,extra_keys=accent_grave:à:è:ì:ò:ù@f|accent_aigu:é:ó@d|accent_circonflexe:î@f|€|ə" /> + <subtype android:label="%s" android:languageTag="ka" android:imeSubtypeLocale="ka_GE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=georgian_mes,dictionary=ka" /> + <subtype android:label="%s" android:languageTag="kk" android:imeSubtypeLocale="kk_KZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_kk" /> + <subtype android:label="%s" android:languageTag="kn" android:imeSubtypeLocale="kn_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=kannada,default_layout=kann_kannada,dictionary=kn" /> + <subtype android:label="%s" android:languageTag="ko" android:imeSubtypeLocale="ko_KR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hangul,default_layout=latn_qwerty_us" /> + <subtype android:label="%s" android:languageTag="lt" android:imeSubtypeLocale="lt_LT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_lt,dictionary=lt,extra_keys=accent_ogonek:ą:ę:į:ų@s|accent_caron:č:š:ž@f|accent_dot_above:ė@s|accent_macron:ū@o|€" /> + <subtype android:label="%s" android:languageTag="lv" android:imeSubtypeLocale="lv_LV" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_lv,dictionary=lv,extra_keys=accent_macron:ā:ē:ī:ū@o|accent_caron:č:š:ž@f|accent_ogonek:ķ:ļ:ņ@s|accent_cedille:ģ@c|€" /> + <subtype android:label="%s" android:languageTag="mk" android:imeSubtypeLocale="mk" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_lynyertdz_mk,extra_keys=ѕ|ѓ|ќ|ѝ|ѐ|љ|њ|џ|„|“|€" /> + <subtype android:label="%s" android:languageTag="mn" android:imeSubtypeLocale="mn_MN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_fcuzhen_mn,extra_keys=ү|ө" /> + <subtype android:label="%s" android:languageTag="mr" android:imeSubtypeLocale="mr_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,dictionary=mr,extra_keys=₹" /> + <subtype android:label="%s" android:languageTag="mt" android:imeSubtypeLocale="mt_MT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_mt,extra_keys=accent_grave:à:è:ì:ò:ù|accent_dot_above:ċ:ż:ġ|ħ" /> + <subtype android:label="%s" android:languageTag="ne" android:imeSubtypeLocale="ne_NE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹" /> + <subtype android:label="%s" android:languageTag="nl" android:imeSubtypeLocale="nl_BE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_be,dictionary=nl,extra_keys=accent_grave:è@f|accent_aigu:á:é:í:ó:ú:ý:j́@d|accent_circonflexe:ê@f|accent_cedille:ç@c|accent_trema@u|€" /> + <subtype android:label="%s" android:languageTag="no" android:imeSubtypeLocale="no_NO" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=€|æ@a|å@a|ø@o|accent_aigu:é:ó@d|accent_grave:è:ò:ù@f|accent_circonflexe:ê:ô@f" /> + <subtype android:label="%s" android:languageTag="pl" android:imeSubtypeLocale="pl_PL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_pl,dictionary=pl" /> + <subtype android:label="%s" android:languageTag="pt" android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_pt,dictionary=pt_BR,extra_keys=accent_aigu:á:é:í:ó:ú@d|accent_cedille:ç@c|accent_circonflexe:â:ê:ô@f|accent_grave:à:ò@f|accent_tilde:ã:õ@n|€|ª|º" /> + <subtype android:label="%s" android:languageTag="ro" android:imeSubtypeLocale="ro_RO" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_ro,dictionary=ro,extra_keys=ă|â|î|ș|ț|€|$" /> + <subtype android:label="%s" android:languageTag="ru" android:imeSubtypeLocale="ru_RU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_ru,dictionary=ru" /> + <subtype android:label="%s" android:languageTag="si" android:imeSubtypeLocale="si_LK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=sinhala,default_layout=sinhala_phonetic,extra_keys=₨" /> + <subtype android:label="%s" android:languageTag="sk" android:imeSubtypeLocale="sk_SK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sk,extra_keys=accent_caron:ě:ř:ž:š:č:ň:ď:ľ:ť@f|accent_ring:ů@s|accent_circonflexe:ô@f|accent_trema:ä:ü:ö@u|accent_aigu:á:é:í:ó:ú:ŕ:ś:ĺ:ý@d" /> + <subtype android:label="%s" android:languageTag="sq" android:imeSubtypeLocale="sq_AL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sq" /> + <subtype android:label="%s" android:languageTag="sr" android:imeSubtypeLocale="sr_" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_lynyertz_sr,dictionary=sr" /> + <subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_se,dictionary=sv,extra_keys=accent_aigu:á@d|accent_trema:ä:ö@o|accent_ring:å@s|€" /> + <subtype android:label="%s" android:languageTag="ta" android:imeSubtypeLocale="ta_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=tamil,default_layout=tamil_default,dictionary=ta" /> + <subtype android:label="%s" android:languageTag="tly-AZ" android:imeSubtypeLocale="tly_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tly,extra_keys=á|ú|â|ê|ı|š|ž" /> + <subtype android:label="%s" android:languageTag="tly-IR" android:imeSubtypeLocale="tly_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=persian,default_layout=arab_hamvaj_tly" /> + <subtype android:label="%s" android:languageTag="tr" android:imeSubtypeLocale="tr_TR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tr,dictionary=tr,extra_keys=accent_cedille:ç:ş@c|accent_trema:ö:ü@u|accent_circonflexe:â:î:û@f|₺|ı|ğ" /> + <subtype android:label="%s" android:languageTag="uk" android:imeSubtypeLocale="uk_UA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_uk,dictionary=uk,extra_keys=ґ|є|і|ї|₴" /> + <subtype android:label="%s" android:languageTag="uz" android:imeSubtypeLocale="uz_UZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_uz,extra_keys=ʻ|ʼ" /> + <subtype android:label="%s" android:languageTag="vi" android:imeSubtypeLocale="vi_VN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_vi" /> + <subtype android:label="%s" android:languageTag="yo" android:imeSubtypeLocale="yo_NG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us,extra_keys=₦|ẹ|ọ|ṣ" /> </input-method> @@ -11,6 +11,8 @@ let buildToolsVersions = [ build_tools_version ]; platformVersions = [ "35" ]; abiVersions = [ "armeabi-v7a" ]; + includeNDK = true; + ndkVersion = "27.0.12077973"; }; emulators = let diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 16a92ef..18e9fe7 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -8,6 +8,8 @@ import android.util.TypedValue; import java.util.ArrayList; import java.util.List; import java.util.Map; +import juloo.cdict.Cdict; +import juloo.keyboard2.dict.Dictionaries; import juloo.keyboard2.prefs.CustomExtraKeysPreference; import juloo.keyboard2.prefs.ExtraKeysPreference; import juloo.keyboard2.prefs.LayoutsPreference; @@ -80,8 +82,8 @@ public final class Config public ExtraKeys extra_keys_subtype; public Map<KeyValue, KeyboardData.PreferredPos> extra_keys_param; public Map<KeyValue, KeyboardData.PreferredPos> extra_keys_custom; - - public final IKeyEventHandler handler; + public Cdict current_dictionary = null; // Might be 'null'. + public IKeyEventHandler handler; public boolean orientation_landscape = false; public boolean foldable_unfolded = false; public boolean wide_screen = false; @@ -90,7 +92,8 @@ public final class Config int current_layout_narrow; int current_layout_wide; - private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h, Boolean foldableUnfolded) + private Config(SharedPreferences prefs, Resources res, + Boolean foldableUnfolded, Dictionaries dicts) { _prefs = prefs; editor_config = new EditorConfig(); @@ -100,17 +103,16 @@ public final class Config labelTextSize = 0.33f; sublabelTextSize = 0.22f; // from prefs - refresh(res, foldableUnfolded); + refresh(res, foldableUnfolded, dicts); // initialized later shouldOfferVoiceTyping = false; extra_keys_subtype = null; - handler = h; } /* ** Reload prefs */ - public void refresh(Resources res, Boolean foldableUnfolded) + public void refresh(Resources res, Boolean foldableUnfolded, Dictionaries dicts) { DisplayMetrics dm = res.getDisplayMetrics(); orientation_landscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; @@ -289,10 +291,10 @@ public final class Config private static Config _globalConfig = null; public static void initGlobalConfig(SharedPreferences prefs, Resources res, - IKeyEventHandler handler, Boolean foldableUnfolded) + Boolean foldableUnfolded, Dictionaries dicts) { migrate(prefs); - _globalConfig = new Config(prefs, res, handler, foldableUnfolded); + _globalConfig = new Config(prefs, res, foldableUnfolded, dicts); LayoutModifier.init(_globalConfig, res); } diff --git a/srcs/juloo.keyboard2/DeviceLocales.java b/srcs/juloo.keyboard2/DeviceLocales.java index a6cfdaf..c0c489c 100644 --- a/srcs/juloo.keyboard2/DeviceLocales.java +++ b/srcs/juloo.keyboard2/DeviceLocales.java @@ -36,6 +36,7 @@ public final class DeviceLocales public final String script; public final String default_layout; // Might be [null] public final ExtraKeys extra_keys; + public final String dictionary; // Might be [null] public Loc(InputMethodSubtype st) { @@ -45,6 +46,7 @@ public final class DeviceLocales String extra_keys_s = st.getExtraValueOf("extra_keys"); extra_keys = (extra_keys_s != null) ? ExtraKeys.parse(script, extra_keys_s) : ExtraKeys.EMPTY; + dictionary = st.getExtraValueOf("dictionary"); } } diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index 18e0592..2bda20c 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -31,14 +31,14 @@ public final class KeyEventHandler [setSelection] could be used instead. */ boolean _move_cursor_force_fallback = false; - public KeyEventHandler(IReceiver recv) + public KeyEventHandler(IReceiver recv, Config config) { _recv = recv; Handler handler = recv.getHandler(); _autocap = new Autocapitalisation(handler, this.new Autocapitalisation_callback()); _mods = Pointers.Modifiers.EMPTY; - _suggestions = new Suggestions(recv); + _suggestions = new Suggestions(recv, config); _typedword = new CurrentlyTypedWord(handler, this); } diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 25af0d0..ffe99ac 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -23,8 +23,11 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import juloo.keyboard2.dict.Dictionaries; +import juloo.keyboard2.dict.DictionariesActivity; import juloo.keyboard2.prefs.LayoutsPreference; import juloo.keyboard2.suggestions.CandidatesView; +import juloo.cdict.Cdict; public class Keyboard2 extends InputMethodService implements SharedPreferences.OnSharedPreferenceChangeListener @@ -40,6 +43,7 @@ public class Keyboard2 extends InputMethodService private KeyboardData _localeTextLayout; /** Installed and current locales. */ private DeviceLocales _device_locales; + private Dictionaries _dictionaries; private ViewGroup _emojiPane = null; private ViewGroup _clipboard_pane = null; private Handler _handler; @@ -115,11 +119,14 @@ public class Keyboard2 extends InputMethodService super.onCreate(); SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this); _handler = new Handler(getMainLooper()); - _keyeventhandler = new KeyEventHandler(this.new Receiver()); _foldStateTracker = new FoldStateTracker(this); - Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded()); - prefs.registerOnSharedPreferenceChangeListener(this); + _dictionaries = Dictionaries.instance(this); + Config.initGlobalConfig(prefs, getResources(), + _foldStateTracker.isUnfolded(), _dictionaries); _config = Config.globalConfig(); + _keyeventhandler = new KeyEventHandler(this.new Receiver(), _config); + _config.handler = _keyeventhandler; + prefs.registerOnSharedPreferenceChangeListener(this); Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs)); refreshSubtypeImm(); create_keyboard_view(); @@ -163,6 +170,18 @@ public class Keyboard2 extends InputMethodService _localeTextLayout = default_layout; } + private void refresh_current_dictionary() + { + _config.current_dictionary = null; + String current = _device_locales.default_.dictionary; + if (current == null) + return; + Cdict[] dicts = _dictionaries.load(current); + if (dicts == null) + return; + _config.current_dictionary = Dictionaries.find_by_name(dicts, "main"); + } + private void refresh_candidates_view() { boolean should_show = @@ -178,7 +197,8 @@ public class Keyboard2 extends InputMethodService private void refresh_config() { int prev_theme = _config.theme; - _config.refresh(getResources(), _foldStateTracker.isUnfolded()); + _config.refresh(getResources(), _foldStateTracker.isUnfolded(), _dictionaries); + refresh_current_dictionary(); // Refreshing the theme config requires re-creating the views if (prev_theme != _config.theme) { @@ -331,6 +351,19 @@ public class Keyboard2 extends InputMethodService return false; } + /** Called from [onClick] attributes. */ + public void launch_dictionaries_activity(View v) + { + start_activity(DictionariesActivity.class); + } + + void start_activity(Class cls) + { + Intent intent = new Intent(this, cls); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + /** Not static */ public class Receiver implements KeyEventHandler.IReceiver { @@ -339,9 +372,7 @@ public class Keyboard2 extends InputMethodService switch (ev) { case CONFIG: - Intent intent = new Intent(Keyboard2.this, SettingsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + start_activity(SettingsActivity.class); break; case SWITCH_TEXT: diff --git a/srcs/juloo.keyboard2/LauncherActivity.java b/srcs/juloo.keyboard2/LauncherActivity.java index ee769fb..d708619 100644 --- a/srcs/juloo.keyboard2/LauncherActivity.java +++ b/srcs/juloo.keyboard2/LauncherActivity.java @@ -21,6 +21,8 @@ import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; import java.util.List; +import juloo.keyboard2.dict.DictionariesActivity; +import juloo.keyboard2.R; public class LauncherActivity extends Activity implements Handler.Callback { @@ -96,6 +98,11 @@ public class LauncherActivity extends Activity implements Handler.Callback imm.showInputMethodPicker(); } + public void launch_dictionaries_activity(View v) + { + startActivity(new Intent(this, DictionariesActivity.class)); + } + Animatable find_anim(int id) { ImageView img = (ImageView)findViewById(id); diff --git a/srcs/juloo.keyboard2/Utils.java b/srcs/juloo.keyboard2/Utils.java index 5cc1664..ae82474 100644 --- a/srcs/juloo.keyboard2/Utils.java +++ b/srcs/juloo.keyboard2/Utils.java @@ -9,6 +9,8 @@ import android.view.View; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Locale; @@ -49,4 +51,14 @@ public final class Utils out.append(buff, 0, l); return out.toString(); } + + public static byte[] read_all_bytes(InputStream inp) throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buff = new byte[128000]; + int l; + while ((l = inp.read(buff)) != -1) + out.write(buff, 0, l); + return out.toByteArray(); + } } diff --git a/srcs/juloo.keyboard2/dict/Dictionaries.java b/srcs/juloo.keyboard2/dict/Dictionaries.java new file mode 100644 index 0000000..ac43c9b --- /dev/null +++ b/srcs/juloo.keyboard2/dict/Dictionaries.java @@ -0,0 +1,149 @@ +package juloo.keyboard2.dict; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import juloo.cdict.Cdict; +import juloo.keyboard2.Logs; +import juloo.keyboard2.Utils; + +/** Manage and load installed dictionaries. */ +public final class Dictionaries +{ + public static Dictionaries instance(Context ctx) + { + if (_instance == null) + _instance = new Dictionaries(ctx); + return _instance; + } + + /** Util for finding a dictionary by name. Returns [null] if not found. */ + public static Cdict find_by_name(Cdict[] dicts, String name) + { + for (Cdict d : dicts) + if (d.name.equals(name)) + return d; + return null; + } + + /** Load an installed dictionary. Return [null] if the requested dictionary + is not installed or the dictionary couldn't be loaded. */ + public Cdict[] load(String dict_name) + { + if (_loaded_dictionaries.containsKey(dict_name)) + return _loaded_dictionaries.get(dict_name); + Cdict[] dict = load_uncached(dict_name); + _loaded_dictionaries.put(dict_name, dict); + return dict; + } + + public Set<String> get_installed() { return _installed_dictionaries; } + + public void install(String dict_name, byte[] data) throws IOException + { + FileOutputStream outp = _context.openFileOutput(dict_file_name(dict_name), + Context.MODE_PRIVATE); + outp.write(data); + outp.close(); + set_installed(dict_name); + } + + /** Return the absolute path used to store the dictionary with the given + name. Return the same result whether the dictionary is installed or not. */ + public File get_install_location(String dict_name) + { + return _context.getFileStreamPath(dict_file_name(dict_name)); + } + + /** Declare a dictionary as installed. A dictionary file must exist at the + path returned by [get_install_location(dict_name)]. */ + public void set_installed(String dict_name) + { + _installed_dictionaries.add(dict_name); + _loaded_dictionaries.remove(dict_name); + save(); + } + + public void uninstall(String dict_name) + { + _context.deleteFile(dict_file_name(dict_name)); + _installed_dictionaries.remove(dict_name); + _loaded_dictionaries.remove(dict_name); + save(); + } + + /** Private */ + + Context _context; + Set<String> _installed_dictionaries; + /** Might be 'null' when safe storage is not available. */ + SharedPreferences _shared_prefs; + Map<String, Cdict[]> _loaded_dictionaries; + + static Dictionaries _instance = null; + + static final String PREF_INSTALLED_DICTS = "installed"; + + Dictionaries(Context ctx) + { + _context = ctx; + _installed_dictionaries = new HashSet(); + _loaded_dictionaries = new TreeMap<String, Cdict[]>(); + load_prefs(); + } + + void load_prefs() + { + _shared_prefs = null; + try + { + _shared_prefs = + _context.getSharedPreferences("dictionaries", Context.MODE_PRIVATE); + Set<String> s = _shared_prefs.getStringSet(PREF_INSTALLED_DICTS, null); + if (s != null) + _installed_dictionaries.addAll(s); + } + catch (Exception e) + { + Logs.exn("", e); + } + } + + Cdict[] load_uncached(String dict_name) + { + if (!_installed_dictionaries.contains(dict_name)) + return null; + try + { + FileInputStream inp = _context.openFileInput(dict_file_name(dict_name)); + byte[] data = Utils.read_all_bytes(inp); + inp.close(); + return Cdict.of_bytes(data); + } + catch (IOException e) { return null; } + catch (Cdict.ConstructionError e) { return null; } + } + + void save() + { + if (_shared_prefs == null) + return; + _shared_prefs.edit() + .putStringSet(PREF_INSTALLED_DICTS, _installed_dictionaries) + .commit(); + } + + static String dict_file_name(String dict_name) + { + return dict_name + ".dict"; + } +} diff --git a/srcs/juloo.keyboard2/dict/DictionariesActivity.java b/srcs/juloo.keyboard2/dict/DictionariesActivity.java new file mode 100644 index 0000000..4143107 --- /dev/null +++ b/srcs/juloo.keyboard2/dict/DictionariesActivity.java @@ -0,0 +1,15 @@ +package juloo.keyboard2.dict; + +import android.app.Activity; +import android.os.Bundle; +import juloo.keyboard2.R; + +public class DictionariesActivity extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.dictionaries_activity); + } +} diff --git a/srcs/juloo.keyboard2/dict/DictionaryListView.java b/srcs/juloo.keyboard2/dict/DictionaryListView.java new file mode 100644 index 0000000..465d373 --- /dev/null +++ b/srcs/juloo.keyboard2/dict/DictionaryListView.java @@ -0,0 +1,191 @@ +package juloo.keyboard2.dict; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import juloo.cdict.Cdict; +import juloo.keyboard2.Config; +import juloo.keyboard2.DeviceLocales; +import juloo.keyboard2.Logs; +import juloo.keyboard2.R; +import juloo.keyboard2.Utils; + +public class DictionaryListView extends LinearLayout +{ + List<DictView> _dict_views; + Dictionaries _dictionaries; + Set<String> _pending = new HashSet(); + + public DictionaryListView(Context ctx, AttributeSet attrs) + { + super(ctx, attrs); + setOrientation(LinearLayout.VERTICAL); + _dictionaries = Dictionaries.instance(ctx); + inflate_views(ctx); + } + + void inflate_views(Context ctx) + { + DeviceLocales locales = DeviceLocales.load(ctx); + SupportedDictionaries ds = new SupportedDictionaries(ctx.getResources()); + DownloadBtnListener listener = this.new DownloadBtnListener(); + _dict_views = new ArrayList<DictView>(); + for (DeviceLocales.Loc loc : locales.installed) + { + int idx = (loc.dictionary != null) ? ds.find(loc.dictionary) : -1; + if (idx >= 0) + { + DictView dv = new DictView(ctx, ds, idx, listener); + addView(dv.view); + _dict_views.add(dv); + } + } + refresh(); + } + + /** Update the "installed" status of item views. Meaning whether the + "download" or "delete" button is shown. */ + void refresh() + { + Set<String> installed = _dictionaries.get_installed(); + for (DictView d : _dict_views) + d.refresh(installed, _pending); + } + + void toggle_installed(String dict_name) + { + run_dictionary_action(dict_name, new Runnable() + { + public void run() + { + if (_dictionaries.get_installed().contains(dict_name)) + _dictionaries.uninstall(dict_name); + else if (install_dictionary_from_internet(dict_name)) + post_toast(R.string.dictionaries_download_success); + else + post_toast(R.string.dictionaries_download_failed); + } + }); + } + + /** Run action [r] for dictionary [name] if no action is already running for + that dictionary. Calls [refresh] after the action completed. */ + void run_dictionary_action(String name, Runnable r) + { + if (_pending.contains(name)) + return; + _pending.add(name); + (new Thread() + { + public void run() + { + r.run(); + post(new Runnable() + { + public void run() + { + _pending.remove(name); + refresh(); + } + }); + } + }).start(); + refresh(); + } + + final class DownloadBtnListener implements View.OnClickListener + { + @Override + public void onClick(View v) + { + for (DictView dv : _dict_views) + if (dv.download_button == v) + toggle_installed(dv.dict_name); + } + } + + static final class DictView + { + public final View view; + public final String dict_name; + public final View download_button; + + public DictView(Context ctx, SupportedDictionaries ds, int dict_index, + DownloadBtnListener on_click) + { + view = View.inflate(ctx, R.layout.dictionary_download_item, null); + dict_name = ds.dict_name(dict_index); + float size_mb = ds.size(dict_index) / 1048576.f; + ((TextView)view.findViewById(R.id.dictionary_download_locale)) + .setText(ds.display_name(dict_index)); + ((TextView)view.findViewById(R.id.dictionary_download_size)) + .setText(NumberFormat.getInstance().format(size_mb) + "MB"); + download_button = view.findViewById(R.id.dictionary_download_button); + download_button.setOnClickListener(on_click); + } + + public void refresh(Set<String> installed, Set<String> pending) + { + download_button.setBackgroundResource(installed.contains(dict_name) + ? R.drawable.ic_delete : R.drawable.ic_download); + download_button.setVisibility(pending.contains(dict_name) + ? View.GONE : View.VISIBLE); + } + } + + static final String DICT_REPO_URL = + "https://github.com/Julow/Unexpected-Keyboard-dictionaries/raw/refs/heads/main"; + + static URL url_of_dictionary(String dict_name) + throws MalformedURLException + { + int format_version = 0; + return new URL(DICT_REPO_URL + "/v" + format_version + "/" + dict_name + + ".dict"); + } + + /** Returns [true] on success. */ + boolean install_dictionary_from_internet(String dict_name) + { + try + { + // Remote files are compressed with gzip at rest. Do not use server side + // compression and force decompression. + URLConnection con = url_of_dictionary(dict_name).openConnection(); + con.setRequestProperty("Accept-Encoding", "identity"); + byte[] data = Utils.read_all_bytes(new GZIPInputStream(con.getInputStream())); + Cdict.of_bytes(data); // Check that the dictionary can load. + _dictionaries.install(dict_name, data); + return true; + } + catch (Exception e) + { + Logs.exn("", e); + return false; + } + } + + void post_toast(int msg_id) + { + post(new Runnable() + { + public void run() + { + Toast.makeText(getContext(), msg_id, Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/srcs/juloo.keyboard2/dict/SupportedDictionaries.java b/srcs/juloo.keyboard2/dict/SupportedDictionaries.java new file mode 100644 index 0000000..879fccd --- /dev/null +++ b/srcs/juloo.keyboard2/dict/SupportedDictionaries.java @@ -0,0 +1,33 @@ +package juloo.keyboard2.dict; + +import android.content.res.Resources; +import java.util.Arrays; +import juloo.keyboard2.R; + +/** Access arrays in [dictionaries.xml]. */ +public class SupportedDictionaries +{ + public String[] locales; + public String[] names; + public int[] sizes; + + public SupportedDictionaries(Resources res) + { + locales = res.getStringArray(R.array.dictionaries_locale); + names = res.getStringArray(R.array.dictionaries_name); + sizes = res.getIntArray(R.array.dictionaries_size); + } + + /** Find the index for a given dictionary name. Return [-1] if not found. */ + public int find(String dict_name) + { + int i = Arrays.binarySearch(locales, dict_name); + return (i < 0) ? -1 : i; + } + + public int length() { return locales.length; } + + public String dict_name(int i) { return locales[i]; } + public String display_name(int i) { return names[i]; } + public int size(int i) { return sizes[i]; } +} diff --git a/srcs/juloo.keyboard2/suggestions/CandidatesView.java b/srcs/juloo.keyboard2/suggestions/CandidatesView.java index 259db35..b41177f 100644 --- a/srcs/juloo.keyboard2/suggestions/CandidatesView.java +++ b/srcs/juloo.keyboard2/suggestions/CandidatesView.java @@ -74,7 +74,7 @@ public class CandidatesView extends LinearLayout // The status message indicates whether the dictionaries should be // installed. _status_no_dict = inflate_and_show(_status_no_dict, - true, + (config.current_dictionary == null), R.layout.candidates_status_no_dict); set_height(config); } diff --git a/srcs/juloo.keyboard2/suggestions/Suggestions.java b/srcs/juloo.keyboard2/suggestions/Suggestions.java index 50c64e0..998d40d 100644 --- a/srcs/juloo.keyboard2/suggestions/Suggestions.java +++ b/srcs/juloo.keyboard2/suggestions/Suggestions.java @@ -2,28 +2,47 @@ package juloo.keyboard2.suggestions; import java.util.Arrays; import java.util.List; +import juloo.cdict.Cdict; +import juloo.keyboard2.dict.Dictionaries; +import juloo.keyboard2.Config; /** Keep track of the word being typed and provide suggestions for [CandidatesView]. */ public final class Suggestions { Callback _callback; + Config _config; - public Suggestions(Callback c) + public Suggestions(Callback c, Config conf) { _callback = c; + _config = conf; } public void currently_typed_word(String word) { - if (word.equals("")) + Cdict dict = _config.current_dictionary; + if (word.length() < 2 || dict == null) { _callback.set_suggestions(NO_SUGGESTIONS); } else { - // TODO - _callback.set_suggestions(Arrays.asList(word)); + Cdict.Result r = dict.find(word); + String[] suggestions = new String[3]; + int i = 0; + if (r.found) + suggestions[i++] = word; + int[] suffixes = dict.suffixes(r, 3); + int[] dist = dict.distance(word, 1, 3); + for (int j = 0; j < 3 && i < 3; j++) + { + if (suffixes.length > j) + suggestions[i++] = dict.word(suffixes[j]); + if (dist.length > j && i < 3) + suggestions[i++] = dict.word(dist[j]); + } + _callback.set_suggestions(Arrays.asList(suggestions)); } } diff --git a/vendor/Android.mk b/vendor/Android.mk new file mode 100644 index 0000000..8a54c3d --- /dev/null +++ b/vendor/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/cdict/libcdict +LOCAL_CFLAGS += +# -Wall -Wextra -Werror +LOCAL_SRC_FILES := cdict/libcdict/libcdict.c cdict/java/jni/juloo_cdict_Cdict.c +LOCAL_MODULE := libcdict_java +LOCAL_SDK_VERSION := 21 + +include $(BUILD_SHARED_LIBRARY) diff --git a/vendor/cdict b/vendor/cdict new file mode 160000 +Subproject aa1c986e79a632dff0a81f92c75d720e80aa844 |
