Refactor input method subtype system

This commit is contained in:
Aleksandras Kostarevas 2024-05-14 17:13:52 -05:00
parent ef1860b8d0
commit 7570e4d258
28 changed files with 1060 additions and 1252 deletions

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@android:color/transparent" /> </shape>

View File

@ -573,4 +573,43 @@ Tip: You can download and remove dictionaries by going to &lt;b>Languages&#160;&
This resource is copied from packages/apps/Settings/res/values/strings.xml -->
<!-- This resource is corresponding to msgid="5433275485499039199" -->
<string name="user_dict_fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="layout_arabic">Arabic</string>
<string name="layout_armenian_phonetic">Armenian (Phonetic)</string>
<string name="layout_azerty">AZERTY</string>
<string name="layout_bengali">Bengali</string>
<string name="layout_bengali_akkhor">Bengali (Akkhor)</string>
<string name="layout_bepo">BÉPO</string>
<string name="layout_bulgarian">Bulgarian</string>
<string name="layout_bulgarian_bds">Bulgarian (BDS)</string>
<string name="layout_colemak">Colemak</string>
<string name="layout_dvorak">Dvorak</string>
<string name="layout_east_slavic">East Slavic</string>
<string name="layout_farsi">Farsi</string>
<string name="layout_georgian">Georgian</string>
<string name="layout_greek">Greek</string>
<string name="layout_hebrew">Hebrew</string>
<string name="layout_hindi">Hindi</string>
<string name="layout_hindi_compact">Hindi (Compact)</string>
<string name="layout_kannada">Kannada</string>
<string name="layout_khmer">Khmer</string>
<string name="layout_lao">Lao</string>
<string name="layout_malayalam">Malayalam</string>
<string name="layout_marathi">Marathi</string>
<string name="layout_mongolian">Mongolian</string>
<string name="layout_nepali_romanized">Nepali (Romanized)</string>
<string name="layout_nepali_traditional">Nepali (Traditional)</string>
<string name="layout_nordic">Nordic</string>
<string name="layout_pcqwerty">PC QWERTY</string>
<string name="layout_qwerty">QWERTY</string>
<string name="layout_qwertz">QWERTZ</string>
<string name="layout_serbian_qwertz">Serbian (QWERTZ)</string>
<string name="layout_sinhala">Sinhala</string>
<string name="layout_south_slavic">South Slavic</string>
<string name="layout_spanish">Spanish</string>
<string name="layout_swiss">Swiss</string>
<string name="layout_tamil">Tamil</string>
<string name="layout_telugu">Telugu</string>
<string name="layout_thai">Thai</string>
<string name="layout_uzbek">Uzbek</string>
</resources>

21
java/res/xml/kbd_bepo.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/rows_bepo" />
</Keyboard>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<KeyboardLayoutSet
xmlns:latin="http://schemas.android.com/apk/res-auto">
<Element
latin:elementName="alphabet"
latin:elementKeyboard="@xml/kbd_bepo"
latin:enableProximityCharsCorrection="true" />
<Element
latin:elementName="symbols"
latin:elementKeyboard="@xml/kbd_symbols" />
<Element
latin:elementName="symbolsShifted"
latin:elementKeyboard="@xml/kbd_symbols_shift" />
<Element
latin:elementName="phone"
latin:elementKeyboard="@xml/kbd_phone" />
<Element
latin:elementName="phoneSymbols"
latin:elementKeyboard="@xml/kbd_phone_symbols" />
<Element
latin:elementName="number"
latin:elementKeyboard="@xml/kbd_number" />
</KeyboardLayoutSet>

View File

@ -1,812 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Copyright (c) 2008, 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.
*/
-->
<!-- The attributes in this XML file provide configuration information -->
<!-- for the Input Method Manager. -->
<!-- Supported subtypes
keyboard_locale: script_name/keyboard_layout_set
af: Afrikaans/qwerty
ar: Arabic/arabic
az_AZ: Azerbaijani (Azerbaijan)/qwerty
be_BY: Belarusian (Belarus)/east_slavic
bg: Bulgarian/bulgarian
bg: Bulgarian/bulgarian_bds
bn_BD: Bengali (Bangladesh)/bengali_akkhor
bn_IN: Bengali (India)/bengali
ca: Catalan/spanish
cs: Czech/qwertz
da: Danish/nordic
de: German/qwertz
de_CH: German (Switzerland)/swiss
el: Greek/greek
en_IN: English (India)/qwerty
en_US: English (United States)/qwerty
en_GB: English (Great Britain)/qwerty
eo: Esperanto/spanish
es: Spanish/spanish
es_US: Spanish (United States)/spanish
es_419: Spanish (Latin America)/spanish
et_EE: Estonian (Estonia)/nordic
eu_ES: Basque (Spain)/spanish
fa: Persian/farsi
fi: Finnish/nordic
fr: French/azerty
fr_CA: French (Canada)/qwerty
fr_CH: French (Switzerland)/swiss
gl_ES: Galician (Spain)/spanish
hi: Hindi/hindi
hi: Hindi/hindi_compact
hi_ZZ: Hinglish/qwerty # This is a preliminary keyboard layout.
hr: Croatian/qwertz
hu: Hungarian/qwertz
hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic
in: Indonesian/qwerty # "id" is the official language code of Indonesian.
is: Icelandic/qwerty
it: Italian/qwerty
it_CH: Italian (Switzerland)/swiss
iw: Hebrew/hebrew # "he" is the official language code of Hebrew.
ka_GE: Georgian (Georgia)/georgian
kk: Kazakh/east_slavic
km_KH: Khmer (Cambodia)/khmer
kn_IN: Kannada (India)/kannada
ky: Kyrgyz/east_slavic
lo_LA: Lao (Laos)/lao
lt: Lithuanian/qwerty
lv: Latvian/qwerty
mk: Macedonian/south_slavic
ml_IN: Malayalam (India)/malayalam
mn_MN: Mongolian (Mongolia)/mongolian
mr_IN: Marathi (India)/marathi
ms_MY: Malay (Malaysia)/qwerty
nb: Norwegian Bokmål/nordic
ne_NP: Nepali (Nepal) Romanized/nepali_romanized
ne_NP: Nepali (Nepal) Traditional/nepali_traditional
nl: Dutch/qwerty
nl_BE: Dutch (Belgium)/azerty
pl: Polish/qwerty
pt_BR: Portuguese (Brazil)/qwerty
pt_PT: Portuguese (Portugal)/qwerty
ro: Romanian/qwerty
ru: Russian/east_slavic
si_LK: Sinhala (Sri Lanka)/sinhala # This is a preliminary keyboard layout.
sk: Slovak/qwerty
sl: Slovenian/qwerty
sr: Serbian/south_slavic
sr_ZZ: Serbian (Latin)/serbian_qwertz # This is a preliminary keyboard layout.
sv: Swedish/nordic
sw: Swahili/qwerty
ta_IN: Tamil (India)/tamil
ta_LK: Tamil (Sri Lanka)/tamil # Disabled in conjunction with si_LK.
ta_SG: Tamil (Singapore)/tamil
te_IN: Telugu (India)/telugu
th: Thai/thai
tl: Tagalog/spanish
tr: Turkish/qwerty
uk: Ukrainian/east_slavic
uz_UZ: Uzbek (Uzbekistan)/uzbek # This is a preliminary keyboard layout.
vi: Vietnamese/qwerty
zu: Zulu/qwerty
zz: QWERTY/qwerty
(zz: Emoji/emoji)
-->
<!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
<!-- TODO: Remove "AsciiCapable" from the extra values when we can stop supporting JB-MR1 -->
<!-- Note: SupportTouchPositionCorrection extra value is obsolete and maintained for backward
compatibility. -->
<!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
subtype.-->
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="org.futo.inputmethod.latin.uix.settings.SettingsActivity"
android:isDefault="@bool/im_is_default"
android:supportsSwitchingToNextInputMethod="true"
android:supportsInlineSuggestions="true">
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_en_US"
android:subtypeId="0xc9194f98"
android:imeSubtypeLocale="en_US"
android:languageTag="en-US"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_en_GB"
android:subtypeId="0xb045e755"
android:imeSubtypeLocale="en_GB"
android:languageTag="en-GB"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x6f972360"
android:imeSubtypeLocale="af"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x590dde40"
android:imeSubtypeLocale="ar"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x70b0f974"
android:imeSubtypeLocale="az_AZ"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x1dc3a859"
android:imeSubtypeLocale="be_BY"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x0ba9c0e8"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_bulgarian_bds"
android:subtypeId="0x5f51ba9a"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xa2144b0c"
android:imeSubtypeLocale="bn_BD"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bengali_akkhor,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xbff5986c"
android:imeSubtypeLocale="bn_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bengali,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xd2e520d5"
android:imeSubtypeLocale="ca"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x2d3d2ed0"
android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x2df4605d"
android:imeSubtypeLocale="da"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x2e2cbe61"
android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x7acfd0aa"
android:imeSubtypeLocale="de_CH"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x0e7802d3"
android:imeSubtypeLocale="el"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=greek,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8d58fc2d"
android:imeSubtypeLocale="en_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x4090554a"
android:imeSubtypeLocale="eo"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x30a6e00e"
android:imeSubtypeLocale="es"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_es_US"
android:subtypeId="0x84d2efc6"
android:imeSubtypeLocale="es_US"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xa23e5d19"
android:imeSubtypeLocale="es_419"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xec2d3955"
android:imeSubtypeLocale="et_EE"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x070e5c07"
android:imeSubtypeLocale="eu_ES"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xbe66c254"
android:imeSubtypeLocale="fa"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x31cecda3"
android:imeSubtypeLocale="fi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x324da12c"
android:imeSubtypeLocale="fr"
android:languageTag="fr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xeadbb691"
android:imeSubtypeLocale="fr_CA"
android:languageTag="fr-CA"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xeadc55f5"
android:imeSubtypeLocale="fr_CH"
android:languageTag="fr-CH"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xb939573c"
android:imeSubtypeLocale="gl_ES"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x39753b7f"
android:imeSubtypeLocale="hi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic_compact"
android:subtypeId="0xe49c89a1"
android:imeSubtypeLocale="hi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi_compact,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- TODO: This Hinglish keyboard is a preliminary layout.
This isn't based on the final specification. -->
<!-- Disabled because there is no LM yet, and this layout does not offer anything different.
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_hi_ZZ"
android:subtypeId="0x352eb37c"
android:imeSubtypeLocale="hi_ZZ"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,KeyboardLayoutSet=qwerty,EmojiCapable"
android:isAsciiCapable="true"
/>
-->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x35b7526a"
android:imeSubtypeLocale="hr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x35e198ed"
android:imeSubtypeLocale="hu"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xe39ac3ca"
android:imeSubtypeLocale="hy_AM"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x7daea460"
android:imeSubtypeLocale="in"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x7df519e5"
android:imeSubtypeLocale="is"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x37885a0b"
android:imeSubtypeLocale="it"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xd914fe1a"
android:imeSubtypeLocale="it_CH"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x66fb18bd"
android:imeSubtypeLocale="iw"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x6e119e6a"
android:imeSubtypeLocale="ka_GE"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x2d73d2f6"
android:imeSubtypeLocale="kk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x1365683a"
android:imeSubtypeLocale="km_KH"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8c78064f"
android:imeSubtypeLocale="kn_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=kannada,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x2e391c04"
android:imeSubtypeLocale="ky"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8315772c"
android:imeSubtypeLocale="lo_LA"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=lao,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8321bb43"
android:imeSubtypeLocale="lt"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x833dea45"
android:imeSubtypeLocale="lv"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xaf50ab7c"
android:imeSubtypeLocale="mk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xc182ebd4"
android:imeSubtypeLocale="ml_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=malayalam,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xcdcfc3ab"
android:imeSubtypeLocale="mn_MN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x747b9f03"
android:imeSubtypeLocale="mr_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=marathi,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x84c87c61"
android:imeSubtypeLocale="ms_MY"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x3f12ee14"
android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xd80a4cee"
android:imeSubtypeLocale="ne_NP"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic_traditional"
android:subtypeId="0x5fafea88"
android:imeSubtypeLocale="ne_NP"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x3f9fd91e"
android:imeSubtypeLocale="nl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x500ca92c"
android:imeSubtypeLocale="nl_BE"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x43098a5c"
android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xcafff4a6"
android:imeSubtypeLocale="pt_BR"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xe2fffc5a"
android:imeSubtypeLocale="pt_PT"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8d185978"
android:imeSubtypeLocale="ro"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x763a8752"
android:imeSubtypeLocale="ru"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- TODO: This Sinhala keyboard is a preliminary layout.
This isn't based on the final specification. -->
<!-- si_LK is currently disabled due to lack of combination rules.
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x5c6b3bde"
android:imeSubtypeLocale="si_LK"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=sinhala,EmojiCapable"
android:isAsciiCapable="false"
/>
-->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8e94d413"
android:imeSubtypeLocale="sk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8ea2eb94"
android:imeSubtypeLocale="sl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x77c5196e"
android:imeSubtypeLocale="sr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- TODO: This Serbian Latin keyboard is a preliminary layout.
This isn't based on the final specification. -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_sr_ZZ"
android:subtypeId="0xf4a5569c"
android:imeSubtypeLocale="sr_ZZ"
android:languageTag="sr-Latn"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=serbian_qwertz,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x48b4ff43"
android:imeSubtypeLocale="sv"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x8f3dee1f"
android:imeSubtypeLocale="sw"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x67acea2a"
android:imeSubtypeLocale="ta_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- TODO: Enabling/Disabling ta_LK subtype must be aligned with si_LK subtype. -->
<!-- ta_LK disabled alongside si_LK subtype due to lack of combination rules.
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x6ca12d84"
android:imeSubtypeLocale="ta_LK"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable"
android:isAsciiCapable="false"
/>
!-->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x785abbd9"
android:imeSubtypeLocale="ta_SG"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x1e177389"
android:imeSubtypeLocale="te_IN"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=telugu,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x1f94d5d4"
android:imeSubtypeLocale="th"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=thai,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xf08285ef"
android:imeSubtypeLocale="tl"
android:languageTag="fil"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x4a3179de"
android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x3e84492c"
android:imeSubtypeLocale="uk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
android:isAsciiCapable="false"
/>
<!-- TODO: This Uzbek keyboard is a preliminary layout.
This isn't based on the final specification. -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xad5cf7f6"
android:imeSubtypeLocale="uz_UZ"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=uzbek,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x93972eee"
android:imeSubtypeLocale="vi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0x9b13ab76"
android:imeSubtypeLocale="zu"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_no_language_qwerty"
android:subtypeId="0xa239ebad"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
<!-- Emoji subtype has to be an addtional subtype added at boot time because ICS doesn't
support Emoji. -->
<!--
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_emoji"
android:subtypeId="0xc14d88b2"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji,EmojiCapable"
android:isAsciiCapable="false"
/>
-->
android:settingsActivity="org.futo.inputmethod.latin.uix.settings.SettingsActivity"
android:isDefault="true"
android:supportsSwitchingToNextInputMethod="true"
android:supportsInlineSuggestions="true">
<subtype android:icon="@drawable/ic_launcher_foreground"
android:label="@string/english_ime_name"
android:imeSubtypeLocale="xx_YY"
android:imeSubtypeMode="keyboard"
android:overridesImplicitlyEnabledSubtype="true" />
</input-method>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<Key
latin:keySpec="b"
latin:keyHintLabel="1"
latin:additionalMoreKeys="1"/>
<Key
latin:keySpec="&#xE9;"
latin:keyHintLabel="2"
latin:additionalMoreKeys="2,&#xE8;" />
<Key
latin:keySpec="p"
latin:keyHintLabel="3"
latin:additionalMoreKeys="3" />
<Key
latin:keySpec="o"
latin:keyHintLabel="4"
latin:additionalMoreKeys="4"
latin:moreKeys="!text/morekeys_o" />
<Key
latin:keySpec="v"
latin:keyHintLabel="5"
latin:additionalMoreKeys="5"
latin:moreKeys="!text/morekeys_v" />
<Key
latin:keySpec="d"
latin:keyHintLabel="6"
latin:additionalMoreKeys="6"
latin:moreKeys="!text/morekeys_d" />
<Key
latin:keySpec="l"
latin:keyHintLabel="7"
latin:additionalMoreKeys="7"
latin:moreKeys="!text/morekeys_l" />
<Key
latin:keySpec="j"
latin:keyHintLabel="8"
latin:additionalMoreKeys="8"
latin:moreKeys="!text/morekeys_j" />
<Key
latin:keySpec="z"
latin:keyHintLabel="9"
latin:additionalMoreKeys="9"
latin:moreKeys="!text/morekeys_z" />
<Key
latin:keySpec="w"
latin:keyHintLabel="0"
latin:additionalMoreKeys="0"
latin:moreKeys="!text/morekeys_w" />
</merge>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<Key
latin:keySpec="a"
latin:moreKeys="!text/morekeys_a" />
<Key
latin:keySpec="u"
latin:moreKeys="!text/morekeys_u" />
<Key
latin:keySpec="i"
latin:moreKeys="!text/morekeys_i" />
<Key
latin:keySpec="e"
latin:moreKeys="!text/morekeys_e" />
<Key
latin:keySpec="c"
latin:moreKeys="!text/morekeys_c" />
<Key
latin:keySpec="t"
latin:moreKeys="!text/morekeys_t" />
<Key
latin:keySpec="s"
latin:moreKeys="!text/morekeys_s" />
<Key
latin:keySpec="r"
latin:moreKeys="!text/morekeys_r" />
<Key
latin:keySpec="n"
latin:moreKeys="!text/morekeys_n" />
<Key
latin:keySpec="m" />
</merge>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<Key
latin:keySpec="y"
latin:moreKeys="!text/morekeys_y" />
<Key
latin:keySpec="x" />
<Key
latin:keySpec="k"
latin:moreKeys="!text/morekeys_k" />
<Key
latin:keySpec="q"
latin:moreKeys="!text/morekeys_q" />
<Key
latin:keySpec="g"
latin:moreKeys="!text/morekeys_g" />
<Key
latin:keySpec="h"
latin:moreKeys="!text/morekeys_h" />
<Key
latin:keySpec="f" />
</merge>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The CyanogenMod 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.
-->
<merge
xmlns:latin="http://schemas.android.com/apk/res-auto"
>
<include
latin:keyboardLayout="@xml/key_styles_common" />
<Row
latin:keyWidth="10%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_bepo1" />
</Row>
<Row
latin:keyWidth="10%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_bepo2" />
</Row>
<Row
latin:keyWidth="10%p"
>
<Key
latin:keyStyle="shiftKeyStyle"
latin:keyWidth="15%p"
latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_bepo3" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"
latin:visualInsetsLeft="1%p" />
</Row>
<include
latin:keyboardLayout="@xml/row_qwerty4" />
</merge>

View File

@ -28,7 +28,6 @@ import androidx.annotation.NonNull;
import org.futo.inputmethod.event.Event;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import org.futo.inputmethod.keyboard.emoji.EmojiPalettesView;
import org.futo.inputmethod.keyboard.internal.KeyboardState;
import org.futo.inputmethod.keyboard.internal.KeyboardTextsSet;
import org.futo.inputmethod.latin.InputView;
@ -36,6 +35,7 @@ import org.futo.inputmethod.latin.LatinIME;
import org.futo.inputmethod.latin.LatinIMELegacy;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodManager;
import org.futo.inputmethod.latin.Subtypes;
import org.futo.inputmethod.latin.WordComposer;
import org.futo.inputmethod.latin.define.ProductionFlags;
import org.futo.inputmethod.latin.settings.Settings;
@ -181,8 +181,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|| !newKeyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils
.getLanguageOnSpacebarFormatType(newKeyboard.mId.mSubtype);
final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm
.hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
final boolean hasMultipleEnabledIMEsOrSubtypes = Subtypes.INSTANCE.hasMultipleEnabledSubtypes(mThemeContext);
keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
hasMultipleEnabledIMEsOrSubtypes);
}

View File

@ -63,6 +63,7 @@ import org.futo.inputmethod.latin.uix.deferGetSetting
import org.futo.inputmethod.latin.uix.deferSetSetting
import org.futo.inputmethod.latin.uix.differsFrom
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.getSettingFlow
import org.futo.inputmethod.latin.uix.setSetting
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
@ -275,7 +276,21 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
lifecycleScope.launch {
saveSubtypes()
val onNewSubtype: (String) -> Unit = {
val activeSubtype = it.ifEmpty {
getSettingBlocking(SubtypesSetting).firstOrNull()
}
if(activeSubtype != null) {
changeInputMethodSubtype(Subtypes.convertToSubtype(activeSubtype))
}
}
onNewSubtype(getSetting(ActiveSubtype))
dataStore.data.collect {
onNewSubtype(it[ActiveSubtype.key] ?: ActiveSubtype.default)
}
}
}
@ -411,8 +426,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
languageModelFacilitator.saveHistoryLog()
}
override fun onCurrentInputMethodSubtypeChanged(newSubtype: InputMethodSubtype?) {
super.onCurrentInputMethodSubtypeChanged(newSubtype)
private fun changeInputMethodSubtype(newSubtype: InputMethodSubtype?) {
latinIMELegacy.onCurrentInputMethodSubtypeChanged(newSubtype)
}

View File

@ -32,7 +32,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.Build;
@ -61,8 +60,6 @@ import androidx.core.content.ContextCompat;
import org.futo.inputmethod.accessibility.AccessibilityUtils;
import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.compat.BuildCompatUtils;
import org.futo.inputmethod.compat.EditorInfoCompatUtils;
import org.futo.inputmethod.compat.ViewOutlineProviderCompatUtils;
import org.futo.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
import org.futo.inputmethod.dictionarypack.DictionaryPackConstants;
@ -90,7 +87,7 @@ import org.futo.inputmethod.latin.settings.SettingsValues;
import org.futo.inputmethod.latin.suggestions.SuggestionStripView;
import org.futo.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
import org.futo.inputmethod.latin.touchinputconsumer.GestureConsumer;
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner;
import org.futo.inputmethod.latin.uix.actions.SwitchLanguageActionKt;
import org.futo.inputmethod.latin.uix.settings.SettingsActivity;
import org.futo.inputmethod.latin.utils.ApplicationUtils;
import org.futo.inputmethod.latin.utils.DialogUtils;
@ -176,7 +173,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
private final SuggestionStripController mSuggestionStripController;
private RichInputMethodManager mRichImm;
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
public final KeyboardSwitcher mKeyboardSwitcher;
private final SubtypeState mSubtypeState = new SubtypeState();
private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
private StatsUtilsManager mStatsUtilsManager;
@ -329,9 +326,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
case MSG_DEALLOCATE_MEMORY:
latinImeLegacy.deallocateMemory();
break;
case MSG_SWITCH_LANGUAGE_AUTOMATICALLY:
latinImeLegacy.switchLanguage((InputMethodSubtype)msg.obj);
break;
}
}
@ -465,10 +459,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
obtainMessage(MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED, suggestedWords).sendToTarget();
}
public void postSwitchLanguage(final InputMethodSubtype subtype) {
obtainMessage(MSG_SWITCH_LANGUAGE_AUTOMATICALLY, subtype).sendToTarget();
}
// Working variables for the following methods.
private boolean mIsOrientationChanging;
private boolean mPendingSuccessiveImsCallback;
@ -589,23 +579,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
mCurrentSubtypeHasBeenUsed = true;
}
public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) {
final InputMethodSubtype currentSubtype = richImm.getInputMethodManager()
.getCurrentInputMethodSubtype();
final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
final boolean currentSubtypeHasBeenUsed = mCurrentSubtypeHasBeenUsed;
if (currentSubtypeHasBeenUsed) {
mLastActiveSubtype = currentSubtype;
mCurrentSubtypeHasBeenUsed = false;
}
if (currentSubtypeHasBeenUsed
&& richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype)
&& !currentSubtype.equals(lastActiveSubtype)) {
richImm.setInputMethodAndSubtype(token, lastActiveSubtype);
return;
}
richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
}
}
// Loading the native library eagerly to avoid unexpected UnsatisfiedLinkError at the initial
@ -929,18 +902,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
}
void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
// If the primary hint language does not match the current subtype language, then try
// to switch to the primary hint language.
// TODO: Support all the locales in EditorInfo#hintLocales.
final Locale primaryHintLocale = EditorInfoCompatUtils.getPrimaryHintLocale(editorInfo);
if (primaryHintLocale == null) {
return;
}
final InputMethodSubtype newSubtype = mRichImm.findSubtypeByLocale(primaryHintLocale);
if (newSubtype == null || newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype())) {
return;
}
mHandler.postSwitchLanguage(newSubtype);
}
public void updateMainKeyboardViewSettings() {
@ -1374,11 +1336,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
if (isShowingOptionDialog()) return false;
switch (requestCode) {
case Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER:
if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
mRichImm.getInputMethodManager().showInputMethodPicker();
return true;
}
return false;
((LatinIME)mInputMethodService).getUixManager().showLanguageSwitcher();
return true;
}
return false;
}
@ -1459,13 +1418,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
return mOptionsDialog != null && mOptionsDialog.isShowing();
}
public void switchLanguage(final InputMethodSubtype subtype) {
final IBinder token = mInputMethodService.getWindow().getWindow().getAttributes().token;
mRichImm.setInputMethodAndSubtype(token, subtype);
}
public void switchToNextSubtype() {
mRichImm.getInputMethodManager().showInputMethodPicker();
SwitchLanguageActionKt.switchToNextLanguage(mInputMethodService);
}
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
@ -2006,12 +1960,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
mDictionaryFacilitator.clearUserHistoryDictionary(mInputMethodService);
}
@UsedForTesting
List<InputMethodSubtype> getEnabledSubtypesForTest() {
return (mRichImm != null) ? mRichImm.getMyEnabledInputMethodSubtypeList(
true /* allowsImplicitlySelectedSubtypes */) : new ArrayList<InputMethodSubtype>();
}
public void dumpDictionaryForDebug(final String dictName) {
if (!mDictionaryFacilitator.isActive()) {
resetDictionaryFacilitatorIfNecessary();

View File

@ -102,9 +102,6 @@ public class RichInputMethodManager {
// Initialize additional subtypes.
SubtypeLocaleUtils.init(context);
final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes();
mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
getInputMethodIdOfThisIme(), additionalSubtypes);
// Initialize the current input method subtype and the shortcut IME.
refreshSubtypeCaches();
@ -122,110 +119,6 @@ public class RichInputMethodManager {
return mImmWrapper.mImm;
}
public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList(
boolean allowsImplicitlySelectedSubtypes) {
return getEnabledInputMethodSubtypeList(
getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes);
}
public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) {
return true;
}
// Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)}
// because the current device is running ICS or previous and lacks the API.
if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) {
return true;
}
return switchToNextInputMethodAndSubtype(token);
}
private boolean switchToNextInputSubtypeInThisIme(final IBinder token,
final boolean onlyCurrentIme) {
final InputMethodManager imm = mImmWrapper.mImm;
final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList(
true /* allowsImplicitlySelectedSubtypes */);
final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes);
if (currentIndex == INDEX_NOT_FOUND) {
Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype="
+ SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype));
return false;
}
final int nextIndex = (currentIndex + 1) % enabledSubtypes.size();
if (nextIndex <= currentIndex && !onlyCurrentIme) {
// The current subtype is the last or only enabled one and it needs to switch to
// next IME.
return false;
}
final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex);
setInputMethodAndSubtype(token, nextSubtype);
return true;
}
private boolean switchToNextInputMethodAndSubtype(final IBinder token) {
final InputMethodManager imm = mImmWrapper.mImm;
final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis);
if (currentIndex == INDEX_NOT_FOUND) {
Log.w(TAG, "Can't find current IME in enabled IMEs: IME package="
+ getInputMethodInfoOfThisIme().getPackageName());
return false;
}
final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis);
final List<InputMethodSubtype> enabledSubtypes = getEnabledInputMethodSubtypeList(nextImi,
true /* allowsImplicitlySelectedSubtypes */);
if (enabledSubtypes.isEmpty()) {
// The next IME has no subtype.
imm.setInputMethod(token, nextImi.getId());
return true;
}
final InputMethodSubtype firstSubtype = enabledSubtypes.get(0);
imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype);
return true;
}
private static int getImiIndexInList(final InputMethodInfo inputMethodInfo,
final List<InputMethodInfo> imiList) {
final int count = imiList.size();
for (int index = 0; index < count; index++) {
final InputMethodInfo imi = imiList.get(index);
if (imi.equals(inputMethodInfo)) {
return index;
}
}
return INDEX_NOT_FOUND;
}
// This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}.
private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex,
final List<InputMethodInfo> imiList) {
final int count = imiList.size();
for (int i = 1; i < count; i++) {
final int nextIndex = (currentIndex + i) % count;
final InputMethodInfo nextImi = imiList.get(nextIndex);
if (!isAuxiliaryIme(nextImi)) {
return nextImi;
}
}
return imiList.get(currentIndex);
}
// Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined.
private static boolean isAuxiliaryIme(final InputMethodInfo imi) {
final int count = imi.getSubtypeCount();
if (count == 0) {
return false;
}
for (int index = 0; index < count; index++) {
final InputMethodSubtype subtype = imi.getSubtypeAt(index);
if (!subtype.isAuxiliary()) {
return false;
}
}
return true;
}
private static class InputMethodInfoCache {
private final InputMethodManager mImm;
private final String mImePackageName;
@ -287,38 +180,6 @@ public class RichInputMethodManager {
return getInputMethodInfoOfThisIme().getId();
}
public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) {
return checkIfSubtypeBelongsToList(subtype,
getEnabledInputMethodSubtypeList(
getInputMethodInfoOfThisIme(),
true /* allowsImplicitlySelectedSubtypes */));
}
public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
final InputMethodSubtype subtype) {
final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype,
getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */));
return subtypeEnabled && !subtypeExplicitlyEnabled;
}
private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
final List<InputMethodSubtype> subtypes) {
return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND;
}
private static int getSubtypeIndexInList(final InputMethodSubtype subtype,
final List<InputMethodSubtype> subtypes) {
final int count = subtypes.size();
for (int index = 0; index < count; index++) {
final InputMethodSubtype ims = subtypes.get(index);
if (ims.equals(subtype)) {
return index;
}
}
return INDEX_NOT_FOUND;
}
public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
updateCurrentSubtype(newSubtype);
updateShortcutIme();
@ -355,142 +216,9 @@ public class RichInputMethodManager {
return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
}
public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
}
public boolean hasMultipleEnabledSubtypesInThisIme(
final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> imiList = Collections.singletonList(
getInputMethodInfoOfThisIme());
return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
}
private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
final List<InputMethodInfo> imiList) {
// Number of the filtered IMEs
int filteredImisCount = 0;
for (InputMethodInfo imi : imiList) {
// We can return true immediately after we find two or more filtered IMEs.
if (filteredImisCount > 1) return true;
final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(imi, true);
// IMEs that have no subtypes should be counted.
if (subtypes.isEmpty()) {
++filteredImisCount;
continue;
}
int auxCount = 0;
for (InputMethodSubtype subtype : subtypes) {
if (subtype.isAuxiliary()) {
++auxCount;
}
}
final int nonAuxCount = subtypes.size() - auxCount;
// IMEs that have one or more non-auxiliary subtypes should be counted.
// If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
// subtypes should be counted as well.
if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
++filteredImisCount;
}
}
if (filteredImisCount > 1) {
return true;
}
final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true);
int keyboardCount = 0;
// imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
// both explicitly and implicitly enabled input method subtype.
// (The current IME should be LatinIME.)
for (InputMethodSubtype subtype : subtypes) {
if (KEYBOARD_MODE.equals(subtype.getMode())) {
++keyboardCount;
}
}
return keyboardCount > 1;
}
public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
final String keyboardLayoutSetName) {
final InputMethodInfo myImi = getInputMethodInfoOfThisIme();
final int count = myImi.getSubtypeCount();
for (int i = 0; i < count; i++) {
final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
if (localeString.equals(subtype.getLocale())
&& keyboardLayoutSetName.equals(layoutName)) {
return subtype;
}
}
return null;
}
public InputMethodSubtype findSubtypeByLocale(final Locale locale) {
// Find the best subtype based on a straightforward matching algorithm.
// TODO: Use LocaleList#getFirstMatch() instead.
final List<InputMethodSubtype> subtypes =
getMyEnabledInputMethodSubtypeList(true /* allowsImplicitlySelectedSubtypes */);
final int count = subtypes.size();
for (int i = 0; i < count; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
if (subtypeLocale.equals(locale)) {
return subtype;
}
}
for (int i = 0; i < count; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
subtypeLocale.getCountry().equals(locale.getCountry()) &&
subtypeLocale.getVariant().equals(locale.getVariant())) {
return subtype;
}
}
for (int i = 0; i < count; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
subtypeLocale.getCountry().equals(locale.getCountry())) {
return subtype;
}
}
for (int i = 0; i < count; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
if (subtypeLocale.getLanguage().equals(locale.getLanguage())) {
return subtype;
}
}
return null;
}
public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) {
mImmWrapper.mImm.setInputMethodAndSubtype(
token, getInputMethodIdOfThisIme(), subtype);
}
public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
getInputMethodIdOfThisIme(), subtypes);
// Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of
// subtypes again next time.
refreshSubtypeCaches();
}
private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi,
final boolean allowsImplicitlySelectedSubtypes) {
return mInputMethodInfoCache.getEnabledInputMethodSubtypeList(
imi, allowsImplicitlySelectedSubtypes);
}
public void refreshSubtypeCaches() {
mInputMethodInfoCache.clear();
updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype());
updateCurrentSubtype(Subtypes.INSTANCE.getActiveSubtype(mContext));
updateShortcutIme();
}
@ -545,13 +273,7 @@ public class RichInputMethodManager {
mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
}
final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
richSubtype.getRawSubtype());
final Locale systemLocale = mContext.getResources().getConfiguration().locale;
LanguageOnSpacebarUtils.onSubtypeChanged(
richSubtype, implicitlyEnabledSubtype, systemLocale);
LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList(
true /* allowsImplicitlySelectedSubtypes */));
// TODO: Update an icon for shortcut IME
final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =

View File

@ -209,14 +209,7 @@ public class RichInputMethodSubtype {
@Nonnull
public static RichInputMethodSubtype getNoLanguageSubtype() {
RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
if (noLanguageSubtype == null) {
final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
.findSubtypeByLocaleAndKeyboardLayoutSet(
SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
if (rawNoLanguageSubtype != null) {
noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
}
}
if (noLanguageSubtype != null) {
sNoLanguageSubtype = noLanguageSubtype;
return noLanguageSubtype;
@ -230,14 +223,7 @@ public class RichInputMethodSubtype {
@Nonnull
public static RichInputMethodSubtype getEmojiSubtype() {
RichInputMethodSubtype emojiSubtype = sEmojiSubtype;
if (emojiSubtype == null) {
final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance()
.findSubtypeByLocaleAndKeyboardLayoutSet(
SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
if (rawEmojiSubtype != null) {
emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
}
}
if (emojiSubtype != null) {
sEmojiSubtype = emojiSubtype;
return emojiSubtype;

View File

@ -1,26 +1,256 @@
package org.futo.inputmethod.latin
import android.content.Context
import android.content.Intent
import android.view.inputmethod.InputMethodManager
import android.view.inputmethod.InputMethodSubtype
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import okhttp3.internal.toImmutableList
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.setSetting
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.setSettingBlocking
import org.futo.inputmethod.latin.uix.settings.NavigationItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
import org.futo.inputmethod.latin.uix.settings.SettingsActivity
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking
import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils
import java.util.Locale
val SubtypesSetting = SettingsKey(
stringSetPreferencesKey("subtypes"),
setOf()
)
suspend fun Context.saveSubtypes() {
val inputMethodManager = getSystemService(android.inputmethodservice.InputMethodService.INPUT_METHOD_SERVICE) as InputMethodManager
val inputMethodList = inputMethodManager.getEnabledInputMethodSubtypeList(
RichInputMethodManager.getInstance().inputMethodInfoOfThisIme,
true
)
val ActiveSubtype = SettingsKey(
stringPreferencesKey("activeSubtype"),
""
)
val encodedSubtypes = inputMethodList.map {
it.locale + ":" + (it.extraValue ?: "") + ":" + it.languageTag
}.toSet()
object Subtypes {
fun convertToSubtype(string: String): InputMethodSubtype {
val splits = string.split(":")
val locale = splits[0]
setSetting(SubtypesSetting, encodedSubtypes)
val extraValue = splits.getOrNull(1) ?: ""
val languageTag = splits.getOrNull(2) ?: ""
return InputMethodSubtypeBuilder()
.setSubtypeLocale(locale)
.setSubtypeExtraValue(extraValue)
.setLanguageTag(languageTag)
.build()
}
fun getActiveSubtype(context: Context): InputMethodSubtype {
val activeSubtype = context.getSettingBlocking(ActiveSubtype).ifEmpty {
context.getSettingBlocking(SubtypesSetting).firstOrNull() ?: "en_US:"
}
return convertToSubtype(activeSubtype)
}
fun hasMultipleEnabledSubtypes(context: Context): Boolean {
return context.getSettingBlocking(SubtypesSetting).size > 1
}
fun subtypeToString(subtype: InputMethodSubtype): String {
return subtype.locale + ":" + (subtype.extraValue ?: "") + ":" + subtype.languageTag
}
fun removeLanguage(context: Context, entry: InputMethodSubtype) {
val value = subtypeToString(entry)
val currentSetting = context.getSettingBlocking(SubtypesSetting)
context.setSettingBlocking(SubtypesSetting.key, currentSetting.filter { it != value && it != value.replace("::", ":") }.toSet())
if(context.getSettingBlocking(ActiveSubtype) == value) {
context.setSettingBlocking(ActiveSubtype.key, currentSetting.find {
it != value
} ?: "")
}
}
fun addLanguage(context: Context, language: Locale, layout: String) {
val value = subtypeToString(
InputMethodSubtypeBuilder()
.setSubtypeLocale(language.toString())
.setSubtypeExtraValue("KeyboardLayoutSet=$layout")
.build()
)
val currentSetting = context.getSettingBlocking(SubtypesSetting)
context.setSettingBlocking(SubtypesSetting.key, currentSetting + setOf(value))
}
fun getName(inputMethodSubtype: InputMethodSubtype): String {
return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(inputMethodSubtype)
}
fun getNameForLocale(locale: String): String {
return getName(InputMethodSubtypeBuilder().setSubtypeLocale(locale).build())
}
fun getLocale(locale: String): Locale {
return Locale.forLanguageTag(locale.replace("_", "-"))
}
fun getLocale(inputMethodSubtype: InputMethodSubtype): Locale {
return getLocale(inputMethodSubtype.locale)
}
fun getLayoutName(context: Context, layout: String): String {
val resourceId = context.resources.getIdentifier("layout_$layout", "string", context.packageName)
if(resourceId == 0){
return layout
} else {
return context.getString(resourceId)
}
}
fun layoutsMappedByLanguage(layouts: Set<String>): Map<String, List<InputMethodSubtype>> {
val subtypes = layouts.map {
convertToSubtype(it)
}
return HashMap<String, MutableList<InputMethodSubtype>>().apply {
subtypes.forEach {
val list = this.getOrPut(it.locale) { mutableListOf() }
list.add(it)
}
}.mapValues { it.value.toImmutableList() }
}
}
@Composable
@Preview
fun LanguageSwitcherDialog(
onDismiss: () -> Unit = { }
) {
val inspection = LocalInspectionMode.current
val context = LocalContext.current
val subtypeSet = if(inspection) {
setOf("en_US:", "pt_PT:", "lt:", "fr:KeyboardLayoutSet=bepo:")
} else {
useDataStoreValueBlocking(SubtypesSetting)
}
val subtypes = remember(subtypeSet) {
Subtypes.layoutsMappedByLanguage(subtypeSet)
}
val keys = remember(subtypes) { subtypes.keys.toList().sorted() }
val activeSubtype = if(inspection) {
"pt_PT:"
} else {
useDataStoreValueBlocking(ActiveSubtype)
}
Surface(shape = RoundedCornerShape(48.dp), color = MaterialTheme.colorScheme.background) {
Column {
Spacer(modifier = Modifier.height(16.dp))
Text(
"Select language",
textAlign = TextAlign.Center,
style = Typography.titleLarge,
modifier = Modifier
.fillMaxWidth()
.padding(0.dp, 16.dp)
)
LazyColumn(modifier = Modifier.weight(1.0f)) {
items(keys) { locale ->
subtypes[locale]!!.forEach { subtype ->
val layout = Subtypes.getLayoutName(context,
subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: ""
)
val title = if(inspection) { subtype.locale } else { Subtypes.getName(subtype) }
val selected = activeSubtype == Subtypes.subtypeToString(subtype)
val item = @Composable {
NavigationItem(
title = title,
subtitle = layout.ifBlank { null },
style = NavigationItemStyle.MiscNoArrow,
navigate = {
context.setSettingBlocking(ActiveSubtype.key, Subtypes.subtypeToString(subtype))
onDismiss()
}
)
}
if (selected) {
Surface(color = MaterialTheme.colorScheme.primary) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimary) {
item()
}
}
} else {
item()
}
}
}
}
Row(modifier = Modifier.height(64.dp)) {
Spacer(modifier = Modifier.weight(1.0f))
TextButton(onClick = {
val inputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showInputMethodPicker()
onDismiss()
}) {
Text("Switch Keyboard")
}
TextButton(onClick = {
val intent = Intent()
intent.setClass(context, SettingsActivity::class.java)
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
)
intent.putExtra("navDest", "languages")
context.startActivity(intent)
onDismiss()
}) {
Text("Languages")
}
Spacer(modifier = Modifier.width(32.dp))
}
}
}
}

View File

@ -59,12 +59,6 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver {
final String intentAction = intent.getAction();
if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) {
Log.i(TAG, "Package has been replaced: " + context.getPackageName());
// Need to restore additional subtypes because system always clears additional
// subtypes when the package is replaced.
RichInputMethodManager.init(context);
final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes();
richImm.setAdditionalInputMethodSubtypes(additionalSubtypes);
} else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) {
Log.i(TAG, "Boot has been completed");
} else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) {

View File

@ -38,7 +38,8 @@ import org.futo.inputmethod.latin.Dictionary
import org.futo.inputmethod.latin.LatinIMELegacy
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.ReadOnlyBinaryDictionary
import org.futo.inputmethod.latin.RichInputMethodManager
import org.futo.inputmethod.latin.Subtypes
import org.futo.inputmethod.latin.SubtypesSetting
import org.futo.inputmethod.latin.uix.settings.NavigationItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
@ -68,13 +69,16 @@ data class InputLanguage(
)
fun getActiveLanguages(context: Context): List<InputLanguage> {
RichInputMethodManager.init(context)
return RichInputMethodManager.getInstance().getMyEnabledInputMethodSubtypeList(true).map {
val name = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(it)
InputLanguage(it.locale, name, it)
}.toList()
SubtypeLocaleUtils.init(context)
return context.getSettingBlocking(SubtypesSetting)
.let { Subtypes.layoutsMappedByLanguage(it) }
.map {
InputLanguage(
it.value.first().locale,
Subtypes.getName(it.value.first()),
it.value.first()
)
}
}

View File

@ -46,6 +46,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.futo.inputmethod.latin.AudioAndHapticFeedbackManager
import org.futo.inputmethod.latin.BuildConfig
import org.futo.inputmethod.latin.LanguageSwitcherDialog
import org.futo.inputmethod.latin.LatinIME
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.SuggestedWords
@ -57,6 +58,7 @@ import org.futo.inputmethod.latin.uix.actions.ActionRegistry
import org.futo.inputmethod.latin.uix.actions.EmojiAction
import org.futo.inputmethod.latin.uix.settings.SettingsActivity
import org.futo.inputmethod.latin.uix.theme.ThemeOption
import org.futo.inputmethod.latin.uix.theme.UixThemeAuto
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import org.futo.inputmethod.updates.DISABLE_UPDATE_REMINDER
import org.futo.inputmethod.updates.autoDeferManualUpdateIfNeeded
@ -66,7 +68,6 @@ import org.futo.inputmethod.updates.openManualUpdateCheck
import org.futo.inputmethod.updates.retrieveSavedLastUpdateCheckResult
import java.util.Locale
private class LatinIMEActionInputTransaction(
private val inputLogic: InputLogic,
shouldApplySpace: Boolean,
@ -334,13 +335,13 @@ class UixManager(private val latinIME: LatinIME) {
}
}
val wordBeingForgotten: MutableState<SuggestedWordInfo?> = mutableStateOf(null)
val forgetWordDismissed: MutableState<Boolean> = mutableStateOf(true)
private val wordBeingForgotten: MutableState<SuggestedWordInfo?> = mutableStateOf(null)
private val forgetWordDismissed: MutableState<Boolean> = mutableStateOf(true)
@Composable
fun BoxScope.ForgetWordDialog() {
AnimatedVisibility(
visible = forgetWordDismissed.value == false,
visible = !forgetWordDismissed.value,
modifier = Modifier.matchParentSize(),
enter = fadeIn(),
exit = fadeOut()
@ -349,11 +350,13 @@ class UixManager(private val latinIME: LatinIME) {
Box(modifier = Modifier.matchParentSize()) {
Surface(
color = Color.Black.copy(alpha = 0.66f),
modifier = Modifier.matchParentSize().pointerInput(Unit) {
this.detectTapGestures(onPress = {
forgetWordDismissed.value = true
})
}
modifier = Modifier
.matchParentSize()
.pointerInput(Unit) {
this.detectTapGestures(onPress = {
forgetWordDismissed.value = true
})
}
) { }
Surface(
@ -405,6 +408,23 @@ class UixManager(private val latinIME: LatinIME) {
}
}
private var languageSwitcherDialog: DialogComposeView? = null
fun showLanguageSwitcher() {
// Dismiss old dialog
languageSwitcherDialog?.dismiss()
// Create new dialog
languageSwitcherDialog = createDialogComposeView(latinIME) {
UixThemeAuto {
LanguageSwitcherDialog(
onDismiss = { it.dismiss() }
)
}
}
languageSwitcherDialog?.show()
}
fun setContent() {
composeView?.setContent {
UixThemeWrapper(latinIME.colorScheme) {
@ -522,6 +542,7 @@ class UixManager(private val latinIME: LatinIME) {
fun onInputFinishing() {
closeActionWindow()
languageSwitcherDialog?.dismiss()
}
fun cleanUpPersistentStates() {

View File

@ -1,8 +1,20 @@
package org.futo.inputmethod.latin.uix
import android.app.Dialog
import android.content.Context
import android.util.TypedValue
import android.view.Gravity
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import org.futo.inputmethod.latin.LatinIME
import org.futo.inputmethod.latin.R
import java.net.URLDecoder
import java.net.URLEncoder
@ -29,4 +41,71 @@ fun String.urlEncode(): String {
fun String.urlDecode(): String {
return URLDecoder.decode(this, "utf-8")
}
// This ugly workaround is required as Android Compose freaks out when you use a Dialog outside of
// an activity (i.e. in an input method service)
data class DialogComposeView(
val dialog: Dialog,
val composeView: ComposeView
)
fun createDialogComposeView(
latinIME: LatinIME,
maxWidthProportion: Float = 0.9f,
maxHeightProportion: Float = 0.75f,
dimAmount: Float = 0.5f,
onDismiss: () -> Unit = { },
content: @Composable (Dialog) -> Unit,
): DialogComposeView {
val context: Context = latinIME
val composeView = ComposeView(context)
val dialog = Dialog(context).apply {
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(composeView)
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
setOnDismissListener {
onDismiss()
}
}
val window = dialog.window
window?.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM or WindowManager.LayoutParams.FLAG_DIM_BEHIND)
window?.attributes?.token = latinIME.latinIMELegacy.mKeyboardSwitcher.mainKeyboardView.windowToken
window?.attributes?.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG
window?.apply {
val displayMetrics = context.resources.displayMetrics
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
val maxWidth = (width * maxWidthProportion).toInt()
val maxHeight = (height * maxHeightProportion).toInt()
setLayout(maxWidth, maxHeight)
setGravity(Gravity.CENTER)
setBackgroundDrawable(AppCompatResources.getDrawable(context, R.drawable.empty))
setDimAmount(dimAmount)
}
composeView.setViewTreeLifecycleOwner(latinIME)
composeView.setViewTreeSavedStateRegistryOwner(latinIME)
composeView.setContent { content(dialog) }
return DialogComposeView(dialog, composeView)
}
fun DialogComposeView.show() {
dialog.show()
}
fun DialogComposeView.dismiss() {
dialog.dismiss()
}

View File

@ -20,6 +20,7 @@ val AllActions = listOf(
RedoAction,
VoiceInputAction,
SystemVoiceInputAction,
SwitchLanguageAction
)
@ -70,7 +71,8 @@ val DefaultActions = listOf(
ClipboardAction,
SettingsAction,
ThemeAction,
MemoryDebugAction
MemoryDebugAction,
SwitchLanguageAction
)
val DefaultActionsString = ActionRegistry.actionsToString(DefaultActions)

View File

@ -0,0 +1,32 @@
package org.futo.inputmethod.latin.uix.actions
import android.content.Context
import org.futo.inputmethod.latin.ActiveSubtype
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.SubtypesSetting
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.setSettingBlocking
fun switchToNextLanguage(context: Context) {
val enabledSubtypes = context.getSettingBlocking(SubtypesSetting).toList()
val currentSubtype = context.getSettingBlocking(ActiveSubtype)
val index = enabledSubtypes.indexOf(currentSubtype)
val nextIndex = if(index == -1) {
0
} else {
(index + 1) % enabledSubtypes.size
}
context.setSettingBlocking(ActiveSubtype.key, enabledSubtypes[nextIndex])
}
val SwitchLanguageAction = Action(
icon = R.drawable.globe,
name = R.string.show_language_switch_key,
simplePressImpl = { manager, _ ->
switchToNextLanguage(manager.getContext())
},
windowImpl = null,
)

View File

@ -23,6 +23,10 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
@ -539,3 +543,64 @@ fun SettingTextField(title: String, placeholder: String, field: SettingsKey<Stri
.padding(8.dp, 4.dp),
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun<T> DropDownPicker(
label: String,
options: List<T>,
selection: T?,
onSet: (T) -> Unit,
getDisplayName: (T) -> String,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
modifier = modifier
) {
TextField(
readOnly = true,
value = selection?.let(getDisplayName) ?: "None",
onValueChange = { },
label = if (label.isNotBlank()) {
{ Text(label) }
} else {
null
},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded
)
},
colors = ExposedDropdownMenuDefaults.textFieldColors(
focusedLabelColor = MaterialTheme.colorScheme.onPrimaryContainer,
focusedLeadingIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
focusedIndicatorColor = MaterialTheme.colorScheme.onPrimaryContainer,
focusedTrailingIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
),
modifier = Modifier.menuAnchor()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
options.forEach { selectionOption ->
DropdownMenuItem(
text = {
Text(getDisplayName(selectionOption))
},
onClick = {
onSet(selectionOption)
expanded = false
}
)
}
}
}
}

View File

@ -11,6 +11,7 @@ import androidx.navigation.compose.rememberNavController
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.ErrorDialog
import org.futo.inputmethod.latin.uix.InfoDialog
import org.futo.inputmethod.latin.uix.settings.pages.AddLanguageScreen
import org.futo.inputmethod.latin.uix.settings.pages.AdvancedParametersScreen
import org.futo.inputmethod.latin.uix.settings.pages.BlacklistScreen
import org.futo.inputmethod.latin.uix.settings.pages.CreditsScreen
@ -49,6 +50,7 @@ fun SettingsNavigator(
) {
composable("home") { HomeScreen(navController) }
composable("languages") { LanguagesScreen(navController) }
composable("addLanguage") { AddLanguageScreen(navController) }
composable("predictiveText") { PredictiveTextScreen(navController) }
composable("advancedparams") { AdvancedParametersScreen(navController) }
composable("typing") { TypingScreen(navController) }

View File

@ -0,0 +1,160 @@
package org.futo.inputmethod.latin.uix.settings.pages
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import org.futo.inputmethod.latin.Subtypes
import org.futo.inputmethod.latin.uix.settings.DropDownPicker
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.SettingItem
val QwertyVariants = listOf("qwerty", "qwertz", "dvorak", "azerty", "colemak", "bepo", "pcqwerty")
val locales = mapOf(
"af" to listOf("qwerty"),
"ar" to listOf("arabic"),
"az_AZ" to listOf("qwerty"),
"be_BY" to listOf("east_slavic"),
"bg" to listOf("bulgarian"),
"bg" to listOf("bulgarian_bds"),
"bn_BD" to listOf("bengali_akkhor"),
"bn_IN" to listOf("bengali"),
"ca" to listOf("spanish"),
"cs" to listOf("qwertz"),
"da" to listOf("nordic"),
"de" to listOf("qwertz"),
"de_CH" to listOf("swiss"),
"el" to listOf("greek"),
"en_IN" to listOf("qwerty"),
"en_US" to QwertyVariants,
"en_GB" to QwertyVariants,
"eo" to listOf("spanish"),
"es" to listOf("spanish"),
"es_US" to listOf("spanish"),
"es_419" to listOf("spanish"),
"et_EE" to listOf("nordic"),
"eu_ES" to listOf("spanish"),
"fa" to listOf("farsi"),
"fi" to listOf("nordic"),
"fr" to listOf("azerty", "qwerty", "swiss", "bepo"),
"fr_CA" to listOf("qwerty", "azerty", "swiss", "bepo"),
"fr_CH" to listOf("swiss", "qwerty", "azerty", "bepo"),
"gl_ES" to listOf("spanish"),
"hi" to listOf("hindi", "hindi_compact"),
//"hi_ZZ" to listOf("qwerty"),
"hr" to listOf("qwertz"),
"hu" to listOf("qwertz"),
"hy_AM" to listOf("armenian_phonetic"),
"in" to listOf("qwerty"),
"is" to listOf("qwerty"),
"it" to listOf("qwerty"),
"it_CH" to listOf("swiss"),
"iw" to listOf("hebrew"),
"ka_GE" to listOf("georgian"),
"kk" to listOf("east_slavic"),
"km_KH" to listOf("khmer"),
"kn_IN" to listOf("kannada"),
"ky" to listOf("east_slavic"),
"lo_LA" to listOf("lao"),
"lt" to listOf("qwerty"),
"lv" to listOf("qwerty"),
"mk" to listOf("south_slavic"),
"ml_IN" to listOf("malayalam"),
"mn_MN" to listOf("mongolian"),
"mr_IN" to listOf("marathi"),
"ms_MY" to listOf("qwerty"),
"nb" to listOf("nordic"),
"ne_NP" to listOf("nepali_romanized", "nepali_traditional"),
"nl" to listOf("qwerty"),
"nl_BE" to listOf("azerty"),
"pl" to listOf("qwerty"),
"pt_BR" to listOf("qwerty"),
"pt_PT" to listOf("qwerty"),
"ro" to listOf("qwerty"),
"ru" to listOf("east_slavic"),
"si_LK" to listOf("sinhala"),
"sk" to listOf("qwerty"),
"sl" to listOf("qwerty"),
"sr" to listOf("south_slavic"),
"sr_ZZ" to listOf("serbian_qwertz"),
"sv" to listOf("nordic"),
"sw" to listOf("qwerty"),
"ta_IN" to listOf("tamil"),
"ta_LK" to listOf("tamil"),
"ta_SG" to listOf("tamil"),
"te_IN" to listOf("telugu"),
"th" to listOf("thai"),
"tl" to listOf("spanish"),
"tr" to listOf("qwerty"),
"uk" to listOf("east_slavic"),
"uz_UZ" to listOf("uzbek"),
"vi" to listOf("qwerty"),
"zu" to listOf("qwerty"),
"zz" to listOf("qwerty"),
)
@Preview
@Composable
fun AddLanguageScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current
val selectedLocale: MutableState<String> = remember { mutableStateOf(context.resources.configuration.locale.toString()) }
val selectedLayout: MutableState<String> = remember { mutableStateOf("qwerty") }
val keys = remember { locales.keys.toList() }
ScrollableList {
ScreenTitle("Add Language", showBack = true, navController)
SettingItem(title = "Language") {
DropDownPicker(
"",
keys,
selectedLocale.value,
{
selectedLocale.value = it
selectedLayout.value = locales[it]!!.first()
},
{
Subtypes.getNameForLocale(it)
},
modifier = Modifier.width(180.dp)
)
}
SettingItem(title = "Layout") {
DropDownPicker(
"",
locales[selectedLocale.value] ?: listOf(),
selectedLayout.value,
{ selectedLayout.value = it },
{ Subtypes.getLayoutName(context, it) },
modifier = Modifier.width(180.dp)
)
}
Button(onClick = {
Subtypes.addLanguage(
context,
Subtypes.getLocale(selectedLocale.value),
selectedLayout.value
)
navController.navigateUp()
}, modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Text("Add")
}
}
}

View File

@ -52,6 +52,11 @@ fun CreditsScreen(navController: NavHostController = rememberNavController()) {
modifier = Modifier.clickable {
context.openURI("https://github.com/gkonovalov/android-vad")
})
ParagraphText("Some keyboard layouts were taken from the CyanogenMod/LineageOS fork of the LatinIME keyboard. Their fork is Apache-2.0 licensed. Copyright (C) 2015 The CyanogenMod Project",
modifier = Modifier.clickable {
context.openURI("https://github.com/LineageOS/android_packages_inputmethods_LatinIME")
})
Spacer(modifier = Modifier.height(16.dp))
ParagraphText("Note: The authors listed above are not affiliated with us and do not endorse or promote us")

View File

@ -1,32 +1,39 @@
package org.futo.inputmethod.latin.uix.settings.pages
import android.content.Context
import android.view.inputmethod.InputMethodManager
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import okhttp3.internal.toImmutableList
import org.futo.inputmethod.latin.BinaryDictionaryGetter
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.RichInputMethodManager
import org.futo.inputmethod.latin.saveSubtypes
import org.futo.inputmethod.latin.Subtypes
import org.futo.inputmethod.latin.SubtypesSetting
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.FileKind
import org.futo.inputmethod.latin.uix.ResourceHelper
import org.futo.inputmethod.latin.uix.getSetting
@ -35,10 +42,10 @@ import org.futo.inputmethod.latin.uix.settings.NavigationItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.Tip
import org.futo.inputmethod.latin.uix.settings.openLanguageSettings
import org.futo.inputmethod.latin.uix.settings.SettingItem
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking
import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.uix.youAreImporting
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils
import org.futo.inputmethod.latin.xlm.ModelPaths
import org.futo.inputmethod.updates.openURI
import java.util.Locale
@ -101,27 +108,12 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
val context = LocalContext.current
val deleteDialogInfo: MutableState<DeleteInfo?> = remember { mutableStateOf(null) }
val inputMethodManager = remember { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
val inputMethodList = remember { mutableStateOf(
inputMethodManager.getEnabledInputMethodSubtypeList(
RichInputMethodManager.getInstance().inputMethodInfoOfThisIme,
true
).toImmutableList()
) }
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleState by lifecycleOwner.lifecycle.currentStateFlow.collectAsState()
LaunchedEffect(lifecycleState) {
delay(250L)
inputMethodList.value = inputMethodManager.getEnabledInputMethodSubtypeList(
RichInputMethodManager.getInstance().inputMethodInfoOfThisIme,
true
)
context.saveSubtypes()
val inputMethods = useDataStoreValueBlocking(SubtypesSetting)
val inputMethodList = remember(inputMethods) {
Subtypes.layoutsMappedByLanguage(inputMethods)
}
val inputMethodKeys = remember(inputMethodList) { inputMethodList.keys.toList().sorted() }
if(deleteDialogInfo.value != null) {
val info = deleteDialogInfo.value!!
@ -139,17 +131,19 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
ScreenTitle("Languages", showBack = true, navController)
NavigationItem(
title = "Enable/disable languages",
title = "Add language",
style = NavigationItemStyle.Misc,
navigate = { context.openLanguageSettings() },
navigate = {
navController.navigate("addLanguage")
},
)
Tip("Note: This screen is a WIP, use the above option to toggle languages")
inputMethodKeys.forEach { localeString ->
val subtypes = inputMethodList[localeString]!!
inputMethodList.value.forEach {
val name = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(it)
val locale = Subtypes.getLocale(localeString)
val locale = Locale.forLanguageTag(it.locale.replace("_", "-"))
val name = Subtypes.getName(subtypes.first())
val voiceInputModelName = ResourceHelper.tryFindingVoiceInputModelForLocale(context, locale)?.name?.let { stringResource(it) }
val dictionaryName = runBlocking { ResourceHelper.findKeyForLocaleAndKind(context, locale, FileKind.Dictionary) }?.let {
@ -170,7 +164,36 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
transformerModel = transformerName
)
ScreenTitle(name)
Row(modifier = Modifier.fillMaxWidth()) {
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.align(Alignment.CenterVertically).padding(0.dp, 16.dp)) {
Text(name, style = Typography.titleLarge)
if(subtypes.size == 1) {
val layout = Subtypes.getLayoutName(context,
subtypes.first().getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: ""
)
Text(layout,
style = Typography.bodySmall,
color = MaterialTheme.colorScheme.outline)
}
}
Spacer(modifier = Modifier.weight(1.0f))
if(subtypes.size == 1) {
IconButton(modifier = Modifier.fillMaxHeight().align(Alignment.CenterVertically), onClick = {
Subtypes.removeLanguage(context, subtypes.first())
}) {
Icon(
Icons.Default.Clear,
contentDescription = "Remove language",
modifier = Modifier
)
}
}
}
NavigationItem(
title = options.voiceInputModel ?: "None",
@ -208,6 +231,25 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
},
icon = painterResource(id = R.drawable.cpu)
)
if(subtypes.size > 1) {
subtypes.forEach {
val layout = Subtypes.getLayoutName(
context,
it.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET)
)
SettingItem(title = "Layout $layout") {
IconButton(modifier = Modifier.fillMaxHeight(), onClick = {
Subtypes.removeLanguage(context, it)
}) {
Icon(
Icons.Default.Clear,
contentDescription = "Remove layout $layout",
modifier = Modifier
)
}
}
}
}
}
}

View File

@ -574,17 +574,6 @@ public class DictionaryInfoUtils {
addOrUpdateDictInfo(dictList, dictionaryInfo);
}
// Generate the dictionary information from the enabled subtypes. This will not
// overwrite the real records.
RichInputMethodManager.init(context);
List<InputMethodSubtype> enabledSubtypes = RichInputMethodManager
.getInstance().getMyEnabledInputMethodSubtypeList(true);
for (InputMethodSubtype subtype : enabledSubtypes) {
Locale locale = LocaleUtils.constructLocaleFromString(subtype.getLocale());
DictionaryInfo dictionaryInfo = createDictionaryInfoFromLocale(locale);
addOrUpdateDictInfo(dictList, dictionaryInfo);
}
return dictList;
}

View File

@ -27,6 +27,7 @@ import android.util.Log;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.Subtypes;
import org.futo.inputmethod.latin.common.LocaleUtils;
import org.futo.inputmethod.latin.common.StringUtils;
@ -286,6 +287,19 @@ public final class SubtypeLocaleUtils {
final String replacementString = getReplacementString(subtype, displayLocale);
// TODO: rework this for multi-lingual subtypes
final int nameResId = subtype.getNameResId();
if(nameResId == 0) {
if(replacementString.isEmpty()) {
return StringUtils.capitalizeFirstCodePoint(
Subtypes.INSTANCE.getLocale(subtype).getDisplayName(displayLocale),
displayLocale);
} else {
return StringUtils.capitalizeFirstCodePoint(
replacementString,
displayLocale);
}
}
final RunInLocale<String> getSubtypeName = new RunInLocale<String>() {
@Override
protected String job(final Resources res) {