Compare commits

..

40 Commits

Author SHA1 Message Date
Patrick Goldinger
4adaf9a315 Release v0.3.16-beta03 2022-05-17 23:14:42 +02:00
florisboard-bot
01eee827df Update translations from Crowdin 2022-05-17 22:33:25 +02:00
SaeID/Rz
fd87241887 Add new persian layout (#1823)
* Delete extension.json

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update fa3.json

insert character "ئ" as the default character

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-05-17 16:12:19 +02:00
Patrick Goldinger
d63792cb15 Merge pull request #1855 from florisboard/emoji-fixes-and-small-improvements
Emoji minor bug fixes / improvements
2022-05-17 15:39:11 +02:00
Patrick Goldinger
e2c4992b51 Fix symbols emoji category icon not representative (#1421) 2022-05-17 00:14:49 +02:00
Patrick Goldinger
481d929f2b Fix incorrect aspect ratio for emoji keys (#1856) 2022-05-16 23:05:40 +02:00
Patrick Goldinger
3d1f1d1e12 Rework internal EmojiCompat implementation (#1770) 2022-05-16 22:52:13 +02:00
Patrick Goldinger
9f4ef7e1ad Re-add vibration to emoji screen (#1675) 2022-05-16 18:26:34 +02:00
Patrick Goldinger
7d24f0c5ae Merge pull request #1844 from florisboard/glide-typing-fixes-and-improvements
Glide typing + gestures improvements / bug fixes
2022-05-15 18:35:56 +02:00
Patrick Goldinger
67408130b1 Fix glide typing not working for long words (#1851) 2022-05-15 13:07:09 +02:00
Patrick Goldinger
cdb9504e5f Fix glide threshold using incorrect units (#1023) 2022-05-14 12:38:45 +02:00
Patrick Goldinger
977b32de6e Add punctuation rules to keyboard extension (#596, #1828) 2022-05-14 11:37:54 +02:00
Patrick Goldinger
4ae4eb00e9 Auto-disable glide typing for password fields 2022-05-14 02:00:47 +02:00
Patrick Goldinger
063fca6dd1 Adjust popup mappings formatting in extension.json 2022-05-13 19:08:10 +02:00
Patrick Goldinger
a650bfe94c Fix active key not appearing pressed for moving gestures (#1846) 2022-05-13 11:47:59 +02:00
Patrick Goldinger
517448da06 Fix phantom space not resetting in selection update (#916) 2022-05-13 11:38:51 +02:00
Patrick Goldinger
8ba9abdace Fix glide typing broken in some input fields (#1009, #1247)
This applies to apps that report that they are "raw" but in fact they are rich. This new detection works by checking if the initial selection is valid, if so then we assume that the editor is rich.
2022-05-12 20:23:58 +02:00
Patrick Goldinger
193d03e0eb Remove Jetifier flag
See https://developer.android.com/studio/releases/gradle-plugin#jetifier-build-analyzer
2022-05-11 23:28:41 +02:00
Patrick Goldinger
363cfe1443 Migrate app namespace from manifest to build.gradle.kts
This is a preparation for AGP 8.0.0 in the future.
2022-05-11 23:15:59 +02:00
Patrick Goldinger
d9cd36966e Upgrade Gradle to 7.3.3 and AGP to 7.2.0 2022-05-11 22:59:37 +02:00
Patrick Goldinger
02bfae55b9 Release v0.3.16-beta02 2022-05-11 22:36:29 +02:00
florisboard-bot
6edbf64104 Update translations from Crowdin 2022-05-11 22:26:14 +02:00
Thanh, H
1c29319036 Expand Basic Telex for Vietnamese to proper Telex (#1797)
* Update tetex rule for: vowel + side char

* Update full rule for a, ă

* Add rule for e

* Update e+{c, m, n} and ê + {c, m} (2 rules)

* Add full rule for e, ă (mark and un-mark)

* Full rule for i

* Full rule o

* Full rule for u

* Add â+{c} (mark + unmark)

* Add â + {m, n}

* Add â + {p, t}

* Full â (mark + unmark)

* Add ê + {n, p, t} (mark)

* Full ê

* Full ô

* Full ơ

* Full ư

* Add full iê + {m, n, p, t, u, ng}: mark & unmark

* Add uyê + {n, t}: mark, un-mark

* Add ươ + {c, i}

* Add ươm

* Add ươ: mark & unmark

* Add full yê: {m, n, t, u, ng} - mark, unmark

* Remove useless rule: "ưow": "ươ"

* Add ach, ich, êch

* Fix Vietnamese default subtype using incorrect composer ID

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-05-11 17:52:12 +02:00
Patrick Goldinger
8c8664cafa Merge pull request #1832 from florisboard/fix-and-improve-kbd-logic
Fix and improve keyboard/input logic
2022-05-10 23:36:05 +02:00
Patrick Goldinger
652c7f4e4b Fix auto-capitalization and re-evaluate on new subtype (#1623)
Switching subtype forces a re-evaluation of the input shift state. Korean never auto-capitalizes anymore.
2022-05-10 00:06:51 +02:00
Patrick Goldinger
753fbc30df Re-implement composers into new editor instance logic 2022-05-09 23:32:10 +02:00
Patrick Goldinger
01de9a4ae1 Add ability to hold shift without caps lock (#1764)
Pressing a char before long press timeout will suppress caps lock -> this allows to use the shift key like on PC
2022-05-09 21:19:27 +02:00
Patrick Goldinger
aa52784174 Fix capitalization issues in text keyboard logic (#227) 2022-05-09 19:36:14 +02:00
Patrick Goldinger
dc72e2162e Fix shift key state concurrency issue in touch logic (#227) 2022-05-09 01:16:13 +02:00
Patrick Goldinger
8ef37cedfb Fix shift long press resetting after releasing (#1834) 2022-05-09 00:14:19 +02:00
Patrick Goldinger
08a77ce0eb Add shift state selector and fix Dvorak layout (#528) 2022-05-08 22:02:39 +02:00
Patrick Goldinger
afc6f21a6a Rework shift state management and possible states
There's now a differentiation between automatic and manual shifting. This is the prerequisite for proper shifting of symbol rows.
2022-05-08 21:30:46 +02:00
Patrick Goldinger
bdc2a9a5d3 Upgrade Accompanist to 0.23.1 2022-05-08 21:08:17 +02:00
Patrick Goldinger
c27365bccd Adjust timing for double tap / key repeat events (#1095)
Double tap events now use a fixed value provided by the system (by default `300ms`) and do not depend on `prefs.keyboard.longPressDelay` anymore.

Additionally key repeat also uses a system value, however both the default system value and the previous hard-coded value are `50ms`, no nothing should change here for the user.
2022-05-08 13:00:36 +02:00
Patrick Goldinger
83d4fc727d Fix backspace sometimes not working in initial state (#1831) 2022-05-07 18:47:45 +02:00
Patrick Goldinger
5bf4819d83 Fix space swipe not checking for raw input editor 2022-05-07 17:50:57 +02:00
Patrick Goldinger
60221743f6 Fix Smartbar suggestion UI not updating correctly 2022-05-07 17:39:55 +02:00
Patrick Goldinger
9d78661ecb Refactor Room to use KSP + export schema correctly 2022-05-07 11:52:49 +02:00
Patrick Goldinger
addedb6f9b Fix long press action being run outside main thread 2022-05-06 19:08:21 +02:00
Patrick Goldinger
e1701b2ba7 Improve devtools input state overlay 2022-05-05 22:51:13 +02:00
104 changed files with 2120 additions and 742 deletions

3
.gitignore vendored
View File

@@ -43,6 +43,3 @@ crowdin.properties
# C++
.cxx/
# AndroidX Room schema JSONs
/app/schemas/

View File

@@ -1,13 +1,14 @@
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
kotlin("plugin.serialization")
id("com.google.devtools.ksp")
id("com.google.android.gms.oss-licenses-plugin")
id("de.mannodermaus.android-junit5")
}
android {
namespace = "dev.patrickgold.florisboard"
compileSdk = 31
buildToolsVersion = "31.0.0"
ndkVersion = "22.1.7171670"
@@ -31,19 +32,15 @@ android {
applicationId = "dev.patrickgold.florisboard"
minSdk = 24
targetSdk = 31
versionCode = 81
versionCode = 83
versionName = "0.3.16"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments += mapOf(
Pair("room.schemaLocation", "$projectDir/schemas"),
Pair("room.incremental", "true"),
Pair("room.expandProjection", "true")
)
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("room.incremental", "true")
arg("room.expandProjection", "true")
}
externalNativeBuild {
@@ -118,7 +115,7 @@ android {
create("beta") // Needed because by default the "beta" BuildType does not exist
named("beta").configure {
applicationIdSuffix = ".beta"
versionNameSuffix = "-beta01"
versionNameSuffix = "-beta03"
proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
resValue("mipmap", "floris_app_icon", "@mipmap/ic_app_icon_beta")
@@ -165,9 +162,9 @@ dependencies {
implementation("androidx.emoji2:emoji2:1.1.0")
implementation("androidx.emoji2:emoji2-views:1.1.0")
implementation("androidx.navigation:navigation-compose:2.4.2")
implementation("com.google.accompanist:accompanist-flowlayout:0.23.0")
implementation("com.google.accompanist:accompanist-insets:0.23.0")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.23.0")
implementation("com.google.accompanist:accompanist-flowlayout:0.23.1")
implementation("com.google.accompanist:accompanist-insets:0.23.1")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.23.1")
implementation("dev.patrickgold.jetpref:jetpref-datastore-model:0.1.0-beta08")
implementation("dev.patrickgold.jetpref:jetpref-datastore-ui:0.1.0-beta08")
implementation("dev.patrickgold.jetpref:jetpref-material-ui:0.1.0-beta08")
@@ -175,7 +172,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
implementation("androidx.room:room-runtime:2.4.2")
kapt("androidx.room:room-compiler:2.4.2")
ksp("androidx.room:room-compiler:2.4.2")
testImplementation("io.kotest:kotest-runner-junit5:5.2.3")
testImplementation("io.kotest:kotest-assertions-core:5.2.3")

View File

@@ -0,0 +1,62 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "d573e2ae2cbe7026957bc8d8fda291f3",
"entities": [
{
"tableName": "clipboard_files",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER NOT NULL, `_display_name` TEXT NOT NULL, `_size` INTEGER NOT NULL, `mimeTypes` TEXT NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "displayName",
"columnName": "_display_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "size",
"columnName": "_size",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "mimeTypes",
"columnName": "mimeTypes",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_clipboard_files__id",
"unique": false,
"columnNames": [
"_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_clipboard_files__id` ON `${TABLE_NAME}` (`_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd573e2ae2cbe7026957bc8d8fda291f3')"
]
}
}

View File

@@ -0,0 +1,80 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "6fbec9d7ea017f8aefac4fb84dbd5189",
"entities": [
{
"tableName": "clipboard_history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `text` TEXT, `uri` TEXT, `creationTimestampMs` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `mimeTypes` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "text",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uri",
"columnName": "uri",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "creationTimestampMs",
"columnName": "creationTimestampMs",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isPinned",
"columnName": "isPinned",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "mimeTypes",
"columnName": "mimeTypes",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_clipboard_history__id",
"unique": false,
"columnNames": [
"_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_clipboard_history__id` ON `${TABLE_NAME}` (`_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6fbec9d7ea017f8aefac4fb84dbd5189')"
]
}
}

View File

@@ -0,0 +1,68 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "787af4a2df15bf9d2c0597519d3fb273",
"entities": [
{
"tableName": "words",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `word` TEXT NOT NULL, `frequency` INTEGER NOT NULL, `locale` TEXT, `shortcut` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "word",
"columnName": "word",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "freq",
"columnName": "frequency",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "locale",
"columnName": "locale",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "shortcut",
"columnName": "shortcut",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_words__id",
"unique": false,
"columnNames": [
"_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_words__id` ON `${TABLE_NAME}` (`_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '787af4a2df15bf9d2c0597519d3fb273')"
]
}
}

View File

@@ -15,8 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dev.patrickgold.florisboard">
xmlns:tools="http://schemas.android.com/tools">
<!-- Permission needed to vibrate if the user has key press vibration enabled -->
<uses-permission android:name="android.permission.VIBRATE"/>
@@ -52,8 +51,7 @@
android:label="@string/floris_app_name"
android:permission="android.permission.BIND_INPUT_METHOD"
android:directBootAware="true"
android:exported="true"
tools:targetApi="n">
android:exported="true">
<intent-filter>
<action android:name="android.view.InputMethod"/>
</intent-filter>
@@ -147,6 +145,17 @@
android:resource="@xml/file_paths"/>
</provider>
<!-- Disable default EmojiCompat initializer -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove"/>
</provider>
</application>
</manifest>

View File

@@ -2,10 +2,10 @@
"$": "ime.extension.keyboard",
"meta": {
"id": "org.florisboard.composers",
"version": "0.1.0",
"version": "0.1.1",
"title": "Default composers",
"description": "Default composers which are always available.",
"maintainers": [ "patrickgold <patrick@patrickgold.dev>" ],
"maintainers": [ "patrickgold <patrick@patrickgold.dev>", "thanhhocse96 <thanh.hoquang@pm.me>" ],
"license": "apache-2.0"
},
"composers": [
@@ -13,10 +13,10 @@
{ "$": "hangul-unicode" },
{ "$": "kana-unicode" },
{ "$": "with-rules",
"id": "basic-telex",
"label": "Basic Telex",
"id": "telex",
"label": "Telex",
"rules": {
"aw": "ă", "aa": "â", "dd": "đ", "ee": "ê", "oo": "ô", "ow": "ơ", "uw": "ư", "w": "ư",
"aw": "ă", "aa": "â", "dd": "đ", "ee": "ê", "oo": "ô", "ow": "ơ", "uw": "ư", "w": "ư",
"uow": "ươ",
"af": "à", "ar": "ả", "ax": "ã", "as": "á", "aj": "ạ",
"ăf": "ằ", "ăr": "ẳ", "ăx": "ẵ", "ăs": "ắ", "ăj": "ặ",
@@ -30,7 +30,7 @@
"uf": "ù", "ur": "ủ", "ux": "ũ", "us": "ú", "uj": "ụ",
"ưf": "ừ", "ưr": "ử", "ưx": "ữ", "ưs": "ứ", "ưj": "ự",
"yf": "ỳ", "yr": "ỷ", "yx": "ỹ", "ys": "ý", "yj": "ỵ",
"ăw": "aw", "âa": "aa", "đd": "dd", "êe": "ee", "ôo": "oo", "ơw": "ow", "ưw": "w",
"ăw": "aw", "âa": "aa", "đd": "dd", "êe": "ee", "ôo": "oo", "ơw": "ow", "ưw": "w", "iêe": "iee",
"ươw": "uow",
"àf": "af", "ảr": "ar", "ãx": "ax", "ás": "as", "ạj": "aj",
"ằf": "ăf", "ẳr": "ăr", "ẵx": "ăx", "ắs": "ăs", "ặj": "ăj",
@@ -55,7 +55,562 @@
"ờz": "ơ", "ởz": "ơ", "ỡz": "ơ", "ớz": "ơ", "ợz": "ơ",
"ùz": "u", "ủz": "u", "ũz": "u", "úz": "u", "ụz": "u",
"ừz": "ư", "ửz": "ư", "ữz": "ư", "ứz": "ư", "ựz": "ư",
"ỳz": "y", "ỷz": "y", "ỹz": "y", "ýz": "y", "ỵz": "y"
"ỳz": "y", "ỷz": "y", "ỹz": "y", "ýz": "y", "ỵz": "y",
"acw": "ăc", "amw": "ăm", "anw": "ăn", "apw": "ăp", "atw": "ăt", "angw": "ăng",
"aca": "âc", "ama": "âm", "ana": "ân", "apa": "âp", "ata": "ât", "aua": "âu", "aya": "ây", "anga": "âng",
"eme": "êm", "ene": "ên", "epe": "êp", "ete": "êt", "enhe": "ênh",
"oio": "ôi","omo": "ôm", "ono": "ôn", "opo": "ôp", "oto": "ôt", "ongo": "ông",
"omw": "ơm", "onw": "ơn", "opw": "ơp", "otw": "ơt",
"uaw": "ưa", "uiw": "ưi", "umw": "ưm", "unw": "ưn", "utw": "ưt", "uuw": "ưu", "ungw": "ưng",
"ieme": "iêm", "iene": "iên", "iepe": "iêp", "iete": "iêt", "ieue": "iêu", "ienge": "iêng",
"uocw": "ươc", "uoiw": "ươi", "uomw": "ươm", "uonw": "ươn", "uotw": "ươt", "uongw": "ương",
"uyene": "uyên", "uyete": "uyêt",
"yeme": "yêm", "yene": "yên", "yete": "yêt", "yeue": "yêu", "yenge": "yêng",
"ăcw": "acw", "ămw": "amw", "ănw": "anw", "ăpw": "apw", "ătw": "atw", "ăngw": "angw",
"âca": "aca", "âma": "ama", "âna": "ana", "âpa": "apa", "âta": "ata", "âua": "aua", "âya": "aya", "ânga": "anga",
"ême": "eme", "êne": "ene", "êpe": "epe", "ête": "ete",
"ôio": "oio", "ômo": "omo", "ôno": "ono", "ôpo": "opo", "ôto": "oto", "ôngo": "ongo",
"ơmw": "omw", "ơnw": "onw", "ơpw": "opw", "ơtw": "otw",
"ưaw": "uaw", "ưiw": "uiw", "ưmw": "umw", "ưnw": "unw" , "ưtw": "utw", "ưuw": "uuw", "ưngw": "ungw",
"iême": "ieme", "iêne": "iene", "iêpe": "iepe", "iête": "iete", "iêue": "ieue", "iênge": "ienge",
"ươcw": "uocw", "ươiw": "uoiw", "ươmw": "uomw", "ươnw": "uonw", "ươtw": "uotw", "ươngw": "uongw",
"uyêne": "uyene", "uyêt": "uyete",
"yême": "yeme", "yêne": "yene", "yête": "yete", "yêue": "yeue", "yênge": "yenge",
"acs": "ác", "acj": "ạc",
"achs": "ách", "achj": "ạch",
"ais": "ái", "aif": "ài", "air": "ải", "aix": "ãi", "aij": "ại",
"ams": "ám", "amf": "àm", "amr": "ảm", "amx": "ãm", "amj": "ạm",
"ans": "án" , "anf": "àn", "anr": "ản", "anx": "ãn", "anj": "ạn",
"aos": "áo", "aof": "ào", "aor": "ảo", "aox": "ão", "aoj": "ạo",
"aps":"áp", "apj": "ạp",
"ats":"át", "atj": "ạt",
"aus":"áu", "auf": "àu", "aur": "ảu", "aux": "ãu", "auj": "ạu",
"ays": "áy", "ayf": "ày", "ayr": "ảy", "ayx": "ãy", "ayj": "ạy",
"angs": "áng", "angf": "àng", "angr": "ảng", "angx": "ãng", "angj": "ạng",
"anhs": "ánh", "anhf": "ành", "anhr": "ảnh", "anhx": "ãnh", "anhj": "ạnh",
"ács": "acs", "ạcj": "acj",
"áchs": "achs", "ạchj": "achj",
"áis": "ais", "àif": "aif", "ảir": "air", "ãix": "aix", "ạij": "aij",
"áms": "ams", "àmf": "amf", "ảmr": "amr", "ãmx": "amx", "ạmj": "amj",
"áns": "ans", "ànf": "anf", "ảnr": "anr", "ãnx": "anx", "ạnj": "anj",
"áos": "aos", "àof": "aof", "ảor": "aor", "ãox": "aox", "ạoj":"aoj",
"áps": "aps", "ạpj": "apj",
"áts": "ats", "ạtj": "atj",
"áus": "aus", "àuf": "auf", "ảur": "aur", "ãux": "aux", "ạuj": "auj",
"áys": "ays", "àyf": "ayf", "ảyr": "ayr", "ãyx": "ayx", "ạyj": "ayj",
"ángs": "angs", "àngf": "angf", "ảngr": "angr", "ãngx": "angx", "ạngj": "angj",
"ánhs": "anhs", "ànhf": "anhf", "ảnhr": "anhr", "ãnhx": "anhx", "ạnhj": "anhj",
"ecs": "éc", "ecj": "ẹc",
"ems": "ém", "emf": "èm", "emr": "ẻm", "emx": "ẽm", "emj": "ẹm",
"ens": "én", "enf": "èn", "enr": "ẻn", "enx": "ẽn", "enj": "ẹn",
"eps": "ép", "epj": "ẹp",
"ets": "ét", "etj": "ẹt",
"engs": "éng", "engf": "èng", "engr": "ẻng", "engx": "ẽng", "engj": "ẹng",
"enhs": "énh", "enhf": "ènh", "enhr": "ẻnh", "enhx": "ẽnh", "enhj": "ẹnh",
"écs": "ecs", "ẹcj": "ecj",
"éms": "ems", "èmf": "emf", "ẻmr": "emr", "ẽmx": "emx", "ẹmj": "emj",
"éns": "ens", "ènf": "enf","ẻnr": "enr", "ẽnx": "enx", "ẹnj": "enj",
"éps": "eps", "ẹpj": "epj",
"éts": "ets", "ẹtj": "etj",
"éngs": "engs", "èngf": "engf", "ẻngr": "engr", "ẽngx":"engx", "ẹngj": "engj",
"énhs": "enhs", "ènhf": "enhf", "ẻnhr": "enhr", "ẽnh": "enhx", "ẹnh": "enhj",
"ias": "ía", "iaf": "ìa", "iar": "ỉa", "iax": "ĩa", "iaj": "ịa",
"ims": "ím", "imf": "ìm", "imr": "ỉm", "imx": "ĩm", "imj": "ịm",
"ins": "ín", "inf": "ìn", "inr": "ỉn", "inx": "ĩn", "inj": "ịn",
"ips": "íp", "ipj": "ịp",
"its": "ít", "itj": "ịt",
"ius": "íu", "iuf": "ìu", "iur": "ỉu", "iux": "ĩu", "iuj": "ịu",
"ichs": "ích", "ichj": "ịch",
"inhs": "ính", "inhf": "ình", "inhr": "ỉnh", "inhx": "ĩnh", "inhj": "ịnh",
"ías": "ias", "ìaf": "iaf", "ỉar": "iar", "ĩax": "iax", "ịaj": "iaj",
"íms": "ims", "ìmf": "imf", "ỉmr": "imr", "ĩmx": "imx", "ịmj": "imj",
"íns": "ins", "ìnf": "inf", "ỉnr": "inr", "ĩnx": "inx", "ịnj": "inj",
"íps": "ips", "ịpj": "ipj",
"íts": "its", "ịtj": "itj",
"íus": "ius", "ìuf": "iuf", "ỉur": "iur", "ĩux": "iux", "ịuj": "iuj",
"íchs": "ichs", "ịchj": "ichj",
"ínhs": "inhs", "ìnhf": "inhf", "ỉnhr": "inhr", "ĩnhx": "inhx", "ịnhj": "inhj",
"oas": "óa", "oaf": "òa", "oar": "ỏa", "oax": "õa", "oaj": "ọa",
"ois": "ói", "oif": "òi", "oir": "ỏi", "oix": "õi", "oij": "ọi",
"oms": "óm", "omf": "òm", "omr": "ỏm", "omx": "õm", "omj": "ọm",
"ons": "ón", "onf": "òn", "onr": "ỏn", "onx": "õn", "onj": "ọn",
"ops": "óp", "opj": "ọp",
"ots": "ót", "otj": "ọt",
"ongs": "óng", "ongf": "òng", "ongr": "ỏng", "ongx": "õng", "ongj": "ọng",
"óas": "oas", "òaf": "oaf", "ỏar": "oar", "õax": "oax", "ọaj": "oaj",
"óis": "ois", "òif": "oif", "ỏir": "oir", "õix": "oix", "ọij": "oij",
"óms": "oms", "òmf": "omf", "ỏmr": "omr", "õmx": "omx", "ọmj": "omj",
"óns": "ons", "ònf": "onf", "ỏnr": "onr", "õnx": "onx", "ọnj": "onj",
"óps": "ops", "ọpj": "opj",
"óts": "ots", "ọtj": "otj",
"óngs": "ongs", "òngf": "ongf", "ỏngr": "ongr", "õngx": "ongx", "ọngj": "ongj",
"uas": "úa", "uaf": "ùa", "uar": "ủa", "uax": "ũa", "uaj": "ụa",
"uis": "úi", "uif": "ùi", "uir": "ủi", "uix": "ũi", "uij": "ụi",
"ums": "úm", "umf": "ùm", "umr": "ủm", "umx": "ũm", "umj": "ụm",
"uns": "ún", "unf": "ùn", "unr": "ủn", "unx": "ũn", "unj": "ụn",
"ups": "úp", "upj": "ụp",
"uts": "út", "utj": "ụt",
"uus": "úu", "uuf": "ùu", "uur": "ủu", "uux": "ũu", "uuj": "ụu",
"uys": "úy", "uyf": "ùy", "uyr": "ủy", "uyx": "ũy", "uyj": "ụy",
"ungs": "úng", "ungf": "ùng", "ungr": "ủng", "ungx": "ũng", "ungj": "ụng",
"úas": "uas", "ùaf": "uaf", "ủar": "uar", "ũax": "uax", "ụaj": "uaj",
"úis": "uis", "ùif": "uif", "ủir": "uir", "ũix": "uix", "ụij": "uij",
"úms": "ums", "ùmf": "umf", "ủmr": "umr", "ũmx": "umx", "ụmj": "umj",
"úns": "uns", "ùnf": "unf", "ủnr": "unr", "ũnx": "unx", "ụnj": "unj",
"úps": "ups", "ụpj": "upj",
"úts": "uts", "ụtj": "utj",
"úus": "uus", "ùuf": "uuf", "ủur": "uur", "ũux": "uux", "ụuj": "uuj",
"úu ": "uus", "ùu ": "uuf", "ủu ": "uur", "ũu ": "uux", "ụu ": "uuj",
"úys": "uys", "ùyf": "uyf", "ủyr": "uyr", "ũyx": "uyx", "ụyj": "uyj",
"úngs": "ungs", "ùngf": "ungf", "ủngr": "ungr", "ũngx": "ungx", "ụngj": "ungj",
"ăcs": "ắc", "ăcj": "ặc",
"ácw": "ắc", "ạcw": "ặc",
"ăms": "ắm", "ămf": "ằm", "ămr": "ẳm", "ămx": "ẵm", "ămj": "ặm",
"ámw": "ắm", "àmw": "ằm", "ảmw": "ẳm", "ãmw": "ẵm", "ạmw": "ặm",
"ăns": "ắn", "ănf": "ằn", "ănr": "ẳn", "ănx": "ẵn", "ănj": "ặn",
"ánw": "ắn", "ànw": "ằn", "ảnw": "ẳn", "ãnw": "ẵn", "ạnw": "ặn",
"ăps": "ắp", "ăpj": "ặp",
"ápw": "ắp", "ạpw": "ặp",
"ăts": "ắt", "ătj": "ặt",
"átw": "ắt", "ạtw": "ặt",
"ăngs": "ắng", "ăngf": "ằng", "ăngr": "ẳng", "ăngx": "ẵng", "ăngj": "ặng",
"ángw": "ắng", "àngw": "ằng", "ảngw": "ẳng", "ãngw":"ẵng", "ạngw": "ặng",
"ắcw": "ácw", "ắcs": "ăcs", "ăcsw": "acsw", "ácws": "acws",
"ặcw": "ạcw", "ặcj": "ăcj", "ăcjw": "acjw", "ạcwj": "acwj",
"ắms": "ăms", "ắmw": "ámw", "ămsw": "amsw", "ámws": "amws",
"ằmf": "ămf", "ằmw": "àmw", "ămfw": "amfw", "àmwf": "amwf",
"ẳmr": "ămr", "ẳmw": "ảmw", "ămrw": "amrw", "ảmwr": "amwr",
"ẵmx": "ămx", "ẵmw": "ãmw", "ămxw": "amxw", "ãmwx": "amwx",
"ặmj": "ămj", "ặmw": "ạmw", "ămjw": "amjw", "ạmwj": "amwj",
"ắns": "ăns", "ắnw": "ánw", "ănsw": "answ", "ánws": "anws",
"ằnf": "ănf", "ằnw": "ànw", "ănfw": "anfw", "ànwf": "anwf",
"ẳnr": "ănr", "ẳnw": "ảnw", "ănrw": "anrw", "ảnwr": "anwr",
"ẵnx": "ănx", "ẵnw": "ãnw", "ănxw": "anxw", "ãnwx": "anwx",
"ặnj": "ănj", "ặnw": "ạnw", "ănjw": "anjw", "ạnwj": "anwj",
"ắps": "ăps", "ắpw": "ápw", "ăpsw": "apsw", "ápws": "apws",
"ặpj": "ăpj", "ặpw": "ạpw", "ăpjw": "apjw", "ạpwj": "apwj",
"ắts": "ăts", "ắtw": "átw", "ătsw": "atws", "átws": "atws",
"ặtj": "ătj", "ặtw": "ạtw", "ătjw": "atjw", "ạtwj": "atwj",
"ắngs": "ăngs", "ắngw": "ángw", "ăngsw": "angsw", "ángws": "angws",
"ằngf": "ăngf", "ằngw": "àngw", "ăngfw": "angfw", "àngwf": "angwf",
"ẳngr": "ăngr", "ẳngw": "ảngw", "ăngrw": "angrw", "ảngwr": "angwr",
"ẵngx": "ăngx", "ẵngw": "ãngw", "ăngxw": "angxw", "ãngwx": "angwx",
"ặngj": "ăngj", "ặngw": "ạngw", "ăngjw": "angjw", "ạngwj": "angwj",
"âcs": "ấc", "âcj": "ậc",
"áca": "ấc", "ạca": "ậc",
"âms": "ấm", "âmf": "ầm", "âmr": "ẩm", "âmx": "ẫm", "âmj": "ậm",
"áma": "ấm", "àma": "ầm", "ảma": "ẩm", "ãma": "ẫm", "ạma": "ậm",
"âns": "ấn", "ânf": "ần", "ânr": "ẩn", "ânx": "ẫn", "ânj": "ận",
"ána": "ấn", "àna": "ần", "ảna": "ẩn", "ãna": "ẫn", "ạna": "ận",
"âps": "ấp", "âpj": "ập",
"ápa": "ấp", "ạpa": "ập",
"âts": "ất", "âtj": "ật",
"áta": "ất", "ạta": "ật",
"âus": "ấu", "âuf": "ầu", "âur": "ẩu", "âux": "ẫu", "âuj": "ậu",
"áua": "ấu", "àua": "ầu", "ảua": "ẩu", "ãua": "ẫu", "ạua": "ậu",
"âys": "ấy", "âyf": "ầy", "âyr": "ẩy", "âyx": "ẫy", "âyj": "ậy",
"áya": "ấy", "àya": "ầy", "ảya": "ẩy", "ãya": "ẫy", "ạya": "ậy",
"ângs": "ấng", "ângf": "ầng", "ângr": "ẩng", "ângx": "ẫng", "ângj": "ậng",
"ánga": "ấng", "ànga": "ầng", "ảnga": "ẩng", "ãnga": "ẫng", "ạnga": "ậng",
"ấcs": "âcs", "ấca": "áca", "âcsa": "acsa", "ácas": "acas",
"ậcj": "âcj", "ậca": "ạca", "âcja": "acja", "ạcaj": "acaj",
"ấms": "âms", "ấma": "áma", "âmsa": "amsa", "ámas": "amas",
"ầmf": "âmf", "ầma": "àma", "âmfa": "amfa", "àmaf": "amaf",
"ẩmr": "âmr", "ẩma": "ảma", "âmra": "amra", "ảmar": "amar",
"ẫmx": "âmx", "ẫma": "ãma", "âmxa": "amxa", "ãmax": "amax",
"ậmj": "âmj", "ậma": "ạma", "âmja": "amja", "ạmaj": "amaj",
"ấns": "âns", "ấna": "ána", "ânsa": "ansa", "ánas": "anas",
"ầnf": "ânf", "ầna": "àna", "ânfa": "anfa", "ànaf": "anaf",
"ẩnr": "ânr", "ẩna": "ảna", "ânra": "anra", "ảnar": "anar",
"ẫnx": "ânx", "ẫna": "ãna", "ânxa": "anxa", "ãnax": "anax",
"ậnj": "ânj", "ậna": "ạna", "ânja": "anja", "ạnaj": "anaj",
"ấps": "âps", "ấpa": "ápa", "âpsa": "apsa", "ápas": "apas",
"ậpj": "âpj", "ậpa": "ạpa", "âpja": "apja", "ạpaj": "apaj",
"ấts": "âts", "ấta": "áta", "âtsa": "atas", "átas": "atas",
"ậtj": "âtj", "ậta": "ạta", "âtja": "atja", "ạtaj": "ataj",
"ấus": "âus", "ấua": "áua", "âusa": "ausa", "áuas": "auas",
"ầuf": "âuf", "ầua": "àua", "âufa": "aufa", "àuaf": "auaf",
"ẩur": "âur", "ẩua": "ảua", "âura": "aura", "ảuar": "auar",
"ẫux": "âux", "ẫua": "ãua", "âuxa": "auxa", "ãuax": "auax",
"ậuj": "âuj", "ậua": "ạua", "âuja": "auja", "ạuaj": "auaj",
"ấys": "âys", "ấya": "áya", "âysa": "aysa", "áyas": "ayas",
"ầyf": "âyf", "ầya": "àya", "âyfa": "ayfa", "àyaf": "ayaf",
"ẩyr": "âyr", "ẩya": "ảya", "âyra": "ayra", "ảyar": "ayar",
"ẫyx": "âyx", "ẫya": "ãya", "âyxa": "ayxa", "ãyax": "ayax",
"ậyj": "âyj", "ậya": "ạya", "âyja": "ayja", "ạyaj": "ayaj",
"ấngs": "ângs", "ấnga": "ánga", "ângsa": "angsa", "ángas": "angas",
"ầngf": "ângf", "ầnga": "ànga", "ângfa": "angfa", "àngaf": "angaf",
"ẩngr": "ângr", "ẩnga": "ảnga", "ângra": "angra", "ảngar": "angar",
"ẫngx": "ângx", "ẫnga": "ãnga", "ângxa": "angxa", "ãngax": "angax",
"ậngj": "ângj", "ậnga": "ạnga", "ângja": "angja", "ạngaj": "angaj",
"êcs": "ếc", "êcj": "ệc",
"éce": "ếc", "ẹce": "ệc",
"êms": "ếm", "êmf": "ềm", "êmr": "ểm", "êmx": "ễm", "êmj": "ệm",
"éme": "ếm", "ème": "ềm", "ẻme": "ểm", "ẽme": "ễm", "ẹme": "ệm",
"êns": "ến", "ênf": "ền", "ênr": "ển", "ênx": "ễn", "ênj": "ện",
"éne": "ến", "ène": "ền", "ẻne": "ển", "ẽne": "ễn", "ẹne": "ện",
"êps": "ếp", "êpj": "ệp",
"épe": "ếp", "ẹpe": "ệp",
"êts": "ết", "êtj": "ệt",
"éte": "ết", "ẹte": "ệt",
"êchs": "ếch", "êchj": "ệch",
"echs": "éch", "echj": "ẹch",
"éche": "ếch", "ẹche": "ệch",
"ênhs": "ếnh", "ênhf": "ềnh", "ênhr": "ểnh", "ênhx": "ễnh", "ênhj": "ệnh",
"énhe": "ếnh", "ènhe": "ềnh", "ẻnhe": "ểnh", "ẽnhe": "ễnh", "ẹnhe": "ệnh",
"ếms": "êms", "ếme": "éme", "êmse": "emse", "émes": "emes",
"ềmf": "êmf", "ềme": "ème", "êmfe": "emfe", "èmef": "emef",
"ểmr": "êmr", "ểme": "ẻme", "êmre": "emre", "ẻmer": "emer",
"ễmx": "êmx", "ễme": "ẽme", "êmxe": "emxe", "ẽmex": "emex",
"ệmj": "êmj", "ệme": "ẹme", "êmje": "emje", "ẹmej": "emej",
"ếns": "êns", "ếne": "éne", "ênse": "ense", "énes": "enes",
"ềnf": "ênf", "ềne": "ène", "ênfe": "enfe", "ènef": "enef",
"ểnr": "ênr", "ểne": "ẻne", "ênre": "enre", "ẻner": "ener",
"ễnx": "ênx", "ễne": "ẽne", "ênxe": "enxe", "ẽnex": "enex",
"ệnj": "ênj", "ệne": "ẹne", "ênje": "enje", "ẹnej": "enej",
"ếps": "êps", "ếpe": "épe", "êpse": "epse", "épes": "epes",
"ệpj": "êpj", "ệpe": "ẹpe", "êpje": "epje", "ẹpej": "epej",
"ếts": "êts", "ếte": "éte", "êtse": "etes", "étes": "etes",
"ệtj": "êtj", "ệte": "ẹte", "êtje": "etje", "ẹtej": "etej",
"ếchs": "êchs", "ếche": "éche", "êchse": "eches", "éches": "eches",
"ệchj": "êchj", "ệche": "ẹche", "êchje": "echje", "ẹchej": "echej",
"ếnhs": "ênhs", "ếnhe": "énhe", "ênhse": "enhse", "énhes": "enhes",
"ềnhf": "ênhf", "ềnhe": "ènhe", "ênhfe": "enhfe", "ènhef": "enhef",
"ểnhr": "ênhr", "ểnhe": "ẻnhe", "ênhre": "enhre", "ẻnher": "enher",
"ễnhx": "ênhx", "ễnhe": "ẽnhe", "ênhxe": "enhxe", "ẽnhex": "enhex",
"ệnhj": "ênhj", "ệnhe": "ẹnhe", "ênhje": "enhje", "ẹnhej": "enhej",
"ôcs": "ốc", "ôcj": "ộc",
"óco": "ốc", "ọco": "ộc",
"ôis": "ối", "ôif": "ồi", "ôir": "ổi", "ôix": "ỗi", "ôij": "ội",
"óio": "ối", "òio": "ồi", "ỏio": "ổi", "õio": "ỗi", "ọio": "ội",
"ôms": "ốm", "ômf": "ồm", "ômr": "ổm", "ômx": "ỗm", "ômj": "ộm",
"ómo": "ốm", "òmo": "ồm", "ỏmo": "ổm", "õmo": "ỗm", "ọmo": "ộm",
"ôns": "ốn", "ônf": "ồn", "ônr": "ổn", "ônx": "ỗn", "ônj": "ộn",
"óno": "ốn", "òno": "ồn", "ỏno": "ổn", "õno": "ỗn", "ọno": "ộn",
"ôps": "ốp", "ôpj": "ộp",
"ópo": "ốp", "ọpo": "ộp",
"ôts": "ốt", "ôtj": "ột",
"óto": "ốt", "ọto": "ột",
"ôngs": "ống", "ôngf": "ồng", "ôngr": "ổng", "ôngx": "ỗng", "ôngj": "ộng",
"óngo": "ống", "òngo": "ồng", "ỏngo": "ổng", "õngo": "ỗng", "ọngo": "ộng",
"ốcs": "ôcs", "ốco": "óco", "ôcso": "ocso", "ócos": "ocos",
"ộcj": "ôcj", "ộco": "ọco", "ôcjo": "ocjo", "ọcoj": "ocoj",
"ốis": "ôis", "ốio": "óio", "ôiso": "oiso", "óios": "oios",
"ồif": "ôif", "ồio": "òio", "ôifo": "oifo", "òiof": "oiof",
"ổir": "ôir", "ổio": "ỏio", "ôiro": "oiro", "ỏior": "oior",
"ỗix": "ôix", "ỗio": "õio", "ôixo": "oixo", "õiox": "oiox",
"ộij": "ôij", "ộio": "ọio", "ôijo": "oijo", "ọioj": "oioj",
"ốms": "ôms", "ốmo": "ómo", "ômso": "omso", "ómos": "omos",
"ồmf": "ômf", "ồmo": "òmo", "ômfo": "omfo", "òmof": "omof",
"ổmr": "ômr", "ổmo": "ỏmo", "ômro": "omro", "ỏmor": "omor",
"ỗmx": "ômx", "ỗmo": "õmo", "ômxo": "omxo", "õmox": "omox",
"ộmj": "ômj", "ộmo": "ọmo", "ômjo": "omjo", "ọmoj": "omoj",
"ốns": "ôns", "ốno": "óno", "ônso": "onso", "ónos": "onos",
"ồnf": "ônf", "ồno": "òno", "ônfo": "onfo", "ònof": "onof",
"ổnr": "ônr", "ổno": "ỏno", "ônro": "onro", "ỏnor": "onor",
"ỗnx": "ônx", "ỗno": "õno", "ônxo": "onxo", "õnox": "onox",
"ộnj": "ônj", "ộno": "ọno", "ônjo": "onjo", "ọnoj": "onoj",
"ốps": "ôps", "ốpo": "ópo", "ôpso": "opso", "ópos": "opos",
"ộpj": "ôpj", "ộpo": "ọpo", "ôpjo": "opjo", "ọpoj": "opoj",
"ốts": "ôts", "ốto": "óto", "ôtso": "otso", "ótos": "otos",
"ộtj": "ôtj", "ộto": "ọto", "ôtjo": "otjo", "ọtoj": "otoj",
"ốngs": "ôngs", "ốngo": "óngo", "ôngso": "ongso", "óngos": "ongos",
"ồngf": "ôngf", "ồngo": "òngo", "ôngfo": "ongfo", "òngof": "ongof",
"ổngr": "ôngr", "ổngo": "ỏngo", "ôngro": "ongro", "ỏngor": "ongor",
"ỗngx": "ôngx", "ỗngo": "õngo", "ôngxo": "ongxo", "õngox": "ongox",
"ộngj": "ôngj", "ộngo": "ọngo", "ôngjo": "ongjo", "ọngoj": "ongoj",
"ơis": "ới", "ơif": "ời", "ơir": "ởi", "ơix": "ỡi", "ơij": "ợi",
"óiw": "ới", "òiw": "ời", "ỏiw": "ởi", "õiw": "ỡi", "ọiw": "ợi",
"ơms": "ớm", "ơmf": "ờm", "ơmr": "ởm", "ơmx": "ỡm", "ơmj": "ợm",
"ómw": "ớm", "òmw": "ờm", "ỏmw": "ởm", "õmw": "ỡm", "ọmw": "ợm",
"ơns": "ớn", "ơnf": "ờn", "ơnr": "ởn", "ơnx": "ỡn", "ơnj": "ợn",
"ónw": "ớn", "ònw": "ờn", "ỏnw": "ởn", "õnw": "ỡn", "ọnw": "ợn",
"ơps": "ớp", "ơpj": "ợp",
"ópw": "ớp", "ọpw": "ợp",
"ơts": "ớt", "ơtj": "ợt",
"ótw": "ớt", "ọtw": "ợt",
"ớis": "ơis", "ớiw": "óiw", "ơisw": "oisw", "óiws": "oiws",
"ờif": "ơif", "ờiw": "òiw", "ơifw": "oifw", "òiwf": "oiwf",
"ởir": "ơir", "ởiw": "ỏiw", "ơirw": "oirw", "ỏiwr": "oiwr",
"ỡix": "ơix", "ỡiw": "õiw", "ơixw": "oixw", "õiwx": "oiwx",
"ợij": "ơij", "ợiw": "ọiw", "ơijw": "oijw", "ọiwj": "oiwj",
"ớms": "ơms", "ớmw": "ómw", "ơmsw": "omsw", "ómws": "omws",
"ờmf": "ơmf", "ờmw": "òmw", "ơmfw": "omfw", "òmwf": "omwf",
"ởmr": "ơmr", "ởmw": "ỏmw", "ơmrw": "omrw", "ỏmwr": "omwr",
"ỡmx": "ơmx", "ỡmw": "õmw", "ơmxw": "omxw", "õmwx": "omwx",
"ợmj": "ơmj", "ợmw": "ọmw", "ơmjw": "omjw", "ọmwj": "omwj",
"ớns": "ơns", "ớnw": "ónw", "ơnsw": "onsw", "ónws": "onws",
"ờnf": "ơnf", "ờnw": "ònw", "ơnfw": "onfw", "ònwf": "onwf",
"ởnr": "ơnr", "ởnw": "ỏnw", "ơnrw": "onrw", "ỏnwr": "onwr",
"ỡnx": "ơnx", "ỡnw": "õnw", "ơnxw": "onxw", "õnwx": "onwx",
"ợnj": "ơnj", "ợnw": "ọnw", "ơnjw": "onjw", "ọnwj": "onwj",
"ớps": "ơps", "ớpw": "ópw", "ơpsw": "opsw", "ópws": "opws",
"ợpj": "ơpj", "ợpw": "ọpw", "ơpjw": "opjw", "ọpwj": "opwj",
"ớts": "ơts", "ớtw": "ótw", "ơtsw": "otsw", "ótws": "otws",
"ợtj": "ơtj", "ợtw": "ọtw", "ơtjw": "otjw", "ọtwj": "otwj",
"ưas": "ứa", "ưaf": "ừa", "ưar": "ửa", "ưax": "ữa", "ưaj": "ựa",
"úaw": "ứa", "ùaw": "ừa", "ủaw": "ửa", "ũaw": "ữa", "ụaw": "ựa",
"ưis": "ứi", "ưif": "ừi", "ưir": "ửi", "ưix": "ữi", "ưij": "ựi",
"úiw": "ứi", "ùiw": "ừi", "ủiw": "ửi", "ũiw": "ữi", "ụiw": "ựi",
"ưms": "ứm", "ưmf": "ừm", "ưmr": "ửm", "ưmx": "ữm", "ưmj": "ựm",
"úmw": "ứm", "ùmw": "ừm", "ủmw": "ửm", "ũmw": "ữm", "ụmw": "ựm",
"ưns": "ứn", "ưnf": "ừn", "ưnr": "ửn", "ưnx": "ữn", "ưnj": "ựn",
"únw": "ứn", "ùnw": "ừn", "ủnw": "ửn", "ũnw": "ữn", "ụnw": "ựn",
"ưts": "ứt", "ưtj": "ựt",
"útw": "ứt", "ụtw": "ựt",
"ưus": "ứu", "ưuf": "ừu", "ưur": "ửu", "ưux": "ữu", "ưuj": "ựu",
"úuw": "ứu", "ùuw": "ừu", "ủuw": "ửu", "ũuw": "ữu", "ụuw": "ựu",
"ưngs": "ứng", "ưngf": "ừng", "ưngr": "ửng", "ưngx": "ững", "ưngj": "ựng",
"úngw": "ứng", "ùngw": "ừng", "ủngw": "ửng", "ũngw": "ững", "ụngw": "ựng",
"ứas": "ưas", "ứaw": "úaw", "ưasw": "uasw", "úaws": "uaws",
"ừaf": "ưaf", "ừaw": "ùaw", "ưafw": "uafw", "ùawf": "uawf",
"ửar": "ưar", "ửaw": "ủaw", "ưarw": "uarw", "ủawr": "uawr",
"ữax": "ưax", "ữaw": "ũaw", "ưaxw": "uaxw", "ũawx": "uawx",
"ựaj": "ưaj", "ựaw": "ụaw", "ưajw": "uajw", "ụawj": "uawj",
"ứis": "ưis", "ứiw": "úiw", "ưisw": "uisw", "úiws": "uiws",
"ừif": "ưif", "ừiw": "ùiw", "ưifw": "uifw", "ùiwf": "uiwf",
"ửir": "ưir", "ửiw": "ủiw", "ưirw": "uirw", "ủiwr": "uiwr",
"ữix": "ưix", "ữiw": "ũiw", "ưixw": "uixw", "ũiwx": "uiwx",
"ựij": "ưij", "ựiw": "ụiw", "ưijw": "uijw", "ụiwj": "uiwj",
"ứms": "ưms", "ứmw": "úmw", "ưmsw": "umsw", "úmws": "umws",
"ừmf": "ưmf", "ừmw": "ùmw", "ưmfw": "umfw", "ùmwf": "umwf",
"ửmr": "ưmr", "ửmw": "ủmw", "ưmrw": "umrw", "ủmwr": "umwr",
"ữmx": "ưmx", "ữmw": "ũmw", "ưmxw": "umxw", "ũmwx": "umwx",
"ựmj": "ưmj", "ựmw": "ụmw", "ưmjw": "umjw", "ụmwj": "umwj",
"ứns": "ưns", "ứnw": "únw", "ưnsw": "unsw", "únws": "unws",
"ừnf": "ưnf", "ừnw": "ùnw", "ưnfw": "unfw", "ùnwf": "unwf",
"ửnr": "ưnr", "ửnw": "ủnw", "ưnrw": "unrw", "ủnwr": "unwr",
"ữnx": "ưnx", "ữnw": "ũnw", "ưnxw": "unxw", "ũnwx": "unwx",
"ựnj": "ưnj", "ựnw": "ụnw", "ưnjw": "unjw", "ụnwj": "unwj",
"ứts": "ưts", "ứtw": "útw", "ưtsw": "utsw", "útws": "utws",
"ựtj": "ưtj", "ựtw": "ụtw", "ưtjw": "utjw", "ụtwj": "utwj",
"ứus": "ưus", "ứuw": "úuw", "ưusw": "uusw", "úuws": "uuws",
"ừuf": "ưuf", "ừuw": "ùuw", "ưufw": "uufw", "ùuwf": "uuwf",
"ửur": "ưur", "ửuw": "ủuw", "ưurw": "uurw", "ủuwr": "uuwr",
"ữux": "ưux", "ữuw": "ũuw", "ưuxw": "uuxw", "ũuwx": "uuwx",
"ựuj": "ưuj", "ựuw": "ụuw", "ưujw": "uujw", "ụuwj": "uuwj",
"ứngs": "ưngs", "ứngw": "úngw", "ưngsw": "ungsw", "úngws": "ungws",
"ừngf": "ưngf", "ừngw": "ùngw", "ưngfw": "ungfw", "ùngwf": "ungwf",
"ửngr": "ưngr", "ửngw": "ủngw", "ưngrw": "ungrw", "ủngwr": "ungwr",
"ữngx": "ưngx", "ữngw": "ũngw", "ưngxw": "ungxw", "ũngwx": "ungwx",
"ựngj": "ưngj", "ựngw": "ụngw", "ưngjw": "ungjw", "ụngwj": "ungwj",
"iems": "iém", "iemf": "ièm", "iemr": "iẻm", "iemx": "iẽm", "iemj": "iẹm",
"iéme": "iếm", "ième": "iềm", "iẻme": "iểm", "iẽme": "iễm", "iẹme": "iệm",
"iêms": "iếm", "iêmf": "iềm", "iêmr": "iểm", "iêmx": "iễm", "iêmj": "iệm",
"iens": "ién", "ienf": "ièn", "ienr": "iẻn", "ienx": "iẽn", "ienj": "iẹn",
"iéne": "iến", "iène": "iền", "iẻne": "iển", "iẽne": "iễn", "iẹne": "iện",
"iêns": "iến", "iênf": "iền", "iênr": "iển", "iênx": "iễn", "iênj": "iện",
"ieps": "iép", "iepj": "iẹp",
"iépe": "iếp", "iẹpe": "iệp",
"iêps": "iếp", "iêpj": "iệp",
"iets": "iét", "ietj": "iẹt",
"iéte": "iết", "iẹte": "iệt",
"iêts": "iết", "iêtj": "iệt",
"ieus": "iéu", "ieuf": "ièu", "ieur": "iẻu", "ieux": "iẽu", "ieuj": "iẹu",
"iéue": "iếu", "ièue": "iều", "iẻue": "iểu", "iẽue": "iễu", "iẹue": "iệu",
"iêus": "iếu", "iêuf": "iều", "iêur": "iểu", "iêux": "iễu", "iêuj": "iệu",
"iengs": "iéng", "iengf": "ièng", "iengr": "iẻng", "iengx": "iẽng", "iengj": "iẹng",
"iénge": "iếng", "iènge": "iềng", "iẻnge": "iểng", "iẽnge": "iễng", "iẹnge": "iệng",
"iêngs": "iếng", "iêngf": "iềng", "iêngr": "iểng", "iêngx": "iễng", "iêngj": "iệng",
"iếms": "iêms", "iếme": "iéme", "iêmse": "iemse", "iémes": "iemes",
"iềmf": "iêmf", "iềme": "ième", "iêmfe": "iemfe", "ièmef": "iemef",
"iểmr": "iêmr", "iểme": "iẻme", "iêmre": "iemre", "iẻmer": "iemer",
"iễmx": "iêmx", "iễme": "iẽme", "iêmxe": "iemxe", "iẽmex": "iemex",
"iệmj": "iêmj", "iệme": "iẹme", "iêmje": "iemje", "iẹmej": "iemej",
"iếns": "iêns", "iếne": "iéne", "iênse": "iense", "iénes": "ienes",
"iềnf": "iênf", "iềne": "iène", "iênfe": "ienfe", "iènef": "ienef",
"iểnr": "iênr", "iểne": "iẻne", "iênre": "ienre", "iẻner": "iener",
"iễnx": "iênx", "iễne": "iẽne", "iênxe": "ienxe", "iẽnex": "ienex",
"iệnj": "iênj", "iệne": "iẹne", "iênje": "ienje", "iẹnej": "ienej",
"iếps": "iêps", "iếpe": "iépe", "iêpse": "iepse", "iépes": "iepes",
"iệpj": "iêpj", "iệpe": "iẹpe", "iêpje": "iepje", "iẹpej": "iepej",
"iếts": "iêts", "iếte": "iéte", "iêtse": "ietse", "iétes": "ietes",
"iệtj": "iêtj", "iệte": "iẹte", "iêtje": "ietje", "iẹtej": "ietej",
"iếus": "iêus", "iếue": "iéue", "iêuse": "ieuse", "iéues": "ieues",
"iềuf": "iêuf", "iềue": "ièue", "iêufe": "ieufe", "ièuef": "ieuef",
"iểur": "iêur", "iểue": "iẻue", "iêure": "ieure", "iẻuer": "ieuer",
"iễux": "iêux", "iễue": "iẽue", "iêuxe": "ieuxe", "iẽuex": "ieuex",
"iệuj": "iêuj", "iệue": "iẹue", "iêuje": "ieuje", "iẹuej": "ieuej",
"iếngs": "iêngs", "iếnge": "iénge", "iêngse": "iengse", "iénges": "ienges",
"iềngf": "iêngf", "iềnge": "iènge", "iêngfe": "iengfe", "ièngef": "iengef",
"iểngr": "iêngr", "iểnge": "iẻnge", "iêngre": "iengre", "iẻnger": "ienger",
"iễngx": "iêngx", "iễnge": "iẽnge", "iêngxe": "iengxe", "iẽngex": "iengex",
"iệngj": "iêngj", "iệnge": "iẹnge", "iêngje": "iengje", "iẹngej": "iengej",
"uyens": "uyén", "uyenf": "uyèn", "uyenr": "uyẻn", "uyenx": "uyẽn", "uyenj": "uyẹn",
"uyéne": "uyến", "uyène": "uyền", "uyẻne": "uyển", "uyẽne": "uyễn", "uyẹne": "uyện",
"uyêns": "uyến", "uyênf": "uyền", "uyênr": "uyển", "uyênx": "uyễn", "uyênj": "uyện",
"uyets": "uyét", "uyetf": "uyèt", "uyetr": "uyẻt", "uyetx": "uyẽt", "uyetj": "uyẹt",
"uyéte": "uyết", "uyète": "uyềt", "uyẻte": "uyểt", "uyẽte": "uyễt", "uyẹte": "uyệt",
"uyêts": "uyết", "uyêtf": "uyềt", "uyêtr": "uyểt", "uyêtx": "uyễt", "uyêtj": "uyệt",
"uyếns": "uyêns", "uyếne": "uyéne", "uyênse": "uyense", "uyénes": "uyenes",
"uyềnf": "uyênf", "uyềne": "uyène", "uyênfe": "uyenfe", "uyènef": "uyenef",
"uyểnr": "uyênr", "uyểne": "uyẻne", "uyênre": "uyenre", "uyẻner": "uyener",
"uyễnx": "uyênx", "uyễne": "uyẽne", "uyênxe": "uyenxe", "uyẽnex": "uyenex",
"uyệnj": "uyênj", "uyệne": "uyẹne", "uyênje": "uyenje", "uyẹnej": "uyenej",
"uyếts": "uyêts", "uyếte": "uyéte", "uyêtse": "uyetse", "uyétes": "uyetes",
"uyềtf": "uyêtf", "uyềte": "uyète", "uyêtfe": "uyetfe", "uyètef": "uyetef",
"uyểtr": "uyêtr", "uyểte": "uyẻte", "uyêtre": "uyetre", "uyẻter": "uyeter",
"uyễtx": "uyêtx", "uyễte": "uyẽte", "uyêtxe": "uyetxe", "uyẽtex": "uyetex",
"uyệtj": "uyêtj", "uyệte": "uyẹte", "uyêtje": "uyetje", "uyẹtej": "uyetej",
"uyts": "uýt", "uytj": "uỵt",
"uynhs": "uýnh", "uynhf": "uỳnh", "uynhr": "uỷnh", "uynhx": "uỹnh", "uynhj": "uỵnh",
"uýts": "uyts", "uỵtj": "uytj",
"uýnhs": "uynhs", "uỳnhf": "uynhf", "uỷnhr": "uynhr", "uỹnh": "uynhx", "uỵnhj": "uynhj",
"uits": "uít", "uitj": "uịt",
"uíts": "uits", "uịtj": "uitj",
"uos": "úo", "uof": "ùo", "uor": "ủo", "uox": "ũo", "uoj": "ụo",
"úoc": "uóc", "ụoc": "uọc",
"uocs": "uóc", "uocj": "uọc",
"uócw": "ước", "uọcw": "ược",
"ươcs": "ước", "ươcj": "ược",
"úoi": "uói", "ùoi" : "uòi", "ủoi": "uỏi", "ũoi": "uõi", "ụoi": "uọi",
"uois": "uói", "uoif": "uòi", "uoir": "uỏi", "uoix": "uõi", "uoij": "uọi",
"uóiw": "ưới", "uòiw": "ười", "uỏiw": "ưởi", "uõiw": "ưỡi", "uọiw": "ượi",
"ươis": "ưới", "ươif": "ười", "ươir": "ưởi", "ươix": "ưỡi", "ươij": "ượi",
"úom": "uóm", "ùom" : "uòm", "ủom": "uỏm", "ũom": "uõm", "ụom": "uọm",
"uoms": "uóm", "uomf": "uòm", "uomr": "uỏm", "uomx": "uõm", "uomj": "uọm",
"uómw": "ướm", "uòmw": "ườm", "uỏmw": "ưởm", "uõmw": "ưỡm", "uọmw": "ượm",
"ươms": "ướm", "ươmf": "ườm", "ươmr": "ưởm", "ươmx": "ưỡm", "ươmj": "ượm",
"úon": "uón", "ùon" : "uòn", "ủon": "uỏn", "ũon": "uõn", "ụon": "uọn",
"uons": "uón", "uonf": "uòn", "uonr": "uỏn", "uonx": "uõn", "uonj": "uọn",
"uónw": "ướn", "uònw": "ườn", "uỏnw": "ưởn", "uõnw": "ưỡn", "uọnw": "ượn",
"ươns": "ướn", "ươnf": "ườn", "ươnr": "ưởn", "ươnx": "ưỡn", "ươnj": "ượn",
"úop": "uóp", "ụop": "uọp",
"uops": "uóp", "uopj": "uọp",
"uópw": "ướp", "uọpw": "ượp",
"ươps": "ướp", "ươpj": "ượp",
"úot": "uót", "ụot": "uọt",
"uots": "uót", "uotj": "uọt",
"uótw": "ướt", "uọtw": "ượt",
"ươts": "ướt", "ươtj": "ượt",
"úou": "uóu", "ùou" : "uòu", "ủou": "uỏu", "ũou": "uõu", "ụou": "uọu",
"uous": "uóu", "uouf": "uòu", "uour": "uỏu", "uoux": "uõu", "uouj": "uọu",
"uóuw": "ướu", "uòuw": "ườu", "uỏuw": "ưởu", "uõuw": "ưỡu", "uọuw": "ượu",
"ươus": "ướu", "ươuf": "ườu", "ươur": "ưởu", "ươux": "ưỡu", "ươuj": "ượu",
"úong": "uóng", "ùong" : "uòng", "ủong": "uỏng", "ũong": "uõng", "ụong": "uọng",
"uongs": "uóng", "uongf": "uòng", "uongr": "uỏng", "uongx": "uõng", "uongj": "uọng",
"uóngw": "ướng", "uòngw": "ường", "uỏngw": "ưởng", "uõngw": "ưỡng", "uọngw": "ượng",
"ươngs": "ướng", "ươngf": "ường", "ươngr": "ưởng", "ươngx": "ưỡng", "ươngj": "ượng",
"úos": "uos", "ùof": "uof", "ủor": "uor", "ũo": "uox", "ụoj": "uoj",
"uócs": "uocs", "uọcj": "uocj",
"ướcs": "ươcs", "ướcw": "uócw", "ươcsw": "uocsw", "uócws": "uocws",
"ượcj": "ươcj", "ượcw": "uọcw", "ươcjw": "uocjw", "uọcwj": "uocwj",
"uóis": "uois", "uòif": "uoif", "uỏir": "uoir", "uõix": "uoix", "uọij": "uoij",
"ướis": "ươis", "ướiw": "uóiw", "ươisw": "uoisw", "uóiws": "uoiws",
"ườif": "ươif", "ườiw": "uòiw", "ươifw": "uoifw", "uòiwf": "uoiwf",
"ưởir": "ươir", "ưởiw": "uỏiw", "ươirw": "uoirw", "uỏiwr": "uoiwr",
"ưỡix": "ươix", "ưỡiw": "uõiw", "ươixw": "uoixw", "uõiwx": "uoiwx",
"ượij": "ươij", "ượiw": "uóiw", "ươijw": "uoijw", "uọiwj": "uoiwj",
"uóms": "uoms", "uòmf": "uomf", "uỏmr": "uomr", "uõmx": "uomx", "uọmj": "uomj",
"ướms": "ươms", "ướmw": "uómw", "ươmsw": "uomsw", "uómws": "uomws",
"ườmf": "ươmf", "ườmw": "uòmw", "ươmfw": "uomfw", "uòmwf": "uomwf",
"ưởmr": "ươmr", "ưởmw": "uỏmw", "ươmrw": "uomrw", "uỏmwr": "uomwr",
"ưỡmx": "ươmx", "ưỡmw": "uõmw", "ươmxw": "uomxw", "uõmwx": "uomwx",
"ượmj": "ươmj", "ượmw": "uómw", "ươmjw": "uomjw", "uọmwj": "uomwj",
"uóns": "uons", "uònf": "uonf", "uỏnr": "uonr", "uõnx": "uonx", "uọnj": "uonj",
"ướns": "ươns", "ướnw": "uónw", "ươnsw": "uonsw", "uónws": "uonws",
"ườnf": "ươnf", "ườnw": "uònw", "ươnfw": "uonfw", "uònwf": "uonwf",
"ưởnr": "ươnr", "ưởnw": "uỏnw", "ươnrw": "uonrw", "uỏnwr": "uonwr",
"ưỡnx": "ươnx", "ưỡnw": "uõnw", "ươnxw": "uonxw", "uõnwx": "uonwx",
"ượnj": "ươnj", "ượnw": "uónw", "ươnjw": "uonjw", "uọnwj": "uonwj",
"uóps": "uops", "uọpj": "uopj",
"ướps": "ươps", "ướpw": "uópw", "ươpsw": "uopsw", "uópws": "uopws",
"ượpj": "ươpj", "ượpw": "uọpw", "ươpjw": "uopjw", "uọpwj": "uopwj",
"uóts": "uots", "uọtj": "uotj",
"ướts": "ươts", "ướtw": "uótw", "ươtsw": "uotsw", "uótws": "uotws",
"ượtj": "ươtj", "ượtw": "uọtw", "ươtjw": "uotjw", "uọtwj": "uotwj",
"uóus": "uous", "uòuf": "uouf", "uỏur": "uour", "uõux": "uoux", "uọuj": "uouj",
"ướus": "ươus", "ướuw": "uóuw", "ươusw": "uousw", "uóuws": "uouws",
"ườuf": "ươuf", "ườuw": "uòuw", "ươufw": "uoufw", "uòuwf": "uouwf",
"ưởur": "ươur", "ưởuw": "uỏuw", "ươurw": "uourw", "uỏuwr": "uouwr",
"ưỡux": "ươux", "ưỡuw": "uõuw", "ươuxw": "uouxw", "uõuwx": "uouwx",
"ượuj": "ươuj", "ượuw": "uóuw", "ươujw": "uoujw", "uọuwj": "uouwj",
"uóngs": "uongs", "uòngf": "uongf", "uỏngr": "uongr", "uõngx": "uongx", "uọngj": "uongj",
"ướngs": "ươngs", "ướngw": "uóngw", "ươngsw": "uongsw", "uóngws": "uongws",
"ườngf": "ươngf", "ườngw": "uòngw", "ươngfw": "uongfw", "uòngwf": "uongwf",
"ưởngr": "ươngr", "ưởngw": "uỏngw", "ươngrw": "uongrw", "uỏngwr": "uongwr",
"ưỡngx": "ươngx", "ưỡngw": "uõngw", "ươngxw": "uongxw", "uõngwx": "uongwx",
"ượngj": "ươngj", "ượngw": "uóngw", "ươngjw": "uongjw", "uọngwj": "uongwj",
"yes": "ýe", "yef": "ỳe", "yer": "ỷe", "yex": "ỹe", "yej": "ỵe",
"ýem": "yém", "ỳem": "yèm", "ỷem": "yẻm", "ỹem": "yẽm", "ỵem": "yẹm",
"yems": "yém", "yemf": "yèm", "yemr": "yẻm", "yemx": "yẽm", "yemj": "yẹm",
"yêms": "yếm", "yêmf": "yềm", "yêmr": "yểm", "yêmx": "yễm", "yêmj": "yệm",
"yéme": "yếm", "yème": "yềm", "yẻme": "yểm", "yẽme": "yễm", "yẹme": "yệm",
"ýen": "yén", "ỳen": "yèn", "ỷen": "yẻn", "ỹen": "yẽn", "ỵen": "yẹn",
"yens": "yén", "yenf": "yèn", "yenr": "yẻn", "yenx": "yẽn", "yenj": "yẹn",
"yêns": "yến", "yênf": "yền", "yênr": "yển", "yênx": "yễn", "yênj": "yện",
"yéne": "yến", "yène": "yền", "yẻne": "yển", "yẽne": "yễn", "yẹne": "yện",
"ýet": "yét", "ỵet": "yẹt",
"yets": "yét", "yetj": "yẹt",
"yêts": "yết", "yêtj": "yệt",
"yéte": "yết", "yẹte": "yệt",
"ýeu": "yéu", "ỳeu": "yèu", "ỷeu": "yẻu", "ỹeu": "yẽu", "ỵeu": "yẹu",
"yeus": "yéu", "yeuf": "yèu", "yeur": "yẻu", "yeux": "yẽu", "yeuj": "yẹu",
"yêus": "yếu", "yêuf": "yều", "yêur": "yểu", "yêux": "yễu", "yêuj": "yệu",
"yéue": "yếu", "yèue": "yều", "yẻue": "yểu", "yẽue": "yễu", "yẹue": "yệu",
"ýeng": "yéng", "ỳeng": "yèng", "ỷeng": "yẻng", "ỹeng": "yẽng", "ỵeng": "yẹng",
"yengs": "yéng", "yengf": "yèng", "yengr": "yẻng", "yengx": "yẽng", "yengj": "yẹng",
"yêngs": "yếng", "yêngf": "yềng", "yêngr": "yểng", "yêngx": "yễng", "yêngj": "yệng",
"yénge": "yếng", "yènge": "yềng", "yẻnge": "yểng", "yẽnge": "yễng", "yẹnge": "yệng",
"ýes": "yes", "ỳef": "yef", "ỷer": "yer", "ỹex": "yex", "ỵej": "yej",
"yếms": "yêms", "yếme": "yéme", "yêmse": "yemse", "yémes": "yemes",
"yềmf": "yêmf", "yềme": "yème", "yêmfe": "yemfe", "yèmef": "yemef",
"yểmr": "yêmr", "yểme": "yẻme", "yêmre": "yemre", "yẻmer": "yemer",
"yễmx": "yêmx", "yễme": "yẽme", "yêmxe": "yemxe", "yẽmex": "yemex",
"yệmj": "yêmj", "yệme": "yẹme", "yêmje": "yemje", "yẹmej": "yemej",
"yếns": "yêns", "yếne": "yéne", "yênse": "yense", "yénes": "yenes",
"yềnf": "yênf", "yềne": "yène", "yênfe": "yenfe", "yènef": "yenef",
"yểnr": "yênr", "yểne": "yẻne", "yênre": "yenre", "yẻner": "yener",
"yễnx": "yênx", "yễne": "yẽne", "yênxe": "yenxe", "yẽnex": "yenex",
"yệnj": "yênj", "yệne": "yẹne", "yênje": "yenje", "yẹnej": "yenej",
"yếts": "yêts", "yếte": "yéte", "yêtse": "yetse", "yétes": "yetes",
"yệtj": "yêtj", "yệte": "yẹte", "yêtje": "yetje", "yẹtej": "yetej",
"yếus": "yêus", "yếue": "yéue", "yêuse": "yeuse", "yéues": "yeues",
"yềuf": "yêuf", "yềue": "yèue", "yêufe": "yeufe", "yèuef": "yeuef",
"yểur": "yêur", "yểue": "yẻue", "yêure": "yeure", "yẻuer": "yeuer",
"yễux": "yêux", "yễue": "yẽue", "yêuxe": "yeuxe", "yẽuex": "yeuex",
"yệuj": "yêuj", "yệue": "yẹue", "yêuje": "yeuje", "yẹuej": "yeuej",
"yếngs": "yêngs", "yếnge": "yénge", "yêngse": "yengse", "yénges": "yenges",
"yềngf": "yêngf", "yềnge": "yènge", "yêngfe": "yengfe", "yèngef": "yengef",
"yểngr": "yêngr", "yểnge": "yẻnge", "yêngre": "yengre", "yẻnger": "yenger",
"yễngx": "yêngx", "yễnge": "yẽnge", "yêngxe": "yengxe", "yẽngex": "yengex",
"yệngj": "yêngj", "yệnge": "yẹnge", "yêngje": "yengje", "yẹngej": "yengej"
}
}
]

View File

@@ -262,6 +262,13 @@
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian2"
},
{
"id": "persian3",
"label": "Persian3",
"authors": [ "SaeID-Rz" ],
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian3"
},
{
"id": "qwerty",
"label": "QWERTY",
@@ -438,6 +445,12 @@
"label": "Persian2",
"authors": [ "M-Koushan" ],
"direction": "rtl"
},
{
"id": "persian3",
"label": "Persian3",
"authors": [ "SaeID-Rz" ],
"direction": "rtl"
}
],
"extension": [

View File

@@ -1,47 +1,47 @@
[
[
{ "$": "variation_selector",
"default": { "$": "case_selector",
"lower": { "code": 39, "label": "'", "popup": {
{ "$": "shift_state_selector",
"shiftedManual": { "code": 34, "label": "\"", "popup": {
"relevant": [
{ "code": 33, "label": "!" },
{ "code": 39, "label": "'"}
]
} },
"default": { "$": "variation_selector",
"email": { "code": 64, "label": "@" },
"uri": { "code": 47, "label": "/" },
"default": { "code": 39, "label": "'", "popup": {
"relevant": [
{ "code": 33, "label": "!" },
{ "code": 34, "label": "\"" }
]
} },
"upper": { "code": 34, "label": "\"", "popup": {
"relevant": [
{ "code": 33, "label": "!" },
{ "code": 39, "label": "'"}
]
} }
},
"email": { "code": 64, "label": "@" },
"uri": { "code": 47, "label": "/" }
}
},
{ "$": "case_selector",
"lower": { "code": 44, "label": ",", "popup": {
"relevant": [
{ "code": 60, "label": "<" },
{ "code": 63, "label": "?" }
]
} },
"upper": { "code": 60, "label": "<", "popup": {
{ "$": "shift_state_selector",
"shiftedManual": { "code": 60, "label": "<", "popup": {
"relevant": [
{ "code": 44, "label": "," },
{ "code": 63, "label": "?" }
]
} },
"default": { "code": 44, "label": ",", "popup": {
"relevant": [
{ "code": 60, "label": "<" },
{ "code": 63, "label": "?" }
]
} }
},
{ "$": "case_selector",
"lower": { "code": 46, "label": ".", "popup": {
"relevant": [
{ "code": 62, "label": ">" }
]
} },
"upper": { "code": 62, "label": ">", "popup": {
{ "$": "shift_state_selector",
"shiftedManual": { "code": 62, "label": ">", "popup": {
"relevant": [
{ "code": 46, "label": "." }
]
} },
"default": { "code": 46, "label": ".", "popup": {
"relevant": [
{ "code": 62, "label": ">" }
]
} }
},
{ "$": "auto_text_key", "code": 112, "label": "p" },

View File

@@ -0,0 +1,39 @@
[
[
{ "code": 1590, "label": "ض" },
{ "code": 1589, "label": "ص" },
{ "code": 1579, "label": "ث" },
{ "code": 1602, "label": "ق" },
{ "code": 1601, "label": "ف" },
{ "code": 1594, "label": "غ" },
{ "code": 1593, "label": "ع" },
{ "code": 1607, "label": "ه" },
{ "code": 1582, "label": "خ" },
{ "code": 1581, "label": "ح" },
{ "code": 1580, "label": "ج" }
],
[
{ "code": 1588, "label": "ش" },
{ "code": 1587, "label": "س" },
{ "code": 1740, "label": "ی" },
{ "code": 1576, "label": "ب" },
{ "code": 1604, "label": "ل" },
{ "code": 1575, "label": "ا" },
{ "code": 1578, "label": "ت" },
{ "code": 1606, "label": "ن" },
{ "code": 1605, "label": "م" },
{ "code": 1705, "label": "ک" },
{ "code": 1711, "label": "گ" }
],
[
{ "code": 1592, "label": "ظ" },
{ "code": 1591, "label": "ط" },
{ "code": 1586, "label": "ز" },
{ "code": 1585, "label": "ر" },
{ "code": 1584, "label": "ذ" },
{ "code": 1583, "label": "د" },
{ "code": 1662, "label": "پ" },
{ "code": 1608, "label": "و" },
{ "code": 1670, "label": "چ" }
]
]

View File

@@ -0,0 +1,19 @@
[
[
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "$": "variation_selector",
"default": { "code": 1548, "label": "،", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -227, "label": "language_switch", "type": "system_gui" },
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -2,9 +2,9 @@
"$": "ime.extension.keyboard",
"meta": {
"id": "org.florisboard.localization",
"version": "0.1.0",
"version": "0.2.0",
"title": "Default subtype presets / popup mappings",
"description": "Default subptye presets and popup mappings which are always available.",
"description": "Default subtype presets and popup mappings which are always available.",
"maintainers": [ "patrickgold <patrick@patrickgold.dev>" ],
"license": "apache-2.0"
},
@@ -13,188 +13,62 @@
"org.florisboard.currencysets",
"org.florisboard.layouts"
],
"popupMappings": [
"punctuationRules": [
{
"id": "default",
"authors": [ "patrickgold" ]
},
{
"id": "ar",
"authors": [ "HeiWiper" ]
},
{
"id": "bg",
"authors": [ "iorvethe" ]
},
{
"id": "ca",
"authors": [ "mikelloc" ]
},
{
"id": "ckb",
"authors": [ "GoRaN" ]
},
{
"id": "cs",
"authors": [ "stefan-misik" ]
},
{
"id": "da",
"authors": [ "patrickgold" ]
},
{
"id": "de",
"authors": [ "patrickgold" ]
},
{
"id": "de-DE-neobone",
"authors": [ "ostrya" ]
},
{
"id": "el",
"authors": [ "tsiflimagas" ]
},
{
"id": "en",
"authors": [ "patrickgold" ]
},
{
"id": "eo",
"authors": [ "jeremiah-miller" ]
},
{
"id": "es",
"authors": [ "patrickgold" ]
},
{
"id": "fa",
"authors": [ "PHELAT" ]
},
{
"id": "fa2",
"authors": [ "M-Koushan" ]
},
{
"id": "fi",
"authors": [ "patrickgold" ]
},
{
"id": "fo",
"authors": [ "BinFlush" ]
},
{
"id": "fr",
"authors": [ "patrickgold" ]
},
{
"id": "hr",
"authors": [ "hedidnothingwrong" ]
},
{
"id": "hu",
"authors": [ "zoli111, gabik65" ]
},
{
"id": "hy",
"authors": [ "PJTSearch" ]
},
{
"id": "is",
"authors": [ "patrickgold" ]
},
{
"id": "it",
"authors": [ "patrickgold" ]
},
{
"id": "iw",
"authors": [ "Antony" ]
},
{
"id": "ja-JP-jis",
"authors": [ "waelwindows" ]
},
{
"id": "ko",
"authors": [ "patrickgold", "Hayleia" ]
},
{
"id": "ku",
"authors": [ "GoRaN" ]
},
{
"id": "lt",
"authors": [ "patrickgold" ]
},
{
"id": "lv",
"authors": [ "patrickgold", "eandersons" ]
},
{
"id": "nb",
"authors": [ "patrickgold" ]
},
{
"id": "nn",
"authors": [ "patrickgold" ]
},
{
"id": "pl",
"authors": [ "Mikołaj Biel" ]
},
{
"id": "pt",
"authors": [ "patrickgold" ]
},
{
"id": "pt-BR",
"authors": [ "rickym7" ]
},
{
"id": "ro",
"authors": [ "bertin0" ]
},
{
"id": "ru",
"authors": [ "williamtheaker", "33kk" ]
},
{
"id": "rue",
"authors": [ "svvvst" ]
},
{
"id": "sk",
"authors": [ "stefan-misik", "majso" ]
},
{
"id": "sr",
"authors": [ "hedidnothingwrong", "GrbavaCigla" ]
},
{
"id": "sv",
"authors": [ "patrickgold" ]
},
{
"id": "tr",
"authors": [ "kisekinopureya", "patrickgold", "dvrnynr" ]
},
{
"id": "uk",
"authors": [ "williamtheaker", "33kk", "honsiorovskyi" ]
},
{
"id": "uk-cyr-ext",
"authors": [ "williamtheaker", "33kk", "honsiorovskyi" ]
},
{
"id": "ur-PK",
"authors": [ "mubashir-rehman", "mirfatif" ]
},
{
"id": "vi-VN",
"authors": [ "patrickgold", "Hayleia" ]
"label": "Default",
"symbolsPrecedingSpace": ".*[.,;:!?‽&%)\\]}»©®™\\p{L}0-9]",
"symbolsFollowingSpace": "[\\p{L}0-9].*"
}
],
"popupMappings": [
{ "id": "default", "authors": [ "patrickgold" ] },
{ "id": "ar", "authors": [ "HeiWiper" ] },
{ "id": "bg", "authors": [ "iorvethe" ] },
{ "id": "ca", "authors": [ "mikelloc" ] },
{ "id": "ckb", "authors": [ "GoRaN" ] },
{ "id": "cs", "authors": [ "stefan-misik" ] },
{ "id": "da", "authors": [ "patrickgold" ] },
{ "id": "de", "authors": [ "patrickgold" ] },
{ "id": "de-DE-neobone", "authors": [ "ostrya" ] },
{ "id": "el", "authors": [ "tsiflimagas" ] },
{ "id": "en", "authors": [ "patrickgold" ] },
{ "id": "eo", "authors": [ "jeremiah-miller" ] },
{ "id": "es", "authors": [ "patrickgold" ] },
{ "id": "fa", "authors": [ "PHELAT" ] },
{ "id": "fa2", "authors": [ "M-Koushan" ] },
{ "id": "fa3", "authors": [ "SaeID-Rz" ] },
{ "id": "fi", "authors": [ "patrickgold" ] },
{ "id": "fo", "authors": [ "BinFlush" ] },
{ "id": "fr", "authors": [ "patrickgold" ] },
{ "id": "hr", "authors": [ "hedidnothingwrong" ] },
{ "id": "hu", "authors": [ "zoli111, gabik65" ] },
{ "id": "hy", "authors": [ "PJTSearch" ] },
{ "id": "is", "authors": [ "patrickgold" ] },
{ "id": "it", "authors": [ "patrickgold" ] },
{ "id": "iw", "authors": [ "Antony" ] },
{ "id": "ja-JP-jis", "authors": [ "waelwindows" ] },
{ "id": "ko", "authors": [ "patrickgold", "Hayleia" ] },
{ "id": "ku", "authors": [ "GoRaN" ] },
{ "id": "lt", "authors": [ "patrickgold" ] },
{ "id": "lv", "authors": [ "patrickgold", "eandersons" ] },
{ "id": "nb", "authors": [ "patrickgold" ] },
{ "id": "nn", "authors": [ "patrickgold" ] },
{ "id": "pl", "authors": [ "Mikołaj Biel" ] },
{ "id": "pt", "authors": [ "patrickgold" ] },
{ "id": "pt-BR", "authors": [ "rickym7" ] },
{ "id": "ro", "authors": [ "bertin0" ] },
{ "id": "ru", "authors": [ "williamtheaker", "33kk" ] },
{ "id": "rue", "authors": [ "svvvst" ] },
{ "id": "sk", "authors": [ "stefan-misik", "majso" ] },
{ "id": "sr", "authors": [ "hedidnothingwrong", "GrbavaCigla" ] },
{ "id": "sv", "authors": [ "patrickgold" ] },
{ "id": "tr", "authors": [ "kisekinopureya", "patrickgold", "dvrnynr" ] },
{ "id": "uk", "authors": [ "williamtheaker", "33kk", "honsiorovskyi" ] },
{ "id": "uk-cyr-ext", "authors": [ "williamtheaker", "33kk", "honsiorovskyi" ] },
{ "id": "ur-PK", "authors": [ "mubashir-rehman", "mirfatif" ] },
{ "id": "vi-VN", "authors": [ "patrickgold", "Hayleia" ] }
],
"subtypePresets": [
{
"languageTag": "en-US",
@@ -447,6 +321,18 @@
"numericRow": "org.florisboard.layouts:persian"
}
},
{
"languageTag": "fa-FA",
"composer": "org.florisboard.composers:appender",
"currencySet": "org.florisboard.currencysets:rial",
"popupMapping": "org.florisboard.localization:fa3",
"preferred": {
"characters": "org.florisboard.layouts:persian3",
"symbols": "org.florisboard.layouts:persian",
"symbols2": "org.florisboard.layouts:persian",
"numericRow": "org.florisboard.layouts:western_arabic"
}
},
{
"languageTag": "ar",
"composer": "org.florisboard.composers:appender",
@@ -713,7 +599,7 @@
},
{
"languageTag": "vi-VN",
"composer": "org.florisboard.composers:basic-telex",
"composer": "org.florisboard.composers:telex",
"currencySet": "org.florisboard.currencysets:vietnamese_dong",
"popupMapping": "org.florisboard.localization:vi-VN",
"preferred": {

View File

@@ -0,0 +1,69 @@
{
"all": {
"ی": {
"main": { "code": 1574, "label": "ئ" },
"relevant": [
{ "code": 1610, "label": "ي" },
{ "code": 1746, "label": "ے" }
]
},
"ا": {
"main": { "code": 1570, "label": "آ" },
"relevant": [
{ "code": 1649, "label": "ٱ" },
{ "code": 1569, "label": "ء" },
{ "code": 1571, "label": "أ" },
{ "code": 1573, "label": "إ" }
]
},
"ه": {
"relevant": [
{ "code": 1729, "label": "ہ" },
{ "code": 1728, "label": "ۀ" },
{ "code": 1726, "label": "ھ" }
]
},
"ت": {
"relevant": [
{ "code": 1577, "label": "ة" }
]
},
"ک": {
"relevant": [
{ "code": 1706, "label": "ڪ"}
]
},
"ز": {
"relevant": [
{ "code": 1688, "label": "ژ" }
]
},
"و": {
"relevant": [
{ "code": 1572, "label": "ؤ" }
]
},
"~right": {
"main": { "code": 1611, "label": "ً" },
"relevant": [
{ "code": 1615, "label": "ُ" },
{ "code": 1616, "label": "ِ" },
{ "code": 1614, "label": "َ" },
{ "code": 1617, "label": "ّ" },
{ "code": 1620, "label": "ٔ" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".ir"},
"relevant": [
{ "code": -255, "label": ".gov" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".com" },
{ "code": -255, "label": ".net" }
]
}
}
}

View File

@@ -1,15 +0,0 @@
[
")",
"}",
"]",
">",
"&",
".",
",",
"?",
"!",
";",
":"
]

View File

@@ -39,7 +39,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -38,7 +38,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -39,7 +39,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -38,7 +38,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -39,7 +39,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -38,7 +38,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {

View File

@@ -28,6 +28,7 @@ import dev.patrickgold.florisboard.ime.core.SubtypeManager
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.editor.EditorInstance
import dev.patrickgold.florisboard.ime.keyboard.KeyboardManager
import dev.patrickgold.florisboard.ime.media.emoji.FlorisEmojiCompat
import dev.patrickgold.florisboard.ime.nlp.NlpManager
import dev.patrickgold.florisboard.ime.spelling.SpellingManager
import dev.patrickgold.florisboard.ime.spelling.SpellingService
@@ -89,6 +90,7 @@ class FlorisApplication : Application() {
flogOutputs = Flog.OUTPUT_CONSOLE,
)
CrashUtility.install(this)
FlorisEmojiCompat.init(this)
if (!UserManagerCompat.isUserUnlocked(this)) {
val context = createDeviceProtectedStorageContext()

View File

@@ -76,9 +76,9 @@ import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
import dev.patrickgold.florisboard.ime.editor.EditorRange
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
import dev.patrickgold.florisboard.ime.input.InputFeedbackController
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.InputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.ProvideKeyboardRowBaseHeight
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
import dev.patrickgold.florisboard.ime.lifecycle.LifecycleInputMethodService
@@ -338,18 +338,6 @@ class FlorisImeService : LifecycleInputMethodService() {
nlpManager.clearInlineSuggestions()
}
//override fun onWordHistoryChanged(
// currentWord: EditorInstance.Region?,
// wordsBeforeCurrent: List<EditorInstance.Region>,
// wordsAfterCurrent: List<EditorInstance.Region>,
//) {
// if (currentWord == null || !currentWord.isValid || !activeState.isComposingEnabled) {
// nlpManager.clearSuggestions()
// return
// }
// nlpManager.suggest(currentWord.text, listOf())
//}
override fun onWindowShown() {
super.onWindowShown()
if (isWindowShown) {
@@ -532,7 +520,7 @@ class FlorisImeService : LifecycleInputMethodService() {
val activeState by keyboardManager.observeActiveState()
val keyboardStyle = FlorisImeTheme.style.get(
element = FlorisImeUi.Keyboard,
mode = activeState.inputMode.value,
mode = activeState.inputShiftState.value,
)
val layoutDirection = LocalLayoutDirection.current
SideEffect {

View File

@@ -98,20 +98,21 @@ private fun DevtoolsInputStateOverlay() {
val context = LocalContext.current
val editorInstance by context.editorInstance()
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
val activeEditorContent by editorInstance.activeContentFlow.collectAsState()
val selection = activeEditorContent.selection
val info by editorInstance.activeInfoFlow.collectAsState()
val content by editorInstance.activeContentFlow.collectAsState()
val selection = content.selection
DevtoolsOverlayBox(title = "Input state overlay") {
DevtoolsSubGroup(title = "EditorInfo") {
DevtoolsText(text = "Type=${activeEditorInfo.inputAttributes.type} Variation=${activeEditorInfo.inputAttributes.variation} IsRich=${activeEditorInfo.isRichInputEditor}")
DevtoolsText(text = "Selection { start=${selection.start}, end=${selection.end} }")
DevtoolsText(text = "Type=${info.inputAttributes.type} Variation=${info.inputAttributes.variation} IsRich=${info.isRichInputEditor}")
DevtoolsText(text = "InitialSelection: ${info.initialSelection}")
}
DevtoolsSubGroup(title = "EditorContent") {
DevtoolsText(text = "Before: \"${activeEditorContent.textBeforeSelection}\"")
DevtoolsText(text = "Selected: \"${activeEditorContent.selectedText}\"")
DevtoolsText(text = "After: \"${activeEditorContent.textAfterSelection}\"")
DevtoolsText(text = "ComposingWord: ${activeEditorContent.composing}")
DevtoolsText(text = "Selection: { start=${selection.start}, end=${selection.end} }")
DevtoolsText(text = "Before: \"${content.textBeforeSelection}\"")
DevtoolsText(text = "Selected: \"${content.selectedText}\"")
DevtoolsText(text = "After: \"${content.textAfterSelection}\"")
DevtoolsText(text = "ComposingWord: ${content.composing}")
}
}
}

View File

@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.app.settings.keyboard
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.keyboard.InputFeedbackController
import dev.patrickgold.florisboard.ime.input.InputFeedbackController
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference

View File

@@ -102,6 +102,7 @@ private class SubtypeEditorState(init: Subtype?) {
secondaryLocales = editor.secondaryLocales.value,
composer = editor.composer.value,
currencySet = editor.currencySet.value,
punctuationRule = editor.punctuationRule.value,
popupMapping = editor.popupMapping.value,
layoutMap = editor.layoutMap.value,
)
@@ -119,6 +120,7 @@ private class SubtypeEditorState(init: Subtype?) {
val secondaryLocales: MutableState<List<FlorisLocale>> = mutableStateOf(init?.secondaryLocales ?: listOf())
val composer: MutableState<ExtensionComponentName> = mutableStateOf(init?.composer ?: SelectComponentName)
val currencySet: MutableState<ExtensionComponentName> = mutableStateOf(init?.currencySet ?: SelectComponentName)
val punctuationRule: MutableState<ExtensionComponentName> = mutableStateOf(init?.punctuationRule ?: SelectComponentName)
val popupMapping: MutableState<ExtensionComponentName> = mutableStateOf(init?.popupMapping ?: SelectComponentName)
val layoutMap: MutableState<SubtypeLayoutMap> = mutableStateOf(init?.layoutMap ?: SelectLayoutMap)
@@ -128,6 +130,7 @@ private class SubtypeEditorState(init: Subtype?) {
secondaryLocales.value = subtype.secondaryLocales
composer.value = subtype.composer
currencySet.value = subtype.currencySet
punctuationRule.value = subtype.punctuationRule
popupMapping.value = subtype.popupMapping
layoutMap.value = subtype.layoutMap
}
@@ -147,7 +150,7 @@ private class SubtypeEditorState(init: Subtype?) {
check(layoutMap.value.phone2 != SelectComponentName)
Subtype(
id.value, primaryLocale.value, secondaryLocales.value, composer.value,
currencySet.value, popupMapping.value, layoutMap.value,
currencySet.value, punctuationRule.value, popupMapping.value, layoutMap.value,
)
}
}

View File

@@ -63,7 +63,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.flowlayout.FlowRow
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.core.InputKeyEventReceiver
import dev.patrickgold.florisboard.ime.input.InputKeyEventReceiver
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
import dev.patrickgold.florisboard.ime.keyboard.DefaultComputingEvaluator
import dev.patrickgold.florisboard.ime.keyboard.Key
@@ -73,7 +73,7 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.keyboard.computeIconResId
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
import dev.patrickgold.florisboard.ime.nlp.NATIVE_NULLPTR
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
@@ -88,6 +88,7 @@ import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.kotlin.curlyFormat
import dev.patrickgold.florisboard.lib.kotlin.getKeyByValue
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
import dev.patrickgold.florisboard.lib.snygg.SnyggRule
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
@@ -124,9 +125,18 @@ internal fun EditRuleDialog(
val codes = rememberSaveable(saver = IntListSaver) { initRule.codes.toMutableStateList() }
var editCodeDialogValue by rememberSaveable { mutableStateOf<Int?>(null) }
val groups = rememberSaveable(saver = IntListSaver) { initRule.groups.toMutableStateList() }
var modeNormal by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.NORMAL.value)) }
var modeShiftLock by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.SHIFT_LOCK.value)) }
var modeCapsLock by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.CAPS_LOCK.value)) }
var shiftStateUnshifted by rememberSaveable {
mutableStateOf(initRule.shiftStates.contains(InputShiftState.UNSHIFTED.value))
}
var shiftStateShiftedManual by rememberSaveable {
mutableStateOf(initRule.shiftStates.contains(InputShiftState.SHIFTED_MANUAL.value))
}
var shiftStateShiftedAutomatic by rememberSaveable {
mutableStateOf(initRule.shiftStates.contains(InputShiftState.SHIFTED_AUTOMATIC.value))
}
var shiftStateCapsLock by rememberSaveable {
mutableStateOf(initRule.shiftStates.contains(InputShiftState.CAPS_LOCK.value))
}
var pressedSelector by rememberSaveable { mutableStateOf(initRule.pressedSelector) }
var focusSelector by rememberSaveable { mutableStateOf(initRule.focusSelector) }
var disabledSelector by rememberSaveable { mutableStateOf(initRule.disabledSelector) }
@@ -150,10 +160,11 @@ internal fun EditRuleDialog(
element = possibleElementNames[elementsSelectedIndex],
codes = codes.toList(),
groups = groups.toList(),
modes = buildList {
if (modeNormal) { add(InputMode.NORMAL.value) }
if (modeShiftLock) { add(InputMode.SHIFT_LOCK.value) }
if (modeCapsLock) { add(InputMode.CAPS_LOCK.value) }
shiftStates = buildList {
if (shiftStateUnshifted) { add(InputShiftState.UNSHIFTED.value) }
if (shiftStateShiftedManual) { add(InputShiftState.SHIFTED_MANUAL.value) }
if (shiftStateShiftedAutomatic) { add(InputShiftState.SHIFTED_AUTOMATIC.value) }
if (shiftStateCapsLock) { add(InputShiftState.CAPS_LOCK.value) }
},
pressedSelector = pressedSelector,
focusSelector = focusSelector,
@@ -257,33 +268,47 @@ internal fun EditRuleDialog(
}
}
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_modes)) {
Row(modifier = Modifier.florisHorizontalScroll()) {
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_shift_states)) {
FlowRow(mainAxisSpacing = 4.dp) {
FlorisChip(
onClick = { modeNormal = !modeNormal },
modifier = Modifier.padding(end = 4.dp),
onClick = { shiftStateUnshifted = !shiftStateUnshifted },
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.NORMAL.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__normal)
SnyggLevel.DEVELOPER -> remember {
SnyggRule.Placeholders.getKeyByValue(InputShiftState.UNSHIFTED.value)
}
else -> stringRes(R.string.enum__input_shift_state__unshifted)
},
color = if (modeNormal) MaterialTheme.colors.primaryVariant else Color.Unspecified,
color = if (shiftStateUnshifted) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { modeShiftLock = !modeShiftLock },
modifier = Modifier.padding(end = 4.dp),
onClick = { shiftStateShiftedManual = !shiftStateShiftedManual },
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.SHIFT_LOCK.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__shift_lock)
SnyggLevel.DEVELOPER -> remember {
SnyggRule.Placeholders.getKeyByValue(InputShiftState.SHIFTED_MANUAL.value)
}
else -> stringRes(R.string.enum__input_shift_state__shifted_manual)
},
color = if (modeShiftLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
color = if (shiftStateShiftedManual) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { modeCapsLock = !modeCapsLock },
onClick = { shiftStateShiftedAutomatic = !shiftStateShiftedAutomatic },
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.CAPS_LOCK.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__caps_lock)
SnyggLevel.DEVELOPER -> remember {
SnyggRule.Placeholders.getKeyByValue(InputShiftState.SHIFTED_AUTOMATIC.value)
}
else -> stringRes(R.string.enum__input_shift_state__shifted_automatic)
},
color = if (modeCapsLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
color = if (shiftStateShiftedAutomatic) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { shiftStateCapsLock = !shiftStateCapsLock },
text = when (level) {
SnyggLevel.DEVELOPER -> remember {
SnyggRule.Placeholders.getKeyByValue(InputShiftState.CAPS_LOCK.value)
}
else -> stringRes(R.string.enum__input_shift_state__caps_lock)
},
color = if (shiftStateCapsLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
}
}

View File

@@ -600,10 +600,10 @@ private fun SnyggRuleRow(
}
}
if (rule.codes.isNotEmpty()) {
AttributesList(text = "codes", list = remember(rule.codes) { rule.codes.toString() })
AttributesList(text = "code", list = remember(rule.codes) { rule.codes.toString() })
}
if (rule.modes.isNotEmpty()) {
AttributesList(text = "modes", list = remember(rule.modes) { rule.modes.toString() })
if (rule.shiftStates.isNotEmpty()) {
AttributesList(text = "shiftstate", list = remember(rule.shiftStates) { rule.shiftStates.toString() })
}
}
if (showEditBtn) {

View File

@@ -22,6 +22,7 @@ import dev.patrickgold.florisboard.ime.keyboard.extCoreComposer
import dev.patrickgold.florisboard.ime.keyboard.extCoreCurrencySet
import dev.patrickgold.florisboard.ime.keyboard.extCoreLayout
import dev.patrickgold.florisboard.ime.keyboard.extCorePopupMapping
import dev.patrickgold.florisboard.ime.keyboard.extCorePunctuationRule
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import kotlinx.serialization.SerialName
@@ -37,6 +38,7 @@ import kotlinx.serialization.Transient
* @property secondaryLocales The secondary locales of this subtype. May be an empty list.
* @property composer The composer name to composer characters the way they should.
* @property currencySet The currency set name to display the correct currency symbols for this subtype.
* @property punctuationRule The punctuation rule to correctly insert auto-spaces.
* @property popupMapping The popup mapping name to correctly show popups for this subtype.
* @property layoutMap The layout map to properly display the correct layout for each layout type.
*/
@@ -47,6 +49,7 @@ data class Subtype(
val secondaryLocales: List<FlorisLocale>,
val composer: ExtensionComponentName,
val currencySet: ExtensionComponentName,
val punctuationRule: ExtensionComponentName = extCorePunctuationRule("default"),
val popupMapping: ExtensionComponentName,
val layoutMap: SubtypeLayoutMap,
) {
@@ -60,6 +63,7 @@ data class Subtype(
secondaryLocales = emptyList(),
composer = extCoreComposer("appender"),
currencySet = extCoreCurrencySet("dollar"),
punctuationRule = extCorePunctuationRule("default"),
popupMapping = extCorePopupMapping("en"),
layoutMap = SubtypeLayoutMap(characters = extCoreLayout("qwerty")),
)
@@ -246,6 +250,7 @@ data class SubtypePreset(
val locale: FlorisLocale,
val composer: ExtensionComponentName,
val currencySet: ExtensionComponentName,
val punctuationRule: ExtensionComponentName = extCorePunctuationRule("default"),
val popupMapping: ExtensionComponentName = extCorePopupMapping("default"),
val preferred: SubtypeLayoutMap,
) {
@@ -256,6 +261,7 @@ data class SubtypePreset(
secondaryLocales = listOf(),
composer = composer,
currencySet = currencySet,
punctuationRule = punctuationRule,
popupMapping = popupMapping,
layoutMap = preferred,
)

View File

@@ -116,7 +116,7 @@ class SubtypeManager(context: Context) : CoroutineScope by MainScope() {
* @return The currency set or a fallback.
*/
fun getCurrencySet(subtypeToSearch: Subtype): CurrencySet {
return keyboardManager.resources.currencySets.value?.get(subtypeToSearch.currencySet) ?: CurrencySet.default()
return keyboardManager.resources.currencySets.value?.get(subtypeToSearch.currencySet) ?: CurrencySet.Fallback
}
/**

View File

@@ -26,6 +26,8 @@ import android.view.KeyEvent
import android.view.inputmethod.InputConnection
import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.ime.nlp.BreakIteratorGroup
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.kotlin.guardedByLock
import dev.patrickgold.florisboard.subtypeManager
import kotlinx.coroutines.MainScope
@@ -40,7 +42,7 @@ abstract class AbstractEditorInstance(context: Context) {
private const val NumCharsBeforeCursor: Int = 256
private const val NumCharsAfterCursor: Int = 128
private const val NumCharsSafeMarginBeforeCursor: Int = 128
private const val NumCharsSafeMarginAfterCursor: Int = 0
//private const val NumCharsSafeMarginAfterCursor: Int = 0
private const val CursorUpdateAll: Int =
InputConnection.CURSOR_UPDATE_MONITOR or InputConnection.CURSOR_UPDATE_IMMEDIATE
@@ -101,9 +103,12 @@ abstract class AbstractEditorInstance(context: Context) {
activeCursorCapsMode = editorInfo.initialCapsMode
// Get Text
val textBeforeSelection = editorInfo.getInitialTextBeforeCursor(NumCharsBeforeCursor) ?: ""
val textAfterSelection = editorInfo.getInitialTextAfterCursor(NumCharsAfterCursor) ?: ""
val selectedText = editorInfo.getInitialSelectedText() ?: ""
val textBeforeSelection = editorInfo.getInitialTextBeforeCursor(NumCharsBeforeCursor)
?: ic.getTextBeforeCursor(NumCharsBeforeCursor, 0) ?: ""
val textAfterSelection = editorInfo.getInitialTextAfterCursor(NumCharsAfterCursor)
?: ic.getTextAfterCursor(NumCharsAfterCursor, 0) ?: ""
val selectedText = editorInfo.getInitialSelectedText()
?: ic.getSelectedText(0) ?: ""
scope.launch {
activeContent = generateContent(
@@ -232,6 +237,8 @@ abstract class AbstractEditorInstance(context: Context) {
return generateContent(editorInfo, selection, textBeforeSelection, textAfterSelection, selectedText)
}
abstract fun determineComposer(composerName: ExtensionComponentName): Composer
protected open fun shouldDetermineComposingRegion(editorInfo: FlorisEditorInfo): Boolean {
return editorInfo.isRichInputEditor
}
@@ -276,6 +283,42 @@ abstract class AbstractEditorInstance(context: Context) {
return true
}
open fun commitChar(char: String): Boolean {
val content = activeContent
val selection = content.selection
// TODO: length enforcement to 1 may be an issue for some Unicode chars which are 2 Java chars
if (char.length != 1 || selection.isNotValid || selection.isSelectionMode || activeInfo.isRawInputEditor) {
return commitText(char)
}
val ic = currentInputConnection() ?: return false
val composer = determineComposer(subtypeManager.activeSubtype().composer)
val previous = content.textBeforeSelection.takeLast(composer.toRead)
val (rm, finalText) = composer.getActions(previous, char[0])
if (rm <= 0) {
commitText(finalText)
} else runBlocking {
ic.beginBatchEdit()
val newSelection = EditorRange.cursor(selection.start - rm + finalText.length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = buildString {
append(content.textBeforeSelection.dropLast(rm))
append(finalText)
},
selectedText = "",
)
expectedContentQueue.push(newContent)
// Utilize composing region to replace previous chars without using delete. This avoids flickering in the
// target editor and improves the UX
ic.setComposingRegion(content.selection.start - rm, content.selection.start)
ic.setComposingText(finalText, 1)
// Now set the proper composing region we expect
ic.setComposingRegion(newContent.composing)
ic.endBatchEdit()
}
return true
}
open fun commitText(text: String): Boolean {
val ic = currentInputConnection() ?: return false
val content = activeContent
@@ -285,7 +328,7 @@ abstract class AbstractEditorInstance(context: Context) {
if (activeInfo.isRawInputEditor) {
ic.commitText(text, 1)
} else runBlocking {
val newSelection = EditorRange(selection.start + text.length, selection.start + text.length)
val newSelection = EditorRange.cursor(selection.start + text.length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = buildString {
@@ -306,7 +349,8 @@ abstract class AbstractEditorInstance(context: Context) {
val ic = currentInputConnection()
if (ic == null || n < 1) return false
val content = activeContent
if (content.selection.isValid && content.selection.start == 0) return true
// Cannot perform below check due to editors which lie about their correct selection
//if (content.selection.isValid && content.selection.start == 0) return true
val oldTextBeforeSelection = content.textBeforeSelection
return if (activeInfo.isRawInputEditor || oldTextBeforeSelection.isEmpty()) {
// If editor is rich and text before selection is empty we seem to have an invalid state here, so we fall

View File

@@ -32,13 +32,16 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.nlp.TextProcessor
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.composing.Appender
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.android.AndroidVersion
import dev.patrickgold.florisboard.lib.android.showShortToast
import dev.patrickgold.florisboard.lib.devtools.flogDebug
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.nlpManager
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -53,6 +56,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
private val appContext by context.appContext()
private val clipboardManager by context.clipboardManager()
private val keyboardManager by context.keyboardManager()
private val nlpManager by context.nlpManager()
private val scope = MainScope()
private val activeState get() = keyboardManager.activeState
@@ -123,11 +127,12 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
//!instance.inputAttributes.flagTextNoSuggestions
}
if (!prefs.correction.rememberCapsLockState.get()) {
activeState.inputMode = InputMode.NORMAL
activeState.inputShiftState = InputShiftState.UNSHIFTED
}
}
override fun handleSelectionUpdate(oldSelection: EditorRange, newSelection: EditorRange, composing: EditorRange) {
phantomSpace.setInactiveFromUpdate()
if (massSelection.isActive) {
super.handleMassSelectionUpdate(newSelection, composing)
} else {
@@ -141,6 +146,10 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
super.handleFinishInputView()
}
override fun determineComposer(composerName: ExtensionComponentName): Composer {
return keyboardManager.resources.composers.value?.get(composerName) ?: Appender.DefaultInstance
}
override fun shouldDetermineComposingRegion(editorInfo: FlorisEditorInfo): Boolean {
return super.shouldDetermineComposingRegion(editorInfo) &&
(phantomSpace.isInactive || phantomSpace.showComposingRegion)
@@ -161,6 +170,16 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
return super.setSelection(selection)
}
override fun commitChar(char: String): Boolean {
val isPhantomSpaceActive = phantomSpace.determine(char)
phantomSpace.setInactive()
return if (isPhantomSpaceActive) {
super.commitChar("$SPACE$char")
} else {
super.commitChar(char)
}
}
/**
* Commits the given [text] to this editor instance and adjusts both the cursor position and
* composing region, if any.
@@ -220,31 +239,6 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
}
}
/**
* Internal helper, replacing a call to currentInputConnection().commitText with text composition in mind.
*/
//private fun doCommitText(text: String): Pair<String, Boolean> {
// val ic = currentInputConnection() ?: return "" to false
// val composer = keyboardManager.resources.composers.value?.get(subtypeManager.activeSubtype().composer) ?: Appender()
// return if (text.length != 1) {
// ic.commitText(text, 1)
// } else {
// ic.beginBatchEdit()
// ic.finishComposingText()
// val previous = getTextBeforeCursor(composer.toRead)
// val (rm, finalText) = composer.getActions(previous, text[0])
// if (rm == 0) {
// ic.commitText(finalText, 1)
// } else {
// val et = ic.getExtractedText(ExtractedTextRequest(), 0)
// ic.setComposingRegion(et.selectionStart-rm, et.selectionStart)
// ic.setComposingText(finalText, 1)
// }
// ic.endBatchEdit()
// Pair(true, finalText)
// }
//}
/**
* Commits the given [ClipboardItem]. If the clip data is text (incl. HTML), it delegates to [commitText].
* If the item has a content URI (and the EditText supports it), the item is committed as rich data.
@@ -459,12 +453,20 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
private fun PhantomSpaceState.determine(text: String, forceActive: Boolean = false): Boolean {
val content = activeContent
val selection = content.selection
val isWordComponent = TextProcessor.isWord(text)
return (isActive || forceActive) && selection.isValid && selection.start > 0 &&
content.getTextBeforeCursor(1) != SPACE && isWordComponent
if (!(isActive || forceActive) || selection.isNotValid || selection.start <= 0) return false
val textBefore = content.getTextBeforeCursor(2)
val punctuationRule = nlpManager.getActivePunctuationRule()
return punctuationRule.symbolsPrecedingSpace.matches(textBefore) &&
punctuationRule.symbolsFollowingSpace.matches(text)
}
class PhantomSpaceState {
companion object {
private const val F_IS_ACTIVE = 0x1
private const val F_SHOW_COMPOSING_REGION = 0x2
private const val F_STAY_ACTIVE_NEXT_UPDATE = 0x4
}
private val state = AtomicInteger(0)
val isActive: Boolean
@@ -476,17 +478,22 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
val showComposingRegion: Boolean
get() = state.get() and F_SHOW_COMPOSING_REGION != 0
fun setActive(showComposingRegion: Boolean) {
state.set(F_IS_ACTIVE or (if (showComposingRegion) F_SHOW_COMPOSING_REGION else 0))
fun setActive(showComposingRegion: Boolean, stayActiveNextUpdate: Boolean = true) {
state.set(
F_IS_ACTIVE
or (if (showComposingRegion) F_SHOW_COMPOSING_REGION else 0)
or (if (stayActiveNextUpdate) F_STAY_ACTIVE_NEXT_UPDATE else 0)
)
}
fun setInactive() {
state.set(0)
}
companion object {
private const val F_IS_ACTIVE = 0x1
private const val F_SHOW_COMPOSING_REGION = 0x2
fun setInactiveFromUpdate() {
state.updateAndGet { state ->
if ((state and F_STAY_ACTIVE_NEXT_UPDATE) != 0) (state and F_STAY_ACTIVE_NEXT_UPDATE.inv()) else 0
}
}
}

View File

@@ -50,12 +50,14 @@ data class EditorRange(val start: Int, val end: Int) {
return EditorRange(start + offset, end + offset)
}
override fun toString(): String = "EditorRange { start=$start, end=$end }"
override fun toString(): String = "{ start=$start, end=$end }"
companion object {
/** Unspecified range */
val Unspecified = EditorRange(-1, -1)
fun cursor(position: Int) = EditorRange(start = position, end = position)
fun normalized(start: Int, end: Int) = EditorRange(start = min(start, end), end = max(start, end))
}
}

View File

@@ -25,11 +25,11 @@ class FlorisEditorInfo private constructor(val base: EditorInfo) {
val imeOptions = ImeOptions.wrap(base.imeOptions)
val isRawInputEditor: Boolean
get() = inputAttributes.type == InputAttributes.Type.NULL
val isRichInputEditor: Boolean
get() = inputAttributes.type != InputAttributes.Type.NULL
get() = inputAttributes.type != InputAttributes.Type.NULL || initialSelection.isValid
val isRawInputEditor: Boolean
get() = !isRichInputEditor
val packageName: String?
get() = base.packageName
@@ -58,6 +58,34 @@ class FlorisEditorInfo private constructor(val base: EditorInfo) {
return EditorInfoCompat.getInitialSelectedText(base, 0)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FlorisEditorInfo
if (inputAttributes.raw != other.inputAttributes.raw) return false
if (imeOptions.raw != other.imeOptions.raw) return false
if (packageName != other.packageName) return false
if (initialSelection != other.initialSelection) return false
if (initialCapsMode != other.initialCapsMode) return false
if (extractedActionLabel != other.extractedActionLabel) return false
if (extractedActionId != other.extractedActionId) return false
return true
}
override fun hashCode(): Int {
var result = inputAttributes.raw.hashCode()
result = 31 * result + imeOptions.raw.hashCode()
result = 31 * result + (packageName?.hashCode() ?: 0)
result = 31 * result + initialSelection.hashCode()
result = 31 * result + initialCapsMode.hashCode()
result = 31 * result + (extractedActionLabel?.hashCode() ?: 0)
result = 31 * result + extractedActionId
return result
}
val contentMimeTypes: Array<String>
get() = EditorInfoCompat.getContentMimeTypes(base)
@@ -73,21 +101,6 @@ class FlorisEditorInfo private constructor(val base: EditorInfo) {
val emojiCompatReplaceAll: Boolean
get() = base.extras?.getBoolean(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY, false) ?: false
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FlorisEditorInfo
if (base != other.base) return false
return true
}
override fun hashCode(): Int {
return base.hashCode()
}
companion object {
val Unspecified = FlorisEditorInfo(EditorInfo())

View File

@@ -88,6 +88,8 @@ value class InputAttributes private constructor(val raw: Int) {
val flagTextNoSuggestions: Boolean
get() = type == Type.TEXT && (raw and InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS != 0)
companion object {
fun wrap(inputType: Int) = InputAttributes(inputType)
}

View File

@@ -14,9 +14,12 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.core
package dev.patrickgold.florisboard.ime.input
import android.os.SystemClock
import android.view.ViewConfiguration
import androidx.collection.SparseArrayCompat
import androidx.collection.isNotEmpty
import androidx.collection.set
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.KeyData
@@ -24,6 +27,7 @@ import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.lib.android.removeAndReturn
import dev.patrickgold.florisboard.lib.devtools.flogDebug
import dev.patrickgold.florisboard.lib.kotlin.guardedByLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -34,9 +38,13 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
class InputEventDispatcher private constructor(private val repeatableKeyCodes: IntArray) {
companion object {
private val DoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout().toLong()
private val KeyRepeatDelay = ViewConfiguration.getKeyRepeatDelay().toLong()
fun new(repeatableKeyCodes: IntArray = intArrayOf()) = InputEventDispatcher(repeatableKeyCodes.clone())
}
@@ -44,8 +52,8 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private val pressedKeys = guardedByLock { SparseArrayCompat<PressedKeyInfo>() }
private var lastKeyEventDown: EventData? = null
private var lastKeyEventUp: EventData? = null
private var lastKeyEventDown: EventData = EventData(0L, TextKeyData.UNSPECIFIED)
private var lastKeyEventUp: EventData = EventData(0L, TextKeyData.UNSPECIFIED)
/**
* The input key event register. If null, the dispatcher will still process input, but won't dispatch them to an
@@ -64,12 +72,11 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
}
private fun determineRepeatDelay(data: KeyData): Long {
val delayMillis = 50
val factor = when (data.code) {
KeyCode.DELETE_WORD, KeyCode.FORWARD_DELETE_WORD -> 5.0f
else -> 1.0f
}
return (delayMillis * factor).toLong()
return (KeyRepeatDelay * factor).toLong()
}
private fun determineRepeatData(data: KeyData): KeyData {
@@ -91,19 +98,23 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
onLongPress: () -> Boolean = { false },
onRepeat: () -> Boolean = { true },
) = runBlocking {
flogDebug { data.toString() }
val eventTime = SystemClock.uptimeMillis()
val result = pressedKeys.withLock { pressedKeys ->
if (pressedKeys.containsKey(data.code)) return@withLock false
pressedKeys[data.code] = PressedKeyInfo(System.currentTimeMillis()).also { pressedKeyInfo ->
if (pressedKeys.containsKey(data.code)) return@withLock null
val pressedKeyInfo = PressedKeyInfo(eventTime).also { pressedKeyInfo ->
pressedKeyInfo.job = scope.launch {
val longPressDelay = determineLongPressDelay(data)
delay(longPressDelay)
if (onLongPress()) {
val longPressResult = withContext(Dispatchers.Main) { onLongPress() }
if (longPressResult) {
pressedKeyInfo.blockUp = true
} else if (repeatableKeyCodes.contains(data.code)) {
val repeatData = determineRepeatData(data)
val repeatDelay = determineRepeatDelay(repeatData)
while (isActive) {
if (onRepeat()) {
val onRepeatResult = withContext(Dispatchers.Main) { onRepeat() }
if (onRepeatResult) {
keyEventReceiver?.onInputKeyRepeat(repeatData)
pressedKeyInfo.blockUp = true
}
@@ -112,15 +123,18 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
}
}
}
return@withLock true
pressedKeys[data.code] = pressedKeyInfo
return@withLock pressedKeyInfo
}
if (result) {
if (result != null) {
keyEventReceiver?.onInputKeyDown(data)
lastKeyEventDown = EventData(System.currentTimeMillis(), data)
lastKeyEventDown = EventData(eventTime, data)
}
result
}
fun sendUp(data: KeyData) = runBlocking {
flogDebug { data.toString() }
val (result, isBlocked) = pressedKeys.withLock { pressedKeys ->
if (pressedKeys.containsKey(data.code)) {
val pressedKeyInfo = pressedKeys.removeAndReturn(data.code)?.also { it.cancelJobs() }
@@ -131,7 +145,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
if (result) {
if (!isBlocked) {
keyEventReceiver?.onInputKeyUp(data)
lastKeyEventUp = EventData(System.currentTimeMillis(), data)
lastKeyEventUp = EventData(SystemClock.uptimeMillis(), data)
} else {
keyEventReceiver?.onInputKeyCancel(data)
}
@@ -139,10 +153,11 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
}
fun sendDownUp(data: KeyData) = runBlocking {
flogDebug { data.toString() }
pressedKeys.withLock { pressedKeys ->
pressedKeys.removeAndReturn(data.code)?.also { it.cancelJobs() }
}
val eventData = EventData(System.currentTimeMillis(), data)
val eventData = EventData(SystemClock.uptimeMillis(), data)
keyEventReceiver?.onInputKeyDown(data)
lastKeyEventDown = eventData
keyEventReceiver?.onInputKeyUp(data)
@@ -150,6 +165,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
}
fun sendCancel(data: KeyData) = runBlocking {
flogDebug { data.toString() }
val result = pressedKeys.withLock { pressedKeys ->
if (pressedKeys.containsKey(data.code)) {
pressedKeys.removeAndReturn(data.code)?.also { it.cancelJobs() }
@@ -173,14 +189,22 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
pressedKeys.withLock { it.containsKey(code) }
}
fun isConsecutiveDown(data: KeyData, maxTimeDiff: Long): Boolean {
val event = lastKeyEventDown ?: return false
return event.data.code == data.code && (System.currentTimeMillis() - event.time) < maxTimeDiff
fun isAnyPressed(): Boolean = runBlocking {
pressedKeys.withLock { it.isNotEmpty() }
}
fun isConsecutiveUp(data: KeyData, maxTimeDiff: Long): Boolean {
val event = lastKeyEventUp ?: return false
return event.data.code == data.code && (System.currentTimeMillis() - event.time) < maxTimeDiff
fun isConsecutiveDown(data: KeyData): Boolean {
val event = lastKeyEventDown
return event.data.code == data.code && (SystemClock.uptimeMillis() - event.time) < DoubleTapTimeout
}
fun isConsecutiveUp(data: KeyData): Boolean {
val event = lastKeyEventUp
return event.data.code == data.code && (SystemClock.uptimeMillis() - event.time) < DoubleTapTimeout
}
fun isUninterruptedEventSequence(data: KeyData): Boolean {
return lastKeyEventDown.data.code == data.code
}
/**
@@ -191,7 +215,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
scope.cancel()
}
private data class PressedKeyInfo(
data class PressedKeyInfo(
val eventTimeDown: Long,
var job: Job? = null,
var blockUp: Boolean = false,
@@ -203,7 +227,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
data class EventData(
val time: Long,
val data: KeyData
val data: KeyData,
)
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.keyboard
package dev.patrickgold.florisboard.ime.input
import android.content.Context
import android.inputmethodservice.InputMethodService
@@ -29,6 +29,7 @@ import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.lib.android.AndroidVersion

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.input
/**
* Enum for the input shift states of a text keyboard.
*/
enum class InputShiftState(val value: Int) {
/**
* The default input mode, no shift modifier is active.
*/
UNSHIFTED(0),
/**
* Shift is active, but resets to [UNSHIFTED] after a single input. Symbol rows are shifted.
* Indicates that this shift was manually activated, e.g. by pressing the shift key.
*/
SHIFTED_MANUAL(1),
/**
* Shift is active, but resets to [UNSHIFTED] after a single input. Symbol rows are not shifted.
* Indicates that this shift was automatically activated through the auto-capitalization feature.
*/
SHIFTED_AUTOMATIC(2),
/**
* Caps lock is active and persists after input. Symbol rows are not shifted.
*/
CAPS_LOCK(3);
companion object {
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: UNSHIFTED
}
fun toInt() = value
}

View File

@@ -22,7 +22,7 @@ import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
import dev.patrickgold.florisboard.ime.editor.ImeOptions
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
@@ -191,7 +191,7 @@ fun ComputingEvaluator.computeIconResId(data: KeyData): Int? {
R.drawable.ic_settings
}
KeyCode.SHIFT -> {
when (evaluator.activeState().inputMode != InputMode.NORMAL) {
when (evaluator.activeState().inputShiftState != InputShiftState.UNSHIFTED) {
true -> R.drawable.ic_keyboard_capslock
else -> R.drawable.ic_keyboard_arrow_up
}

View File

@@ -28,6 +28,19 @@ class CurrencySet(
private val slots: List<TextKeyData>
) {
companion object {
val Fallback = CurrencySet(
id = "fallback",
label = "Fallback",
slots = listOf(
TextKeyData(code = 36, label = "$"),
TextKeyData(code = 162, label = "¢"),
TextKeyData(code = 8364, label = ""),
TextKeyData(code = 163, label = "£"),
TextKeyData(code = 165, label = "¥"),
TextKeyData(code = 8369, label = "")
)
)
fun isCurrencySlot(keyCode: Int): Boolean {
return when (keyCode) {
KeyCode.CURRENCY_SLOT_1,
@@ -39,19 +52,6 @@ class CurrencySet(
else -> false
}
}
fun default(): CurrencySet = CurrencySet(
id = "default",
label = "Default",
slots = listOf(
TextKeyData(code = 36, label = "$"),
TextKeyData(code = 162, label = "¢"),
TextKeyData(code = 8364, label = ""),
TextKeyData(code = 163, label = "£"),
TextKeyData(code = 165, label = "¥"),
TextKeyData(code = 8369, label = "")
)
)
}
fun getSlot(keyCode: Int): TextKeyData? {

View File

@@ -17,6 +17,7 @@
package dev.patrickgold.florisboard.ime.keyboard
import androidx.compose.ui.unit.LayoutDirection
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.popup.PopupSet
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
@@ -137,6 +138,56 @@ class CaseSelector(
}
}
/**
* Allows to select an [AbstractKeyData] based on the current shift state. Note that this type of selector only really
* makes sense in a text context, though technically speaking it can be used anywhere, so this implementation allows
* for any [AbstractKeyData] to be used here. The JSON class identifier for this selector is `shift_state_selector`.
*
* Example usage in a layout JSON file:
* ```
* { "$": "shift_state_selector",
* "shiftedManual": { "code": 59, "label": ";" },
* "default": { "code": 58, "label": ":" }
* }
* ```
*
* @property unshifted The key data to use if the current shift state is [InputShiftState.UNSHIFTED], falling back to
* [default] if unspecified.
* @property shifted The key data to use if the current shift state is either [InputShiftState.SHIFTED_MANUAL] or
* [InputShiftState.SHIFTED_AUTOMATIC]. Is overridden if [shiftedManual] or [shiftedAutomatic] is specified.
* @property shiftedManual The key data to use if the current shift state is [InputShiftState.SHIFTED_MANUAL],
* falling back to [shifted] or [default] if unspecified.
* @property shiftedAutomatic The key data to use if the current shift state is [InputShiftState.SHIFTED_AUTOMATIC],
* falling back to [shifted] or [default] if unspecified.
* @property capsLock The key data to use if the current shift state is [InputShiftState.CAPS_LOCK], falling back to
* [default] if unspecified.
* @property default The key data to use if the current shift state is set to a value not specified by this selector.
* If a key data is provided for all shift states possible this key data will never be used.
*/
@Serializable
@SerialName("shift_state_selector")
class ShiftStateSelector(
val unshifted: AbstractKeyData? = null,
val shifted: AbstractKeyData? = null,
val shiftedManual: AbstractKeyData? = null,
val shiftedAutomatic: AbstractKeyData? = null,
val capsLock: AbstractKeyData? = null,
val default: AbstractKeyData? = null,
) : AbstractKeyData {
override fun compute(evaluator: ComputingEvaluator): KeyData? {
return when (evaluator.activeState().inputShiftState) {
InputShiftState.UNSHIFTED -> unshifted ?: default
InputShiftState.SHIFTED_MANUAL -> shiftedManual ?: shifted ?: default
InputShiftState.SHIFTED_AUTOMATIC -> shiftedAutomatic ?: shifted ?: default
InputShiftState.CAPS_LOCK -> capsLock ?: default
}?.compute(evaluator)
}
override fun asString(isForDisplay: Boolean): String {
return ""
}
}
/**
* Allows to select an [AbstractKeyData] based on the current variation. Note that this type of selector only really
* makes sense in a text context, though technically speaking it can be used anywhere, so this implementation allows
@@ -195,7 +246,7 @@ data class VariationSelector(
*
* Example usage in a layout JSON file:
* ```
* { "$": "case_selector",
* { "$": "layout_direction_selector",
* "ltr": { "code": 59, "label": ";" },
* "rtl": { "code": 58, "label": ":" }
* }

View File

@@ -17,6 +17,7 @@
package dev.patrickgold.florisboard.ime.keyboard
import dev.patrickgold.florisboard.ime.core.SubtypePreset
import dev.patrickgold.florisboard.ime.nlp.PunctuationRule
import dev.patrickgold.florisboard.ime.popup.PopupMappingComponent
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.lib.ext.Extension
@@ -35,6 +36,7 @@ data class KeyboardExtension(
val composers: List<Composer> = listOf(),
val currencySets: List<CurrencySet> = listOf(),
val layouts: Map<String, List<LayoutArrangementComponent>> = mapOf(),
val punctuationRules: List<PunctuationRule> = listOf(),
val popupMappings: List<PopupMappingComponent> = listOf(),
val subtypePresets: List<SubtypePreset> = listOf(),
) : Extension() {
@@ -78,6 +80,14 @@ inline fun extCoreLayout(id: String): ExtensionComponentName {
)
}
@Suppress("NOTHING_TO_INLINE")
inline fun extCorePunctuationRule(id: String): ExtensionComponentName {
return ExtensionComponentName(
extensionId = "org.florisboard.localization",
componentId = id,
)
}
@Suppress("NOTHING_TO_INLINE")
inline fun extCorePopupMapping(id: String): ExtensionComponentName {
return ExtensionComponentName(

View File

@@ -33,19 +33,20 @@ import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.glideTypingManager
import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.InputEventDispatcher
import dev.patrickgold.florisboard.ime.core.InputKeyEventReceiver
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.core.SubtypePreset
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
import dev.patrickgold.florisboard.ime.editor.ImeOptions
import dev.patrickgold.florisboard.ime.editor.InputAttributes
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
import dev.patrickgold.florisboard.ime.input.InputKeyEventReceiver
import dev.patrickgold.florisboard.ime.nlp.NlpManager
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.popup.PopupMappingComponent
import dev.patrickgold.florisboard.ime.text.composing.Composer
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.nlp.PunctuationRule
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
@@ -59,6 +60,7 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
import dev.patrickgold.florisboard.nlpManager
import dev.patrickgold.florisboard.subtypeManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -66,6 +68,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
data class RenderInfo(
val version: Int = 0,
@@ -84,6 +87,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
private val editorInstance by context.editorInstance()
private val extensionManager by context.extensionManager()
private val glideTypingManager by context.glideTypingManager()
private val nlpManager by context.nlpManager()
private val subtypeManager by context.subtypeManager()
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
@@ -142,6 +146,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
}
subtypeManager.activeSubtype.observeForever { newSubtype ->
updateRenderInfo()
updateCapsState()
if (prefs.glide.enabled.get()) {
glideTypingManager.setWordData(newSubtype)
}
@@ -149,6 +154,18 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
clipboardManager.primaryClip.observeForever {
updateRenderInfo()
}
scope.launch {
withContext(Dispatchers.Main) {
nlpManager.clearSuggestions()
}
editorInstance.activeContentFlow.collect { content ->
if (content.composing.isNotValid || !activeState.isComposingEnabled) {
nlpManager.clearSuggestions()
return@collect
}
nlpManager.suggest(content.composingText, listOf())
}
}
}
private fun updateRenderInfo(action: () -> Unit = { }) = scope.launch {
@@ -216,12 +233,13 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
}
fun updateCapsState() {
if (activeState.inputMode != InputMode.CAPS_LOCK) {
val shift = prefs.correction.autoCapitalization.get() &&
editorInstance.activeCursorCapsMode != InputAttributes.CapsMode.NONE
activeState.inputMode = when {
shift -> InputMode.SHIFT_LOCK
else -> InputMode.NORMAL
if (activeState.inputShiftState != InputShiftState.CAPS_LOCK && !inputEventDispatcher.isPressed(KeyCode.SHIFT)) {
val shift = prefs.correction.autoCapitalization.get()
&& subtypeManager.activeSubtype().primaryLocale.supportsCapitalization
&& editorInstance.activeCursorCapsMode != InputAttributes.CapsMode.NONE
activeState.inputShiftState = when {
shift -> InputShiftState.SHIFTED_AUTOMATIC
else -> InputShiftState.UNSHIFTED
}
}
}
@@ -299,9 +317,19 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
* otherwise , abc -> abc
*/
fun fixCase(word: String): String {
return when(activeState.inputMode) {
InputMode.CAPS_LOCK -> word.uppercase(subtypeManager.activeSubtype().primaryLocale.base)
InputMode.SHIFT_LOCK -> word.replaceFirstChar { if (it.isLowerCase()) it.titlecase(subtypeManager.activeSubtype().primaryLocale.base) else it.toString() }
return when(activeState.inputShiftState) {
InputShiftState.CAPS_LOCK -> {
word.uppercase(subtypeManager.activeSubtype().primaryLocale.base)
}
InputShiftState.SHIFTED_MANUAL, InputShiftState.SHIFTED_AUTOMATIC -> {
word.replaceFirstChar {
if (it.isLowerCase()) {
it.titlecase(subtypeManager.activeSubtype().primaryLocale.base)
} else {
it.toString()
}
}
}
else -> word
}
}
@@ -453,13 +481,13 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
* Handles a [KeyCode.SHIFT] down event.
*/
private fun handleShiftDown(data: KeyData) {
if (inputEventDispatcher.isConsecutiveDown(data, prefs.keyboard.longPressDelay.get().toLong())) {
activeState.inputMode = InputMode.CAPS_LOCK
if (inputEventDispatcher.isConsecutiveDown(data)) {
activeState.inputShiftState = InputShiftState.CAPS_LOCK
} else {
if (activeState.inputMode == InputMode.NORMAL) {
activeState.inputMode = InputMode.SHIFT_LOCK
if (activeState.inputShiftState == InputShiftState.UNSHIFTED) {
activeState.inputShiftState = InputShiftState.SHIFTED_MANUAL
} else {
activeState.inputMode = InputMode.NORMAL
activeState.inputShiftState = InputShiftState.UNSHIFTED
}
}
}
@@ -467,22 +495,25 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
/**
* Handles a [KeyCode.SHIFT] up event.
*/
private fun handleShiftUp() {
//activeState.shiftLock = newCapsState
private fun handleShiftUp(data: KeyData) {
if (activeState.inputShiftState != InputShiftState.CAPS_LOCK && !inputEventDispatcher.isAnyPressed() &&
!inputEventDispatcher.isUninterruptedEventSequence(data)) {
activeState.inputShiftState = InputShiftState.UNSHIFTED
}
}
/**
* Handles a [KeyCode.CAPS_LOCK] event.
*/
private fun handleCapsLock() {
activeState.inputMode = InputMode.CAPS_LOCK
activeState.inputShiftState = InputShiftState.CAPS_LOCK
}
/**
* Handles a [KeyCode.SHIFT] cancel event.
*/
private fun handleShiftCancel() {
activeState.inputMode = InputMode.NORMAL
activeState.inputShiftState = InputShiftState.UNSHIFTED
}
/**
@@ -501,7 +532,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
}
}
if (prefs.correction.doubleSpacePeriod.get()) {
if (inputEventDispatcher.isConsecutiveUp(data, prefs.keyboard.longPressDelay.get().toLong())) {
if (inputEventDispatcher.isConsecutiveUp(data)) {
val text = editorInstance.run { activeContent.getTextBeforeCursor(2) }
if (text.length == 2 && DoubleSpacePeriodMatcher.matches(text)) {
editorInstance.deleteBackwards()
@@ -640,7 +671,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
KeyCode.LANGUAGE_SWITCH -> handleLanguageSwitch()
KeyCode.REDO -> editorInstance.performRedo()
KeyCode.SETTINGS -> FlorisImeService.launchSettings()
KeyCode.SHIFT -> handleShiftUp()
KeyCode.SHIFT -> handleShiftUp(data)
KeyCode.SPACE -> handleSpace(data)
KeyCode.SYSTEM_INPUT_METHOD_PICKER -> InputMethodUtils.showImePicker(appContext)
KeyCode.SYSTEM_PREV_INPUT_METHOD -> FlorisImeService.switchToPrevInputMethod()
@@ -682,15 +713,15 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
else -> when (data.type) {
KeyType.CHARACTER, KeyType.NUMERIC ->{
val text = data.asString(isForDisplay = false)
editorInstance.commitText(text)
editorInstance.commitChar(text)
}
else -> {
flogError(LogTopic.KEY_EVENTS) { "Received unknown key: $data" }
}
}
}
if (activeState.inputMode != InputMode.CAPS_LOCK) {
activeState.inputMode = InputMode.NORMAL
if (activeState.inputShiftState != InputShiftState.CAPS_LOCK && !inputEventDispatcher.isPressed(KeyCode.SHIFT)) {
activeState.inputShiftState = InputShiftState.UNSHIFTED
}
}
}
@@ -732,6 +763,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
val currencySets = MutableLiveData<Map<ExtensionComponentName, CurrencySet>>(emptyMap())
val layouts = MutableLiveData<Map<LayoutType, Map<ExtensionComponentName, LayoutArrangementComponent>>>(emptyMap())
val popupMappings = MutableLiveData<Map<ExtensionComponentName, PopupMappingComponent>>(emptyMap())
val punctuationRules = MutableLiveData<Map<ExtensionComponentName, PunctuationRule>>(emptyMap())
val subtypePresets = MutableLiveData<List<SubtypePreset>>(emptyList())
val anyChanged = MutableLiveData(Unit)
@@ -747,6 +779,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
val localCurrencySets = mutableMapOf<ExtensionComponentName, CurrencySet>()
val localLayouts = mutableMapOf<LayoutType, MutableMap<ExtensionComponentName, LayoutArrangementComponent>>()
val localPopupMappings = mutableMapOf<ExtensionComponentName, PopupMappingComponent>()
val localPunctuationRules = mutableMapOf<ExtensionComponentName, PunctuationRule>()
val localSubtypePresets = mutableListOf<SubtypePreset>()
for (layoutType in LayoutType.values()) {
localLayouts[layoutType] = mutableMapOf()
@@ -766,6 +799,9 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
keyboardExtension.popupMappings.forEach { popupMapping ->
localPopupMappings[ExtensionComponentName(keyboardExtension.meta.id, popupMapping.id)] = popupMapping
}
keyboardExtension.punctuationRules.forEach { punctuationRule ->
localPunctuationRules[ExtensionComponentName(keyboardExtension.meta.id, punctuationRule.id)] = punctuationRule
}
localSubtypePresets.addAll(keyboardExtension.subtypePresets)
}
localSubtypePresets.sortBy { it.locale.displayName() }
@@ -780,6 +816,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
currencySets.postValue(localCurrencySets)
layouts.postValue(localLayouts)
popupMappings.postValue(localPopupMappings)
punctuationRules.postValue(localPunctuationRules)
anyChanged.postValue(Unit)
}
}

View File

@@ -21,7 +21,7 @@ package dev.patrickgold.florisboard.ime.keyboard
import androidx.compose.ui.unit.LayoutDirection
import androidx.lifecycle.LiveData
import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
import dev.patrickgold.florisboard.lib.devtools.flogError
import java.util.concurrent.atomic.AtomicInteger
@@ -42,8 +42,7 @@ import kotlin.properties.Delegates
* ---------|----------|----------|----------|---------------------------------
* | | | 1111 | Active [KeyboardMode]
* | | | 1111 | Active [KeyVariation]
* | | 1 | | Shift lock flag (shift and caps combined is InputMode id)
* | | 1 | | Caps lock flag
* | | 11 | | InputShiftState
* | | 1 | | Is selection active (length > 0)
* | | 1 | | Is manual selection mode
* | | 1 | | Is manual selection mode (start)
@@ -70,8 +69,8 @@ class KeyboardState private constructor(initValue: ULong) : LiveData<KeyboardSta
const val O_KEYBOARD_MODE: Int = 0
const val M_KEY_VARIATION: ULong = 0x0Fu
const val O_KEY_VARIATION: Int = 4
const val M_INPUT_MODE: ULong = 0x03u
const val O_INPUT_MODE: Int = 8
const val M_INPUT_SHIFT_STATE: ULong = 0x03u
const val O_INPUT_SHIFT_STATE: Int = 8
const val M_IME_UI_MODE: ULong = 0x07u
const val O_IME_UI_MODE: Int = 24
@@ -231,9 +230,9 @@ class KeyboardState private constructor(initValue: ULong) : LiveData<KeyboardSta
get() = KeyboardMode.fromInt(getRegion(M_KEYBOARD_MODE, O_KEYBOARD_MODE))
set(v) { setRegion(M_KEYBOARD_MODE, O_KEYBOARD_MODE, v.toInt()) }
var inputMode: InputMode
get() = InputMode.fromInt(getRegion(M_INPUT_MODE, O_INPUT_MODE))
set(v) { setRegion(M_INPUT_MODE, O_INPUT_MODE, v.toInt()) }
var inputShiftState: InputShiftState
get() = InputShiftState.fromInt(getRegion(M_INPUT_SHIFT_STATE, O_INPUT_SHIFT_STATE))
set(v) { setRegion(M_INPUT_SHIFT_STATE, O_INPUT_SHIFT_STATE, v.toInt()) }
var imeUiMode: ImeUiMode
get() = ImeUiMode.fromInt(getRegion(M_IME_UI_MODE, O_IME_UI_MODE))
@@ -244,10 +243,10 @@ class KeyboardState private constructor(initValue: ULong) : LiveData<KeyboardSta
set(v) { setFlag(F_IS_RTL_LAYOUT_DIRECTION, v == LayoutDirection.Rtl) }
val isLowercase: Boolean
get() = inputMode == InputMode.NORMAL
get() = inputShiftState == InputShiftState.UNSHIFTED
val isUppercase: Boolean
get() = inputMode != InputMode.NORMAL
get() = inputShiftState != InputShiftState.UNSHIFTED
var isSelectionMode: Boolean
get() = getFlag(F_IS_SELECTION_MODE)

View File

@@ -16,6 +16,7 @@
package dev.patrickgold.florisboard.ime.media
import android.annotation.SuppressLint
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -31,6 +32,7 @@ import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -46,10 +48,12 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.core.InputEventDispatcher
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.media.emoji.EmojiPaletteView
import dev.patrickgold.florisboard.ime.media.emoji.PlaceholderLayoutDataMap
import dev.patrickgold.florisboard.ime.media.emoji.parseRawEmojiSpecsFile
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
@@ -58,6 +62,7 @@ import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.snygg.ui.SnyggSurface
import kotlinx.coroutines.coroutineScope
@SuppressLint("MutableCollectionMutableState")
@Composable
fun MediaInputLayout(
modifier: Modifier = Modifier,
@@ -65,6 +70,11 @@ fun MediaInputLayout(
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
var emojiLayoutDataMap by remember { mutableStateOf(PlaceholderLayoutDataMap) }
LaunchedEffect(Unit) {
emojiLayoutDataMap = parseRawEmojiSpecsFile(context, "ime/media/emoji/root.txt")
}
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Column(
modifier = modifier
@@ -73,7 +83,7 @@ fun MediaInputLayout(
) {
EmojiPaletteView(
modifier = Modifier.weight(1f),
fullEmojiMappings = parseRawEmojiSpecsFile(context, "ime/media/emoji/root.txt"),
fullEmojiMappings = emojiLayoutDataMap,
)
Row(
modifier = Modifier
@@ -108,6 +118,7 @@ internal fun KeyboardLikeButton(
keyData: KeyData,
content: @Composable RowScope.() -> Unit,
) {
val inputFeedbackController = LocalInputFeedbackController.current
var isPressed by remember { mutableStateOf(false) }
val keyStyle = FlorisImeTheme.style.get(
element = FlorisImeUi.EmojiKey,
@@ -122,6 +133,7 @@ internal fun KeyboardLikeButton(
awaitFirstDown(requireUnconsumed = false).also { it.consumeDownChange() }
isPressed = true
inputEventDispatcher.sendDown(keyData)
inputFeedbackController.keyPress(keyData)
val up = waitForUpOrCancellation()
isPressed = false
if (up != null) {

View File

@@ -26,6 +26,12 @@ import java.util.*
*/
typealias EmojiLayoutDataMap = EnumMap<EmojiCategory, MutableList<EmojiSet>>
val PlaceholderLayoutDataMap = EmojiLayoutDataMap(EmojiCategory::class.java).also { map ->
for (category in EmojiCategory.values()) {
map[category] = mutableListOf()
}
}
private var cachedEmojiLayoutMap: EmojiLayoutDataMap? = null
/**

View File

@@ -25,6 +25,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -79,7 +80,9 @@ import com.google.accompanist.flowlayout.FlowRow
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
@@ -87,7 +90,6 @@ import dev.patrickgold.florisboard.lib.android.showShortToast
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.safeTimes
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.kotlin.tryOrNull
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBackground
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBorder
import dev.patrickgold.florisboard.lib.snygg.ui.snyggShadow
@@ -133,8 +135,9 @@ fun EmojiPaletteView(
}
}
val metadataVersion = activeEditorInfo.emojiCompatMetadataVersion
val emojiCompatInstance = tryOrNull { EmojiCompat.get().takeIf { it.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED } }
val emojiMappings = remember(emojiCompatInstance, metadataVersion, systemFontPaint) {
val replaceAll = activeEditorInfo.emojiCompatReplaceAll
val emojiCompatInstance by FlorisEmojiCompat.getAsFlow(replaceAll).collectAsState()
val emojiMappings = remember(emojiCompatInstance, fullEmojiMappings, metadataVersion, systemFontPaint) {
fullEmojiMappings.mapValues { (_, emojiSetList) ->
emojiSetList.mapNotNull { emojiSet ->
emojiSet.emojis.filter { emoji ->
@@ -248,6 +251,7 @@ private fun EmojiCategoriesTabRow(
activeCategory: EmojiCategory,
onCategoryChange: (EmojiCategory) -> Unit,
) {
val inputFeedbackController = LocalInputFeedbackController.current
val tabStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiTab)
val tabStyleFocused = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiTab, isFocus = true)
val unselectedContentColor = tabStyle.foreground.solidColor(default = FlorisImeTheme.fallbackContentColor())
@@ -273,7 +277,10 @@ private fun EmojiCategoriesTabRow(
) {
for (category in EmojiCategoryValues) {
Tab(
onClick = { onCategoryChange(category) },
onClick = {
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
onCategoryChange(category)
},
selected = activeCategory == category,
icon = { Icon(
modifier = Modifier.size(ButtonDefaults.IconSize),
@@ -298,19 +305,24 @@ private fun EmojiKey(
onEmojiInput: (Emoji) -> Unit,
onLongPress: (Emoji) -> Unit,
) {
val inputFeedbackController = LocalInputFeedbackController.current
val base = emojiSet.base(withSkinTone = preferredSkinTone)
val variations = emojiSet.variations(withoutSkinTone = preferredSkinTone)
var showVariantsBox by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.height(FlorisImeSizing.smartbarHeight)
.aspectRatio(1f)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
},
onTap = {
onEmojiInput(base)
},
onLongPress = {
inputFeedbackController.keyLongPress(TextKeyData.UNSPECIFIED)
onLongPress(base)
if (variations.isNotEmpty()) {
showVariantsBox = true

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.media.emoji
import android.annotation.SuppressLint
import android.content.Context
import androidx.emoji2.text.DefaultEmojiCompatConfig
import androidx.emoji2.text.EmojiCompat
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.devtools.flogInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
/**
* Helper object which manages two separate EmojiCompat instances, something EmojiCompat by default does not want us
* to do for unknown reasons. Additionally we implement a proper loaded callback and a state flow, so the UI can always
* receive the EmojiCompat instance as soon as it is loaded. This helper still uses the default config and thus relies
* either on a system font with emoji or Google GMS services with their downloadable font provider.
*
* TODO: investigate how AOSP-like ROMs without any GMS services installed handle backwards emoji compatibility. Same
* goes for newer Huawei devices, which are subjected to no Google services. (Probably these devices rely on the good
* old method of just querying the system painter, which we already use as a fallback in the palette logic).
*
* TODO: investigate if having two instances of EmojiCompat has significant memory impact. Based on the docs one
* instance has ~300kB, so two should have ~600kB, which should not cause issues.
*
* TODO: investigate if having two instances of EmojiCompat causes other logic issues or if there's a better way of
* achieving the same result than the current implementation does.
*/
object FlorisEmojiCompat {
private lateinit var instanceNoReplace: InstanceHandler
private lateinit var instanceReplaceAll: InstanceHandler
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
/**
* Initialize this helper and its EmojiCompat instances with given [context]. Immediately begins loading the emoji
* metadata in a background thread. After this method has been called, it is safe to call [getAsFlow].
*/
fun init(context: Context) {
instanceNoReplace = InstanceHandler(context, replaceAll = false)
instanceReplaceAll = InstanceHandler(context, replaceAll = true)
scope.launch {
instanceNoReplace.load()
}
scope.launch {
instanceReplaceAll.load()
}
}
/**
* Gets the current EmojiCompat instance based on [replaceAll] and sets it as the default instance if
* [setAsDefaultInstance] is true. Calling this method before [init] will cause an exception to be thrown.
*
* @return A state flow providing the latest EmojiCompat instance for given args. The flow may provide null if
* EmojiCompat is still loading or if it has failed.
*/
@SuppressLint("RestrictedApi")
fun getAsFlow(replaceAll: Boolean, setAsDefaultInstance: Boolean = true): StateFlow<EmojiCompat?> {
val instanceFlow = if (replaceAll) {
instanceReplaceAll.publishedInstanceFlow
} else {
instanceNoReplace.publishedInstanceFlow
}
val instance = instanceFlow.value
if (setAsDefaultInstance && instance != null) {
flogInfo { "Set default EmojiCompat instance to $instance(replaceAll=$replaceAll)" }
// This API is not really supposed to be used by third-party apps, but it is really handy and does
// exactly what we need, so we suppress the restriction here
EmojiCompat.reset(instance)
}
return instanceFlow
}
private class InstanceHandler(context: Context, replaceAll: Boolean = false) {
private val initCallback: EmojiCompat.InitCallback = object : EmojiCompat.InitCallback() {
override fun onInitialized() {
super.onInitialized()
flogInfo { "EmojiCompat(replaceAll=$replaceAll) successfully loaded!" }
publishedInstanceFlow.value = instance
}
override fun onFailed(throwable: Throwable?) {
super.onFailed(throwable)
flogError { "EmojiCompat(replaceAll=$replaceAll) failed to load: $throwable" }
}
}
private val config: EmojiCompat.Config? = DefaultEmojiCompatConfig.create(context)?.apply {
setReplaceAll(replaceAll)
setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL)
registerInitCallback(initCallback)
}
// Despite its name, `EmojiCompat.reset()` actually creates a new instance, exactly what we need
private val instance: EmojiCompat? = if (config != null) EmojiCompat.reset(config) else null
val publishedInstanceFlow = MutableStateFlow<EmojiCompat?>(null)
/**
* Manually loads the EmojiCompat instance. Call this method on a background thread to avoid blocking main.
*
* @see EmojiCompat.load
*/
fun load() {
instance?.load()
}
}
}

View File

@@ -28,13 +28,18 @@ import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.subtypeManager
import java.util.*
class NlpManager(context: Context) {
private val prefs by florisPreferenceModel()
private val clipboardManager by context.clipboardManager()
private val editorInstance by context.editorInstance()
private val keyboardManager by context.keyboardManager()
private val subtypeManager by context.subtypeManager()
private val _suggestions = MutableLiveData<SuggestionList2?>(null)
val suggestions: LiveData<SuggestionList2?> get() = _suggestions
@@ -58,6 +63,27 @@ class NlpManager(context: Context) {
}
}
/**
* Gets the punctuation rule from the currently active subtype and returns it. Falls back to a default one if the
* subtype does not exist or defines an invalid punctuation rule.
*
* @return The punctuation rule or a fallback.
*/
fun getActivePunctuationRule(): PunctuationRule {
return getPunctuationRule(subtypeManager.activeSubtype())
}
/**
* Gets the punctuation rule from the given subtype and returns it. Falls back to a default one if the subtype does
* not exist or defines an invalid punctuation rule.
*
* @return The punctuation rule or a fallback.
*/
fun getPunctuationRule(subtype: Subtype): PunctuationRule {
return keyboardManager.resources.punctuationRules.value
?.get(subtype.punctuationRule) ?: PunctuationRule.Fallback
}
fun suggest(
currentWord: String,
precedingWords: List<String>,

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.nlp
import dev.patrickgold.florisboard.lib.ext.ExtensionComponent
import dev.patrickgold.florisboard.lib.kotlin.RegexSerializer
import kotlinx.serialization.Serializable
/**
* Data class which describes a punctuation rule for auto-spacing and phantom-space determination. Punctuation rules
* are defined in keyboard extension's manifest file, there can be multiple ones defined, if necessary.
*
* Here's an example of a properly configured punctuation rule in a keyboard extension:
*
* ```json
* "punctuationRules": [
* {
* "id": "default",
* "label": "Default",
* "symbolsPrecedingSpace": ".*[.,;:!?‽&%)\\]}»©®™\\p{L}0-9]",
* "symbolsFollowingSpace": "[\\p{L}0-9].*"
* }
* ]
* ```
*
* For auto-spacing and phantom-space to consider inserting a space by itself, both [symbolsPrecedingSpace] (which gets
* a small subset of the text before the cursor) and [symbolsFollowingSpace] (which gets the character or word to
* insert) must match the entire string they get.
*
* @property id The ID of this punctuation rule. Can be any ID as long as it follows the ID syntax rule, conventionally
* though this is either "default" or the language tag of the locale this punctuation rule is meant for.
* @property label The label of this punctuation rule, for showing this rule in the UI. Defaults to [id] if not set.
* @property authors List of authors, not relevant for punctuation rules though. Can and should be omitted.
* @property symbolsPrecedingSpace Regex which checks if the text before the cursor indicates that a space should
* follow. This can be a simple regex, which matches a list of single symbols, or a more complex one, which matches
* symbols based on previous characters. This regex must match the entire text, partial matches are not allowed.
* This means that the regex should begin with a match all expression (`.*`), to match preceding unrelated symbols and
* characters.
* @property symbolsFollowingSpace Regex which checks if the text to insert after the cursor indicates that a space
* should precede. This can be a simple regex, which matches a list of single symbols, or a more complex one, which
* matches symbols based on next characters. This regex must match the entire text, partial matches are not allowed.
* This means that the regex should end with a match all expression (`.*`), to match following unrelated symbols and
* characters.
*/
@Serializable
data class PunctuationRule(
override val id: String,
override val label: String = id,
override val authors: List<String> = listOf("unspecified"),
val symbolsPrecedingSpace: @Serializable(with = RegexSerializer::class) Regex,
val symbolsFollowingSpace: @Serializable(with = RegexSerializer::class) Regex,
) : ExtensionComponent {
companion object {
/** Fallback rule which does bare bone matching for spaces in case a proper punctuation rule is not found. */
val Fallback = PunctuationRule(
id = "fallback",
label = "Fallback",
symbolsPrecedingSpace = """[^\s]""".toRegex(),
symbolsFollowingSpace = """\p{L}[0-9]""".toRegex(),
)
}
}

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.lib.compose.stringRes

View File

@@ -17,6 +17,10 @@ interface Composer {
@Serializable
@SerialName("appender")
class Appender : Composer {
companion object {
val DefaultInstance = Appender()
}
override val id: String = "appender"
override val label: String = "Appender"
override val toRead: Int = 0

View File

@@ -5,6 +5,8 @@ import android.view.MotionEvent
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
import dev.patrickgold.florisboard.lib.devtools.flogDebug
import dev.patrickgold.florisboard.lib.util.ViewUtils
import kotlin.math.pow
import kotlin.math.sqrt
@@ -18,13 +20,13 @@ class GlideTypingGesture {
*/
class Detector(context: Context) {
private var pointerData: PointerData = PointerData(mutableListOf(), 0)
private val keySize = context.resources.getDimensionPixelSize(R.dimen.key_width).toDouble()
private val keySize = ViewUtils.px2dp(context.resources.getDimension(R.dimen.key_width))
private val listeners: ArrayList<Listener> = arrayListOf()
private var pointerId: Int = -1
companion object {
private const val MAX_DETECT_TIME = 500
private const val VELOCITY_THRESHOLD = 0.65
private const val VELOCITY_THRESHOLD = 0.10 // dp per ms
private val SWIPE_GESTURE_KEYS = arrayOf(KeyCode.DELETE, KeyCode.SHIFT, KeyCode.SPACE, KeyCode.CJK_SPACE)
}
@@ -37,13 +39,13 @@ class GlideTypingGesture {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN -> {
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
resetState()
}
if (pointerId != -1) {
// if we already have another pointer, we don't care
return false
}
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
resetState()
}
val pointerIndex = event.actionIndex
pointerId = event.getPointerId(pointerIndex)
pointerData.apply {
@@ -67,8 +69,9 @@ class GlideTypingGesture {
pointerData.positions.add(pos)
if (pointerData.isActuallyGesture == null) {
// evaluate whether is actually a gesture
val dist = pointerData.positions[0].dist(pos)
val dist = ViewUtils.px2dp(pointerData.positions[0].dist(pos))
val time = (System.currentTimeMillis() - pointerData.startTime) + 1
flogDebug { "Distance glided: $dist dp with velocity: ${dist / time} dp/ms" }
if (dist > keySize && (dist / time) > VELOCITY_THRESHOLD && (initialKey?.computedData?.code !in SWIPE_GESTURE_KEYS)) {
pointerData.isActuallyGesture = true
// Let listener know all those points need to be added.
@@ -83,8 +86,10 @@ class GlideTypingGesture {
}
if (pointerData.isActuallyGesture == true)
pointerData.positions.last().let { point -> listeners.forEach { it.onGlideAddPoint(point) } }
if (pointerData.isActuallyGesture == true) {
pointerData.positions.last()
.let { point -> listeners.forEach { it.onGlideAddPoint(point) } }
}
}
return pointerData.isActuallyGesture ?: false
}

View File

@@ -347,7 +347,7 @@ class StatisticalGlideTypingClassifier : GlideTypingClassifier {
private val cachedIdealLength = ConcurrentHashMap<String, Float>()
private fun getCachedIdealLength(word: String, idealGesture: Gesture): Float {
return cachedIdealLength.getOrPut(word, { idealGesture.getLength() })
return cachedIdealLength.getOrPut(word) { idealGesture.getLength() }
}
companion object {
@@ -416,14 +416,14 @@ class StatisticalGlideTypingClassifier : GlideTypingClassifier {
}
}
class Gesture(private val xs: FloatArray, private val ys: FloatArray, private var size: Int) {
val isEmpty: Boolean
get() = size == 0
constructor() : this(FloatArray(MAX_SIZE), FloatArray(MAX_SIZE), 0)
class Gesture(
private val xs: FloatArray = FloatArray(MAX_SIZE),
private val ys: FloatArray = FloatArray(MAX_SIZE),
private var size: Int = 0,
) {
companion object {
private const val MAX_SIZE = 300
// TODO: Find out optimal max size
private const val MAX_SIZE = 500
fun generateIdealGestures(word: String, keysByCharacter: SparseArrayCompat<TextKey>): List<Gesture> {
val idealGesture = Gesture()
@@ -497,6 +497,9 @@ class StatisticalGlideTypingClassifier : GlideTypingClassifier {
}
}
val isEmpty: Boolean
get() = size == 0
fun addPoint(x: Float, y: Float) {
if (size >= MAX_SIZE) {
return

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2020 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.ime.text.key
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
/**
* Enum for the input modes of a text keyboard.
*/
enum class InputMode(val value: Int) {
/**
* The default input mode.
*/
NORMAL(0),
/**
* Shift lock is active, but not persistent after character input. Symbol rows are shifted.
*/
SHIFT_LOCK(1),
/**
* Shift lock is active, but persistent after character input = Caps lock. Symbol rows are not shifted.
*/
CAPS_LOCK(2);
companion object {
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: NORMAL
@Composable
fun listEntries() = listPrefEntries {
entry(
key = NORMAL,
label = stringRes(R.string.enum__input_mode__normal),
)
entry(
key = SHIFT_LOCK,
label = stringRes(R.string.enum__input_mode__shift_lock),
)
entry(
key = CAPS_LOCK,
label = stringRes(R.string.enum__input_mode__caps_lock),
)
}
}
fun toInt() = value
}

View File

@@ -48,7 +48,6 @@ object KeyCode {
const val FORWARD_DELETE = -9
const val FORWARD_DELETE_WORD = -10
const val SHIFT = -11
const val SHIFT_LOCK = -12
const val CAPS_LOCK = -13
const val ARROW_LEFT = -21

View File

@@ -40,6 +40,9 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
var computedNumberHint: KeyData? = null
var computedHintData: KeyData = TextKeyData.UNSPECIFIED
// This should exclusively be set and used by the TextKeyboardLayout
var computedDataOnDown: KeyData = TextKeyData.UNSPECIFIED
fun compute(evaluator: ComputingEvaluator) {
val keyboard = evaluator.keyboard() as? TextKeyboard ?: return
val keyboardMode = keyboard.mode
@@ -255,4 +258,8 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
}
}
}
override fun toString(): String {
return computedData.toString()
}
}

View File

@@ -112,7 +112,6 @@ class TextKeyData(
FORWARD_DELETE,
FORWARD_DELETE_WORD,
SHIFT,
SHIFT_LOCK,
CAPS_LOCK,
ARROW_LEFT,
ARROW_RIGHT,
@@ -234,12 +233,6 @@ class TextKeyData(
code = KeyCode.SHIFT,
label = "shift",
)
/** Predefined key data for [KeyCode.SHIFT_LOCK] */
val SHIFT_LOCK = TextKeyData(
type = KeyType.MODIFIER,
code = KeyCode.SHIFT_LOCK,
label = "shift_lock",
)
/** Predefined key data for [KeyCode.CAPS_LOCK] */
val CAPS_LOCK = TextKeyData(
type = KeyType.MODIFIER,

View File

@@ -64,6 +64,7 @@ import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.glideTypingManager
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.keyboard.RenderInfo
@@ -75,6 +76,7 @@ import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.gestures.SwipeGesture
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
@@ -122,7 +124,9 @@ fun TextKeyboardLayout(
else -> false
}
}
val glideEnabled by prefs.glide.enabled.observeAsState()
val glideEnabledInternal by prefs.glide.enabled.observeAsState()
val glideEnabled = glideEnabledInternal && renderInfo.evaluator.activeEditorInfo().isRichInputEditor &&
renderInfo.evaluator.activeState().keyVariation != KeyVariation.PASSWORD
val glideShowTrail by prefs.glide.showTrail.observeAsState()
val glideTrailColor = FlorisImeTheme.style.get(element = FlorisImeUi.GlideTrail)
.foreground.solidColor(default = Color.Green)
@@ -186,7 +190,7 @@ fun TextKeyboardLayout(
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL
-> {
val clonedEvent = MotionEvent.obtain(event)
val clonedEvent = MotionEvent.obtainNoHistory(event)
touchEventChannel
.trySend(clonedEvent)
.onFailure {
@@ -333,7 +337,7 @@ private fun TextKeyButton(
val keyStyle = FlorisImeTheme.style.get(
element = if (isSmartbarKey) FlorisImeUi.SmartbarKey else FlorisImeUi.Key,
code = key.computedData.code,
mode = renderInfo.state.inputMode.value,
mode = renderInfo.state.inputShiftState.value,
isPressed = key.isPressed && key.isEnabled,
isDisabled = !key.isEnabled,
)
@@ -395,7 +399,7 @@ private fun TextKeyButton(
val keyHintStyle = FlorisImeTheme.style.get(
element = FlorisImeUi.KeyHint,
code = key.computedHintData.code,
mode = renderInfo.state.inputMode.value,
mode = renderInfo.state.inputShiftState.value,
isPressed = key.isPressed,
)
val hintFontSize = keyHintStyle.fontSize.spSize() safeTimes fontSizeMultiplier
@@ -462,13 +466,16 @@ private class TextKeyboardLayoutController(
lateinit var keyboard: TextKeyboard
var size = Size.Zero
val isGlideEnabled: Boolean get() = prefs.glide.enabled.get() && editorInstance.activeInfo.isRichInputEditor &&
keyboardManager.activeState.keyVariation != KeyVariation.PASSWORD
fun onTouchEventInternal(event: MotionEvent) {
flogDebug { "event=$event" }
swipeGestureDetector.onTouchEvent(event)
if (prefs.glide.enabled.get() && keyboard.mode == KeyboardMode.CHARACTERS) {
if (isGlideEnabled && keyboard.mode == KeyboardMode.CHARACTERS) {
val glidePointer = pointerMap.findById(0)
if (glideTypingDetector.onTouchEvent(event, glidePointer?.initialKey)) {
val isNotBlocked = glidePointer?.hasTriggeredLongPress != true
if (isNotBlocked && glideTypingDetector.onTouchEvent(event, glidePointer?.initialKey)) {
for (pointer in pointerMap) {
if (pointer.activeKey != null) {
onTouchCancelInternal(event, pointer)
@@ -531,8 +538,7 @@ private class TextKeyboardLayoutController(
if (swipeGestureDetector.onTouchMove(event, pointer, alwaysTriggerOnMove) || pointer.hasTriggeredGestureMove) {
pointer.hasTriggeredGestureMove = true
pointer.activeKey?.let { activeKey ->
activeKey.isPressed = false
inputEventDispatcher.sendCancel(activeKey.computedData)
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
}
} else {
onTouchMoveInternal(event, pointer)
@@ -602,9 +608,11 @@ private class TextKeyboardLayoutController(
val key = keyboard.getKeyForPos(event.getX(pointer.index), event.getY(pointer.index))
if (key != null && key.isEnabled) {
inputEventDispatcher.sendDown(
key.computedDataOnDown = key.computedData
pointer.pressedKeyInfo = inputEventDispatcher.sendDown(
data = key.computedData,
onLongPress = {
onLongPress = onLongPress@ {
pointer.hasTriggeredLongPress = true
when (key.computedData.code) {
KeyCode.SPACE, KeyCode.CJK_SPACE -> {
when (prefs.gestures.spaceBarLongPress.get()) {
@@ -618,9 +626,12 @@ private class TextKeyboardLayoutController(
true
}
KeyCode.SHIFT -> {
inputEventDispatcher.sendDownUp(TextKeyData.CAPS_LOCK)
inputFeedbackController?.keyLongPress(key.computedData)
true
if (inputEventDispatcher.isUninterruptedEventSequence(key.computedData)) {
inputEventDispatcher.sendDownUp(TextKeyData.CAPS_LOCK)
inputFeedbackController?.keyLongPress(key.computedData)
}
// We always return false here to prevent blockade for the up touch event
false
}
KeyCode.LANGUAGE_SWITCH -> {
inputEventDispatcher.sendDownUp(TextKeyData.SYSTEM_INPUT_METHOD_PICKER)
@@ -685,6 +696,8 @@ private class TextKeyboardLayoutController(
private fun onTouchUpInternal(event: MotionEvent, pointer: TouchPointer) {
flogDebug(LogTopic.TEXT_KEYBOARD_VIEW) { "pointer=$pointer" }
pointer.pressedKeyInfo?.cancelJobs()
pointer.pressedKeyInfo = null
val initialKey = pointer.initialKey
val activeKey = pointer.activeKey
@@ -694,20 +707,30 @@ private class TextKeyboardLayoutController(
val retData = popupUiController.getActiveKeyData(activeKey)
if (retData != null && !pointer.hasTriggeredGestureMove) {
if (retData == activeKey.computedData) {
inputEventDispatcher.sendUp(activeKey.computedData)
if (activeKey.computedData != activeKey.computedDataOnDown) {
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
inputEventDispatcher.sendDownUp(activeKey.computedData)
} else {
inputEventDispatcher.sendUp(activeKey.computedDataOnDown)
}
} else {
inputEventDispatcher.sendCancel(activeKey.computedData)
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
inputEventDispatcher.sendDownUp(retData)
}
} else {
inputEventDispatcher.sendCancel(activeKey.computedData)
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
}
popupUiController.hide()
} else {
if (pointer.hasTriggeredGestureMove) {
inputEventDispatcher.sendCancel(activeKey.computedData)
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
} else {
inputEventDispatcher.sendUp(activeKey.computedData)
if (activeKey.computedData != activeKey.computedDataOnDown) {
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
inputEventDispatcher.sendDownUp(activeKey.computedData)
} else {
inputEventDispatcher.sendUp(activeKey.computedDataOnDown)
}
}
}
pointer.activeKey = null
@@ -717,11 +740,13 @@ private class TextKeyboardLayoutController(
private fun onTouchCancelInternal(event: MotionEvent, pointer: TouchPointer) {
flogDebug(LogTopic.TEXT_KEYBOARD_VIEW) { "pointer=$pointer" }
pointer.pressedKeyInfo?.cancelJobs()
pointer.pressedKeyInfo = null
val activeKey = pointer.activeKey
if (activeKey != null) {
activeKey.isPressed = false
inputEventDispatcher.sendCancel(activeKey.computedData)
inputEventDispatcher.sendCancel(activeKey.computedDataOnDown)
if (popupUiController.isSuitableForPopups(activeKey)) {
popupUiController.hide()
}
@@ -746,13 +771,13 @@ private class TextKeyboardLayoutController(
initialKey.computedData.code == KeyCode.SHIFT && activeKey?.computedData?.code != KeyCode.SHIFT &&
event.type == SwipeGesture.Type.TOUCH_UP -> {
activeKey?.let {
inputEventDispatcher.sendUp(popupUiController.getActiveKeyData(it) ?: it.computedData)
inputEventDispatcher.sendUp(popupUiController.getActiveKeyData(it) ?: it.computedDataOnDown)
}
inputEventDispatcher.sendCancel(TextKeyData.SHIFT)
true
}
initialKey.computedData.code > KeyCode.SPACE && !popupUiController.isShowingExtendedPopup -> when {
!prefs.glide.enabled.get() && !pointer.hasTriggeredGestureMove -> when (event.type) {
!isGlideEnabled && !pointer.hasTriggeredGestureMove -> when (event.type) {
SwipeGesture.Type.TOUCH_UP -> {
val swipeAction = when (event.direction) {
SwipeGesture.Direction.UP -> prefs.gestures.swipeUp.get()
@@ -832,10 +857,13 @@ private class TextKeyboardLayoutController(
val count = if (!pointer.hasTriggeredGestureMove) it - 1 else it
if (count > 0) {
inputFeedbackController?.gestureMovingSwipe(TextKeyData.SPACE)
// TODO: Maybe find way to integrate this into mass select?
//keyboardManager.handleArrow(KeyCode.ARROW_LEFT, count)
val selection = editorInstance.activeContent.selection
editorInstance.setSelection(selection.end - count, selection.end - count)
if (editorInstance.activeInfo.isRawInputEditor) {
keyboardManager.handleArrow(KeyCode.ARROW_LEFT, count)
} else {
// TODO: Maybe find way to integrate this into mass select?
val selection = editorInstance.activeContent.selection
editorInstance.setSelection(selection.end - count, selection.end - count)
}
}
}
true
@@ -850,10 +878,13 @@ private class TextKeyboardLayoutController(
val count = if (!pointer.hasTriggeredGestureMove) it - 1 else it
if (count > 0) {
inputFeedbackController?.gestureMovingSwipe(TextKeyData.SPACE)
// TODO: Maybe find way to integrate this into mass select?
//keyboardManager.handleArrow(KeyCode.ARROW_RIGHT, count)
val selection = editorInstance.activeContent.selection
editorInstance.setSelection(selection.end + count, selection.end + count)
if (editorInstance.activeInfo.isRawInputEditor) {
// TODO: Maybe find way to integrate this into mass select?
keyboardManager.handleArrow(KeyCode.ARROW_RIGHT, count)
} else {
val selection = editorInstance.activeContent.selection
editorInstance.setSelection(selection.end + count, selection.end + count)
}
}
}
true
@@ -909,7 +940,7 @@ private class TextKeyboardLayoutController(
}
override fun onGlideAddPoint(point: GlideTypingGesture.Detector.Position) {
if (prefs.glide.enabled.get()) {
if (isGlideEnabled) {
glideDataForDrawing.add(point to System.currentTimeMillis())
}
}
@@ -976,12 +1007,16 @@ private class TextKeyboardLayoutController(
var initialKey: TextKey? = null
var activeKey: TextKey? = null
var hasTriggeredGestureMove: Boolean = false
var hasTriggeredLongPress: Boolean = false
var pressedKeyInfo: InputEventDispatcher.PressedKeyInfo? = null
override fun reset() {
super.reset()
initialKey = null
activeKey = null
hasTriggeredGestureMove = false
hasTriggeredLongPress = false
pressedKeyInfo = null
}
override fun toString(): String {

View File

@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.ime.theme
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheet
@@ -60,7 +60,7 @@ val FlorisImeThemeBaseStyle = SnyggStylesheet {
}
FlorisImeUi.Key(
codes = listOf(KeyCode.SHIFT),
modes = listOf(InputMode.CAPS_LOCK.value),
modes = listOf(InputShiftState.CAPS_LOCK.value),
) {
foreground = rgbaColor(255, 152, 0)
}

View File

@@ -184,6 +184,16 @@ class FlorisLocale private constructor(val base: Locale) {
*/
val iso3Country: String get() = base.isO3Country
/**
* Returns true if this language has a capitalization concept, false otherwise.
* TODO: this is absolutely not exhaustive and hard-coded, find solution based on ICU or system
*/
val supportsCapitalization: Boolean
get() = when (language) {
"zh", "ko" -> false
else -> true
}
/**
* Generates the language tag for this locale in the format `xx`,
* `xx-YY` or `xx-YY-zzz` and returns it as a string.

View File

@@ -24,6 +24,7 @@ import dev.patrickgold.florisboard.ime.keyboard.CharWidthSelector
import dev.patrickgold.florisboard.ime.keyboard.KanaSelector
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.keyboard.LayoutDirectionSelector
import dev.patrickgold.florisboard.ime.keyboard.ShiftStateSelector
import dev.patrickgold.florisboard.ime.keyboard.VariationSelector
import dev.patrickgold.florisboard.ime.text.keyboard.AutoTextKeyData
import dev.patrickgold.florisboard.ime.text.keyboard.MultiTextKeyData
@@ -50,6 +51,7 @@ val DefaultJsonConfig = Json {
subclass(AutoTextKeyData::class, AutoTextKeyData.serializer())
subclass(MultiTextKeyData::class, MultiTextKeyData.serializer())
subclass(CaseSelector::class, CaseSelector.serializer())
subclass(ShiftStateSelector::class, ShiftStateSelector.serializer())
subclass(VariationSelector::class, VariationSelector.serializer())
subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer())
subclass(CharWidthSelector::class, CharWidthSelector.serializer())

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.lib.kotlin
@Throws(NoSuchElementException::class)
fun <K, V> Map<K, V>.getKeyByValue(value: V): K {
for ((k, v) in this.entries) {
if (value == v) return k
}
throw NoSuchElementException("Value $value is missing in the map.")
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2021 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.lib.kotlin
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class RegexSerializer : KSerializer<Regex> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Regex", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Regex) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Regex {
return decoder.decodeString().toRegex()
}
}

View File

@@ -17,7 +17,7 @@
package dev.patrickgold.florisboard.lib.snygg
import androidx.compose.runtime.saveable.Saver
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.lib.kotlin.curlyFormat
@@ -34,7 +34,7 @@ data class SnyggRule(
val element: String,
val codes: List<Int> = emptyList(),
val groups: List<Int> = emptyList(),
val modes: List<Int> = emptyList(),
val shiftStates: List<Int> = emptyList(),
val pressedSelector: Boolean = false,
val focusSelector: Boolean = false,
val disabledSelector: Boolean = false,
@@ -49,7 +49,7 @@ data class SnyggRule(
const val ATTRIBUTE_OR = '|'
const val CODES_KEY = "code"
const val GROUPS_KEY = "group"
const val MODES_KEY = "mode"
const val SHIFT_STATES_KEY = "shiftstate"
const val SELECTOR_COLON = ':'
const val PRESSED_SELECTOR = "pressed"
@@ -58,15 +58,17 @@ data class SnyggRule(
@Suppress("RegExpRedundantEscape", "RegExpSingleCharAlternation")
private val RuleValidator =
"""^(@?)[a-zA-Z0-9-]+(\[(code|group|mode)=(\+|-)?([0-9]+)(\|(\+|-)?([0-9]+))*\])*(:(pressed|focus|disabled))*${'$'}""".toRegex()
private val Placeholders = mapOf(
"""^(@?)[a-zA-Z0-9-]+(\[(code|group|shiftstate)=(\+|-)?([0-9]+)(\|(\+|-)?([0-9]+))*\])*(:(pressed|focus|disabled))*${'$'}""".toRegex()
val Placeholders = mapOf(
"c:delete" to KeyCode.DELETE,
"c:enter" to KeyCode.ENTER,
"c:shift" to KeyCode.SHIFT,
"c:space" to KeyCode.SPACE,
"m:normal" to InputMode.NORMAL.value,
"m:shiftlock" to InputMode.SHIFT_LOCK.value,
"m:capslock" to InputMode.CAPS_LOCK.value,
"sh:unshifted" to InputShiftState.UNSHIFTED.value,
"sh:shifted_manual" to InputShiftState.SHIFTED_MANUAL.value,
"sh:shifted_automatic" to InputShiftState.SHIFTED_AUTOMATIC.value,
"sh:caps_lock" to InputShiftState.CAPS_LOCK.value,
)
private val PreferredElementSorting = listOf(
@@ -112,7 +114,7 @@ data class SnyggRule(
}
val codes = mutableListOf<Int>()
val groups = mutableListOf<Int>()
val modes = mutableListOf<Int>()
val shiftStates = mutableListOf<Int>()
val attributes = elementAndAttributes.split(ATTRIBUTE_OPEN, ATTRIBUTE_CLOSE)
val isAnnotation = attributes[0].startsWith(ANNOTATION_MARKER)
val element = if (isAnnotation) attributes[0].substring(1) else attributes[0]
@@ -124,13 +126,13 @@ data class SnyggRule(
when (key) {
CODES_KEY -> codes
GROUPS_KEY -> groups
MODES_KEY -> modes
SHIFT_STATES_KEY -> shiftStates
else -> null
}?.addAll(values.map { it.toInt(10) })
}
}
return SnyggRule(
isAnnotation, element, codes.toList(), groups.toList(), modes.toList(),
isAnnotation, element, codes.toList(), groups.toList(), shiftStates.toList(),
pressedSelector, focusSelector, disabledSelector,
)
} catch (e: Exception) {
@@ -146,7 +148,7 @@ data class SnyggRule(
append(element)
appendAttribute(CODES_KEY, codes)
appendAttribute(GROUPS_KEY, groups)
appendAttribute(MODES_KEY, modes)
appendAttribute(SHIFT_STATES_KEY, shiftStates)
appendSelector(PRESSED_SELECTOR, pressedSelector)
appendSelector(FOCUS_SELECTOR, focusSelector)
appendSelector(DISABLED_SELECTOR, disabledSelector)
@@ -183,14 +185,14 @@ data class SnyggRule(
0 -> when {
this.codes.size != other.codes.size -> this.codes.size.compareTo(other.codes.size)
this.groups.size != other.groups.size -> this.groups.size.compareTo(other.groups.size)
this.modes.size != other.modes.size -> this.modes.size.compareTo(other.modes.size)
this.shiftStates.size != other.shiftStates.size -> this.shiftStates.size.compareTo(other.shiftStates.size)
else -> {
this.codes.indices.firstNotNullOfOrNull { n ->
(this.codes[n].compareTo(other.codes[n])).takeIf { it != 0 }
} ?: this.groups.indices.firstNotNullOfOrNull { n ->
(this.groups[n].compareTo(other.groups[n])).takeIf { it != 0 }
} ?: this.modes.indices.firstNotNullOfOrNull { n ->
(this.modes[n].compareTo(other.modes[n])).takeIf { it != 0 }
} ?: this.shiftStates.indices.firstNotNullOfOrNull { n ->
(this.shiftStates[n].compareTo(other.shiftStates[n])).takeIf { it != 0 }
} ?: 0
}
}
@@ -208,7 +210,7 @@ data class SnyggRule(
private fun comparatorWeight(): Int {
return (if (codes.isNotEmpty()) 0x01 else 0) +
(if (groups.isNotEmpty()) 0x02 else 0) +
(if (modes.isNotEmpty()) 0x04 else 0) +
(if (shiftStates.isNotEmpty()) 0x04 else 0) +
(if (pressedSelector) 0x08 else 0) +
(if (focusSelector) 0x10 else 0) +
(if (disabledSelector) 0x20 else 0)
@@ -224,7 +226,7 @@ data class SnyggRule(
if (element != other.element) return false
if (!codes.containsAll(other.codes) || !other.codes.containsAll(codes)) return false
if (!groups.containsAll(other.groups) || !other.groups.containsAll(groups)) return false
if (!modes.containsAll(other.modes) || !other.modes.containsAll(modes)) return false
if (!shiftStates.containsAll(other.shiftStates) || !other.shiftStates.containsAll(shiftStates)) return false
if (pressedSelector != other.pressedSelector) return false
if (focusSelector != other.focusSelector) return false
if (disabledSelector != other.disabledSelector) return false
@@ -241,7 +243,7 @@ data class SnyggRule(
for (group in groups.sorted()) {
result = 31 * result + group.hashCode()
}
for (mode in modes.sorted()) {
for (mode in shiftStates.sorted()) {
result = 31 * result + mode.hashCode()
}
result = 31 * result + pressedSelector.hashCode()

View File

@@ -50,7 +50,7 @@ class SnyggStylesheet(
rule.element == referenceRule.element
&& (rule.codes.isEmpty() || referenceRule.codes.isEmpty() || rule.codes.any { it in referenceRule.codes })
&& (rule.groups.isEmpty() || referenceRule.groups.isEmpty() || rule.groups.any { it in referenceRule.groups })
&& (rule.modes.isEmpty() || referenceRule.modes.isEmpty() || rule.modes.any { it in referenceRule.modes })
&& (rule.shiftStates.isEmpty() || referenceRule.shiftStates.isEmpty() || rule.shiftStates.any { it in referenceRule.shiftStates })
&& ((referenceRule.pressedSelector == rule.pressedSelector) || !rule.pressedSelector)
&& ((referenceRule.focusSelector == rule.focusSelector) || !rule.focusSelector)
&& ((referenceRule.disabledSelector == rule.disabledSelector) || !rule.disabledSelector)
@@ -71,7 +71,7 @@ class SnyggStylesheet(
rule.element == element
&& (code == Unspecified || rule.codes.isEmpty() || rule.codes.contains(code))
&& (group == Unspecified || rule.groups.isEmpty() || rule.groups.contains(group))
&& (mode == Unspecified || rule.modes.isEmpty() || rule.modes.contains(mode))
&& (mode == Unspecified || rule.shiftStates.isEmpty() || rule.shiftStates.contains(mode))
&& (isPressed == rule.pressedSelector || !rule.pressedSelector)
&& (isFocus == rule.focusSelector || !rule.focusSelector)
&& (isDisabled == rule.disabledSelector || !rule.disabledSelector)

View File

@@ -1,11 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M3,2h8v2h-8z"/>
<path android:fillColor="#000000" android:pathData="M6,11l2,0l0,-4l3,0l0,-2l-8,0l0,2l3,0z"/>
<path android:fillColor="#000000" android:pathData="M12.4036,20.1819l7.7781,-7.7781l1.4142,1.4142l-7.7781,7.7781z"/>
<path android:fillColor="#000000" android:pathData="M14.5,14.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<path android:fillColor="#000000" android:pathData="M19.5,19.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<path android:fillColor="#000000" android:pathData="M15.5,11c1.38,0 2.5,-1.12 2.5,-2.5V4h3V2h-4v4.51C16.58,6.19 16.07,6 15.5,6C14.12,6 13,7.12 13,8.5C13,9.88 14.12,11 15.5,11z"/>
<path android:fillColor="#000000" android:pathData="M9.74,15.96l-1.41,1.41l-0.71,-0.71l0.35,-0.35c0.98,-0.98 0.98,-2.56 0,-3.54c-0.49,-0.49 -1.13,-0.73 -1.77,-0.73c-0.64,0 -1.28,0.24 -1.77,0.73c-0.98,0.98 -0.98,2.56 0,3.54l0.35,0.35l-1.06,1.06c-0.98,0.98 -0.98,2.56 0,3.54C4.22,21.76 4.86,22 5.5,22s1.28,-0.24 1.77,-0.73l1.06,-1.06l1.41,1.41l1.41,-1.41l-1.41,-1.41l1.41,-1.41L9.74,15.96zM5.85,14.2c0.12,-0.12 0.26,-0.15 0.35,-0.15s0.23,0.03 0.35,0.15c0.19,0.2 0.19,0.51 0,0.71l-0.35,0.35L5.85,14.9C5.66,14.71 5.66,14.39 5.85,14.2zM5.85,19.85C5.73,19.97 5.59,20 5.5,20s-0.23,-0.03 -0.35,-0.15c-0.19,-0.19 -0.19,-0.51 0,-0.71l1.06,-1.06l0.71,0.71L5.85,19.85z"/>
<path android:fillColor="#000000" android:pathData="M20,4H4C2.9,4 2,4.9 2,6v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM7.64,15H6.49v-4.5l-0.9,0.66l-0.58,-0.89L6.77,9h0.87V15zM13.5,15H9.61v-1.02c1.07,-1.07 1.77,-1.77 2.13,-2.15c0.4,-0.42 0.54,-0.69 0.54,-1.06c0,-0.4 -0.31,-0.72 -0.81,-0.72c-0.52,0 -0.8,0.39 -0.9,0.72l-1.01,-0.42c0.01,-0.02 0.18,-0.76 1,-1.15c0.69,-0.33 1.48,-0.2 1.95,0.03c0.86,0.44 0.91,1.24 0.91,1.48c0,0.64 -0.31,1.26 -0.92,1.86c-0.25,0.25 -0.72,0.71 -1.4,1.39l0.03,0.05h2.37V15zM18.75,14.15C18.67,14.28 18.19,15 16.99,15c-0.04,0 -1.6,0.08 -2.05,-1.51l1.03,-0.41c0.03,0.1 0.19,0.86 1.02,0.86c0.41,0 0.89,-0.28 0.89,-0.77c0,-0.55 -0.48,-0.79 -1.04,-0.79h-0.5v-1h0.46c0.33,0 0.88,-0.14 0.88,-0.72c0,-0.39 -0.31,-0.65 -0.75,-0.65c-0.5,0 -0.74,0.32 -0.85,0.64l-0.99,-0.41C15.2,9.9 15.68,9 16.94,9c1.09,0 1.54,0.64 1.62,0.75c0.33,0.5 0.28,1.16 0.02,1.57c-0.15,0.22 -0.32,0.38 -0.52,0.48v0.07c0.28,0.11 0.51,0.28 0.68,0.52C19.11,12.91 19.07,13.66 18.75,14.15z"/>
</vector>

View File

@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">العنصر المستهدف</string>
<string name="settings__theme_editor__rule_codes">رموز المفتاح المستهدف</string>
<string name="settings__theme_editor__rule_groups">مجموعات</string>
<string name="settings__theme_editor__rule_modes">أنماط</string>
<string name="settings__theme_editor__rule_shift_states">حالة زر shift</string>
<string name="settings__theme_editor__rule_selectors">محددات</string>
<string name="settings__theme_editor__add_code">إضافة رموز المفتاح</string>
<string name="settings__theme_editor__edit_code">تعديل رمز المفتاح</string>
@@ -658,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">دائمًا ما يكون الحرف الأولي المحدد بعد الضغط لفترة طويلة هو رمز التلميح ، أو العلامة الأساسية في حالة عدم توفر رمز تلميح</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">تحديد الاولويات الذكي</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">يتم تحديد الحرف الأولي المحدد بعد الضغط لفترة طويلة ديناميكيًا ليكون إما العلامة الأساسية أو رمز التلميح ، بناءً على اللغة والتخطيط الحاليين</string>
<string name="enum__input_mode__normal" comment="Enum value label">عادي</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">مفتاح قفل التغيير Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">مفتاح الحروف الكبيرة</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">غير متحول</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">متحول(يدوي)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">متحول(ألي)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">مفتاح الحروف الكبيرة</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">لا تُظهر أبداً</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">الإظهار دائماً</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">يعين بشكل ديناميكي</string>

View File

@@ -7,7 +7,7 @@
<!-- One-handed strings -->
<string name="one_handed__close_btn_content_description" comment="Content description for the one-handed close button">Излизане от режима за работа с една ръка.</string>
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">Преместване на клавиатурата отляво.</string>
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">Премества клавиатурата отдясно.</string>
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">Преместване на клавиатурата отдясно.</string>
<!-- Media strings -->
<string name="settings__media__title">Емоджи</string>
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Емоджи</string>
@@ -17,11 +17,11 @@
<string name="prefs__media__emoji_preferred_skin_tone">Предпочитан цвят на кожата за емоджи</string>
<string name="prefs__media__emoji_preferred_hair_style">Предпочитана прическа за емоджи</string>
<!-- Emoji strings -->
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Усмивки и Емоции</string>
<string name="emoji__category__people_body" comment="Emoji category name">Хора и Тяло</string>
<string name="emoji__category__animals_nature" comment="Emoji category name">Животни и Природа</string>
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Усмивки и емоции</string>
<string name="emoji__category__people_body" comment="Emoji category name">Хора и тяло</string>
<string name="emoji__category__animals_nature" comment="Emoji category name">Животни и природа</string>
<string name="emoji__category__food_drink" comment="Emoji category name">Храни и напитки</string>
<string name="emoji__category__travel_places" comment="Emoji category name">Пътуване и Забележителности</string>
<string name="emoji__category__travel_places" comment="Emoji category name">Пътуване и забележителности</string>
<string name="emoji__category__activities" comment="Emoji category name">Дейности</string>
<string name="emoji__category__objects" comment="Emoji category name">Предмети</string>
<string name="emoji__category__symbols" comment="Emoji category name">Символи</string>
@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">Целеви елемент</string>
<string name="settings__theme_editor__rule_codes">Целеви кодове</string>
<string name="settings__theme_editor__rule_groups">Групи</string>
<string name="settings__theme_editor__rule_modes">Режим</string>
<string name="settings__theme_editor__rule_shift_states">Състояние на Shift</string>
<string name="settings__theme_editor__rule_selectors">Селектор</string>
<string name="settings__theme_editor__add_code">Добавяне на код</string>
<string name="settings__theme_editor__edit_code">Промяна на код</string>
@@ -269,7 +269,7 @@
<!-- Typing strings -->
<string name="settings__typing__title" comment="Title of Typing experience screen">Подсказки и помощ при писане</string>
<string name="pref__suggestion__title" comment="Preference group title">Предложения</string>
<string name="pref__suggestion__api30_inline_suggestions_enabled__summary" comment="Preference summary">Показвай предложения от услуги за автоматично попълване</string>
<string name="pref__suggestion__api30_inline_suggestions_enabled__summary" comment="Preference summary">Показване на предложения от услугите за автоматично попълване</string>
<string name="pref__suggestion__display_mode__label" comment="Preference title">Оформление на предложенията</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">Предложения от междинната памет</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">Време за предложения от междинната памет</string>
@@ -349,11 +349,11 @@
<string name="pref__advanced__force_private_mode__summary" comment="Summary of Force private mode preference in Advanced">Изключва обработката на въвежданите от вас данни</string>
<!-- Spelling UI strings -->
<string name="settings__spelling__title" comment="Title of the Spelling screen">Проверка на правописа</string>
<string name="settings__spelling__use_floris_config" comment="String used for the stub subtype in the system spell checker UI">Използвай конфигурацията за проверка на правописа от клавиатурата</string>
<string name="settings__spelling__use_floris_config" comment="String used for the stub subtype in the system spell checker UI">Използване на настройката за проверка на правописа от клавиатурата</string>
<string name="pref__spelling__active_spellchecker__label">Проверка на правописа при въвеждане</string>
<string name="pref__spelling__active_spellchecker__summary_disabled">Изключено от системните настройки. Сгрешените думи няма да бъдат подчертавани с червено. Докоснете, за да промените.</string>
<string name="pref__spelling__active_spellchecker__summary_none">Няма избрана услуга за автоматично попълване. Докоснете, за да промените.</string>
<string name="pref__spelling__active_spellchecker__summary_use_sys_lang_set">Опция \"Езици\" в системните настройки за проверка на правописа е сложена на \"Използвай системните езици\". Поради грешка, {app_name} може да не проверява правописа. Препоръчително е да я смените на \"{use_floris_config}\".</string>
<string name="pref__spelling__active_spellchecker__summary_none">Не е избрана услуга за проверка на правописа на текстови полета. Докоснете, за да промените.</string>
<string name="pref__spelling__active_spellchecker__summary_use_sys_lang_set">Опция \"Езици\" в системните настройки за проверка на правописа е сложена на \"Използване на системните езици\". Поради дефект, {app_name} може да не проверява правописа. Препоръчително е да я смените на {use_floris_config}.</string>
<string name="pref__spelling__group_spellchecker_config__title" comment="Title of Fine-adjustment sub-group">Настройка</string>
<string name="pref__spelling__language_mode__label" comment="Label of Language mode pref">Езици</string>
<string name="pref__spelling__use_contacts__label" comment="Label of Use contact list pref">[NOT IMPLEMENTED] Имена от контактите</string>
@@ -658,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Първоначално избраният знак след задържане на клавиш винаги е знака от подсказката или основния акцент ако в подсказката няма знак</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Интелигентен приоритет</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Първоначално избраният знак при задържане на клавиш се определя между основния акцент или знака от подсказката на базата на текущия език и подредба</string>
<string name="enum__input_mode__normal" comment="Enum value label">Обикн.</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Закл. на Shift</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps Lock</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Без Shift</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Shift (ръчно)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Shift (автоматично)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Caps Lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Не се показва</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Винаги се показва</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Показва се динамично</string>

View File

@@ -80,7 +80,6 @@
<!-- State strings -->
<string name="state__disabled">Onemogućeno</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Normalno</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nikad ne prikaži</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Uvijek prikaži</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Prikaži dinamički</string>

View File

@@ -95,7 +95,6 @@
<string name="settings__theme_editor__add_rule">Afegir norma</string>
<string name="settings__theme_editor__edit_rule">Editar norma</string>
<string name="settings__theme_editor__rule_groups">Grups</string>
<string name="settings__theme_editor__rule_modes">Modes</string>
<string name="settings__theme_editor__rule_selectors">Selectors</string>
<string name="settings__theme_editor__add_property">Afegir propietat</string>
<string name="settings__theme_editor__edit_property">Editar propietat</string>
@@ -176,7 +175,6 @@
<string name="state__enabled">Habilitat</string>
<!-- Enum label and description strings -->
<string name="enum__key_hint_mode__accent_priority" comment="Enum value label">Accent està prioritzat</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">No mostrar mai</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Mostrar sempre</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Mostrar dinàmicament</string>

View File

@@ -3,7 +3,7 @@
<string name="app_name">FlorisBoard</string>
<string name="key__phone_pause" comment="Label for the Pause key in the telephone keyboard layout">ڕاگرتن</string>
<string name="key__phone_wait" comment="Label for the Wait key in the telephone keyboard layout">چاوەڕوانبە</string>
<string name="key_popup__threedots_alt" comment="Content description for the three-dots icon in a key popup">ئایکۆنی سی نوقتە، ئەگەر چالاک بێت پیت و هێما لاوەکیەکانی خانەی دووەم پیشان ئەدرێن لەکاتی دەست ڕاگرتن</string>
<string name="key_popup__threedots_alt" comment="Content description for the three-dots icon in a key popup">ئایکۆنی سێ نوقتە، ئەگەر چالاک بێت پیت و هێما لاوەکیەکانی خانەی دووەم پیشان ئەدرێن لەکاتی دەست ڕاگرتن</string>
<!-- One-handed strings -->
<string name="one_handed__close_btn_content_description" comment="Content description for the one-handed close button">داخستنی دۆخی بەکارهێنانی یەک دەست</string>
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">بردنی تەختەکلیل بۆ لای چەپ</string>
@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">بەشی مەبەست</string>
<string name="settings__theme_editor__rule_codes">سەرچاوەی کۆدی مەبەست</string>
<string name="settings__theme_editor__rule_groups">گرووپ</string>
<string name="settings__theme_editor__rule_modes">شێواز</string>
<string name="settings__theme_editor__rule_shift_states">دۆخی شێفت</string>
<string name="settings__theme_editor__rule_selectors">هەڵبژاردن</string>
<string name="settings__theme_editor__add_code">زیادکردنی کۆدی دوگمە</string>
<string name="settings__theme_editor__edit_code">دەستکاریکردنی کۆد</string>
@@ -247,7 +247,7 @@
<string name="pref__keyboard__key_spacing__label" comment="Preference title">بۆشایی نێوان دوگمەکان</string>
<string name="pref__keyboard__bottom_offset__label" comment="Preference title">مەودای بەشی خوارەوە</string>
<string name="pref__keyboard__group_keypress__label" comment="Preference group title">کاریگەرییەکان</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">بچووککراوە پیت</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">بچووککراوەی پیت</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">پیشاندانی بچووککراوەی پیت لەکاتی نووسین</string>
<string name="pref__keyboard__merge_hint_popups_enabled__label" comment="Preference title">پیشاندانی پیتە لاوەکییەکانی خانەی دووەمی بەشی هێماکان</string>
<string name="pref__keyboard__merge_hint_popups_enabled__summary" comment="Preference summary">پیشاندانی بچووککراوەی هێماکانی خانەی دووەم</string>
@@ -319,7 +319,9 @@
<string name="pref__glide_trail_fade_duration">ماوەی دەرکەوتن</string>
<string name="pref__glide_preview_refresh_delay">ماوەی پیشاندان</string>
<string name="pref__glide__show_preview">پیشاندانی هێما لەکاتی نووسین بەسەریەکەوە</string>
<string name="pref__gestures__general_title" comment="Preference group title">ئاماژژە گشتییەکان</string>
<string name="pref__glide__immediate_backspace_deletes_word__label">هەمیشە سڕینەوەی ووشە</string>
<string name="pref__glide__immediate_backspace_deletes_word__summary">دەسدان لە دوگمەی سڕینەوە و ڕاکێشان بۆ لای ڕاست دوای ئاماژەکان سەرجەم ووشەکان ئەسڕێتەوە</string>
<string name="pref__gestures__general_title" comment="Preference group title">ئاماژە گشتییەکان</string>
<string name="pref__gestures__space_bar_title" comment="Preference group title">ئاماژەی دوگمەی بۆشایی</string>
<string name="pref__gestures__other_title" comment="Preference group title">ئاماژەکانی/فرمانەکانی تر</string>
<string name="pref__gestures__swipe_up__label" comment="Preference title">ڕاکێشان بۆ سەرەوە</string>
@@ -331,6 +333,7 @@
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">دوگمەی بۆشایی بۆ لای ڕاست</string>
<string name="pref__gestures__space_bar_long_press__label" comment="Preference title">دوگمەی بۆشایی لەکاتی دەستڕاگرتن</string>
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">دوگمەی سڕینەوە بۆ لای چەپ</string>
<string name="pref__gestures__delete_key_long_press__label" comment="Preference title">دوگمەی سڕینەوە لەکاتی دەست ڕاگرتن</string>
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">ئاستی خێرایی ڕاکێشان</string>
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">ماوەی ڕاکێشان</string>
<string name="settings__advanced__title" comment="Title of Advanced settings">ڕێکخستنی زیاتر</string>
@@ -503,6 +506,8 @@
<string name="devtools__show_heap_memory_stats__summary" comment="Summary of Show heap memory stats in Devtools">پیشاندانی زانیاری لەسەر ڕێژەی بیرگەی کاتی بەکارهاتوو</string>
<string name="devtools__show_primary_clip__label" comment="Label of Show primary clip in Devtools">پیشاندانی لەبەرگیراوەی بنەڕەتی</string>
<string name="devtools__show_primary_clip__summary" comment="Summary of Show primary clip in Devtools">جێگۆڕینی لەبەرگیراوەکانی ئێستا لەگەڵ لەبەرگیراوەی بەرنامەکە</string>
<string name="devtools__show_input_state_overlay__label" comment="Label of Show input cache overlay in Devtools">پیشاندانی دۆخی تەختەکلیل</string>
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">پیشاندانی زانیاری دۆخی تەختەکلیل</string>
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">پیشاندانی ڕاستکردنەوەی ڕێنووس</string>
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">پیشاندانی ڕاستکردنەوەی هەڵەی ڕێنووس لەسەروی تەختەکلیل</string>
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">پیشاندانی سنوورەکانی کلیل</string>
@@ -654,9 +659,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">پیشاندانی یەکەم هێمای خانەی دووەم لەکاتی دەست راگرتن</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">هەڵبژاردنی زیرەک</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">پیشاندانی یەکەم پیت یان هێمای خانەی دووەم لەکاتی دەست راگرتن بەخۆکاری بەپێی زمانی هەڵبژێردراو</string>
<string name="enum__input_mode__normal" comment="Enum value label">بنەڕەتی</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">قفڵکردنی شێفت</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">گەورەکردنی شێفت</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">ئاسایی</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">شێفت (دەستی)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">شێفت (خۆکاری)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">قفڵکردنی شێفت</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">شاردنەوە</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">هەمیشە پیشاندان</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">پیشاندانی زیرەکانە</string>
@@ -688,7 +694,9 @@
<string name="enum__swipe_action__no_action" comment="Enum value label">هیچ فرمانێک</string>
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">گەڕانەوە بۆ شێوازی پێشوو</string>
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">گەڕانەوە بۆ شێوازی دواتر</string>
<string name="enum__swipe_action__delete_character" comment="Enum value label">سڕینەوەی پیتی پێش هێما</string>
<string name="enum__swipe_action__delete_characters_precisely" comment="Enum value label">سڕینەوەی پیتەکان یەک بە یەک</string>
<string name="enum__swipe_action__delete_word" comment="Enum value label">سڕینەوەی ووشەی پێش هێما</string>
<string name="enum__swipe_action__delete_words_precisely" comment="Enum value label">سڕینەوەی ووشەکان یەک بە یەک</string>
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">لابردنی تەختەکلیل</string>
<string name="enum__swipe_action__insert_space" comment="Enum value label">زیادکردنی ماوە</string>

View File

@@ -101,7 +101,6 @@
<string name="settings__theme_editor__edit_rule">Upravit pravidlo</string>
<string name="settings__theme_editor__rule_element">Cílový prvek</string>
<string name="settings__theme_editor__rule_groups">Skupiny</string>
<string name="settings__theme_editor__rule_modes">Režimy</string>
<string name="settings__theme_editor__rule_selectors">Selektory</string>
<string name="settings__theme_editor__code_already_exists">Tento klávesový kód je již definován.</string>
<string name="settings__theme_editor__add_property">Přidat vlastnost</string>
@@ -367,7 +366,6 @@
<!-- State strings -->
<string name="state__disabled">Vypnuto</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Normální</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nikdy nezobrazovat</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Vždy zobrazovat</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dynamicky zobrazovat</string>

View File

@@ -139,7 +139,6 @@
<string name="state__disabled">Deaktivéret</string>
<string name="state__enabled">Aktivéret</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Vis aldrig</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Vis altid</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Vis dynamisk</string>

View File

@@ -104,7 +104,6 @@
<string name="settings__theme_editor__rule_already_exists">Diese Stylesheetregel ist schon definiert.</string>
<string name="settings__theme_editor__rule_element">Zielelement</string>
<string name="settings__theme_editor__rule_groups">Gruppen</string>
<string name="settings__theme_editor__rule_modes">Modi</string>
<string name="settings__theme_editor__rule_selectors">Selektoren</string>
<string name="settings__theme_editor__add_code">Key code hinzufügen</string>
<string name="settings__theme_editor__edit_code">Key code bearbeiten</string>
@@ -318,6 +317,7 @@
<string name="pref__glide_trail_fade_duration">Ausblendzeit der Spur</string>
<string name="pref__glide_preview_refresh_delay">Verzögerung der Vorschau</string>
<string name="pref__glide__show_preview">Vorschau während des Gleitens anzeigen</string>
<string name="pref__glide__immediate_backspace_deletes_word__summary">Löschtaste löscht ganzes Wort nach Glide-Typing-Eingabe</string>
<string name="pref__gestures__general_title" comment="Preference group title">Allgemeine Gesten</string>
<string name="pref__gestures__space_bar_title" comment="Preference group title">Leertaste Gesten</string>
<string name="pref__gestures__other_title" comment="Preference group title">Andere Gesten / Gesten Schwellenwerte</string>
@@ -330,6 +330,7 @@
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">Leertaste nach rechts wischen</string>
<string name="pref__gestures__space_bar_long_press__label" comment="Preference title">Leertaste lang drücken</string>
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Löschtaste nach links wischen</string>
<string name="pref__gestures__delete_key_long_press__label" comment="Preference title">Aktion bei lang gedrückter Löschtaste</string>
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">Gesten-Geschwindigkeitsschwelle</string>
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Gesten-Distanzschwelle</string>
<string name="settings__advanced__title" comment="Title of Advanced settings">Erweitert</string>
@@ -652,9 +653,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Das erste Zeichen, das nach langem Drücken ausgewählt wird, ist immer das Hinweissymbol, oder der primäre Akzent, wenn kein Hinweissymbol verfügbar ist</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Intelligente Priorisierung</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Das erste Zeichen, das nach langem Drücken ausgewählt wird, wird je nach Sprache und Layout dynamisch, entweder als Hauptakzent oder als Hinweissymbol festgelegt</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Feststelltaste</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Feststelltaste</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nie anzeigen</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Immer anzeigen</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dynamisch anzeigen</string>
@@ -686,7 +684,9 @@
<string name="enum__swipe_action__no_action" comment="Enum value label">Keine Aktion</string>
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Wechsle zum vorherigen Tastaturmodus</string>
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Wechsle zum nächsten Tastaturmodus</string>
<string name="enum__swipe_action__delete_character" comment="Enum value label">Löscht einzelne Zeichen</string>
<string name="enum__swipe_action__delete_characters_precisely" comment="Enum value label">Zeichen einzeln löschen</string>
<string name="enum__swipe_action__delete_word" comment="Enum value label">Löscht ganze Wörter</string>
<string name="enum__swipe_action__delete_words_precisely" comment="Enum value label">Ganzes Wort löschen</string>
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">Tastatur ausblenden</string>
<string name="enum__swipe_action__insert_space" comment="Enum value label">Leerzeichen einfügen</string>

View File

@@ -447,8 +447,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Ο αρχικός χαρακτήρας που επιλέγεται μετά από παρατεταμένο πάτημα είναι πάντα το υπονοούμενο σύμβολο, ή ο κύριος τονισμός εάν δεν υπάρχει διαθέσιμο υπονοούμενο σύμβολο</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Έξυπνη προτεραιοποίηση</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Ο αρχικός χαρακτήρας που επιλέγεται μετά από παρατεταμένο πάτημα αποφασίζεται δυναμικά ώστε να είναι είτε ο κύριος τονισμός, είτε το υπονοούμενο σύμβολο, βάσει της τρέχουσας γλώσσας και διάταξης</string>
<string name="enum__input_mode__normal" comment="Enum value label">Κανονικό</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Κεφαλαία</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Να μην εμφανίζεται ποτέ</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Να εμφανίζεται πάντα</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Να εμφανίζεται δυναμικά</string>

View File

@@ -75,7 +75,6 @@
<!-- State strings -->
<string name="state__disabled">Malŝaltita</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Norma</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Neniam montri</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Ĉiam montri</string>
<string name="enum__one_handed_mode__off" comment="Enum value label">Malvalidigita</string>

View File

@@ -105,7 +105,6 @@
<string name="settings__theme_editor__rule_element">Elemento destino</string>
<string name="settings__theme_editor__rule_codes">Códigos de clave destino</string>
<string name="settings__theme_editor__rule_groups">Grupos</string>
<string name="settings__theme_editor__rule_modes">Modos</string>
<string name="settings__theme_editor__rule_selectors">Selectores</string>
<string name="settings__theme_editor__add_code">Añadir código clave</string>
<string name="settings__theme_editor__edit_code">Editar código clave</string>
@@ -640,9 +639,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">El carácter inicial elegido al hacer una pulsación larga será siempre el símbolo, o el acento principal si no hay símbolo disponible</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Priorización inteligente</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">El carácter inicial elegido al hacer una pulsación larga será decidido dinámicamente entre el símbolo o el acento principal, de acuerdo al idioma y distribución del teclado actual</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Bloq Mayús</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">No mostrar nunca</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Mostrar siempre</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Mostrar dinámicamente</string>

View File

@@ -100,7 +100,6 @@
<string name="settings__theme_editor__rule_element">مؤلفه مورد نظر</string>
<string name="settings__theme_editor__rule_codes">کد کلیدهای مورد نظر</string>
<string name="settings__theme_editor__rule_groups">گروه ها</string>
<string name="settings__theme_editor__rule_modes">حالت ها</string>
<string name="settings__theme_editor__rule_selectors">انتخابگرها</string>
<string name="settings__theme_editor__add_code">افزودن کد کلید</string>
<string name="settings__theme_editor__edit_code">ویرایش کد کلید</string>
@@ -316,9 +315,6 @@
<!-- State strings -->
<string name="state__disabled">غیرفعال شد</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">عادی</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">قفل Shift</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">قفل Caps</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">هرگز نشان نده</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">همیشه نشان بده</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">به صورت پویا نشان بده</string>

View File

@@ -176,7 +176,6 @@
<!-- State strings -->
<string name="state__disabled">Poissa käytöstä</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Normaali</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Älä näytä koskaan</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Näytä aina</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Näytä dynaamisesti</string>

View File

@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">Élément cible</string>
<string name="settings__theme_editor__rule_codes">Codes clés de la cible</string>
<string name="settings__theme_editor__rule_groups">Groupes</string>
<string name="settings__theme_editor__rule_modes">Modes</string>
<string name="settings__theme_editor__rule_shift_states">États de décalage</string>
<string name="settings__theme_editor__rule_selectors">Sélecteurs</string>
<string name="settings__theme_editor__add_code">Ajouter un code clé</string>
<string name="settings__theme_editor__edit_code">Modifier le code de la clé</string>
@@ -317,6 +317,8 @@
<string name="pref__glide_trail_fade_duration">Temps de fondu du parcours de glissement</string>
<string name="pref__glide_preview_refresh_delay">Délai d\'actualisation de l\'aperçu</string>
<string name="pref__glide__show_preview">Afficher l\'aperçu lors de la saisie en glissant</string>
<string name="pref__glide__immediate_backspace_deletes_word__label">Toujours supprimer le mot</string>
<string name="pref__glide__immediate_backspace_deletes_word__summary">Appuyer sur supprimer juste après un glissement supprime le mot entier</string>
<string name="pref__gestures__general_title" comment="Preference group title">Gestes généraux</string>
<string name="pref__gestures__space_bar_title" comment="Preference group title">Gestes sur la barre d\'espace</string>
<string name="pref__gestures__other_title" comment="Preference group title">Autres gestes / Seuils des gestes</string>
@@ -329,6 +331,7 @@
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">Glisser de la barre d\'espace vers la droite</string>
<string name="pref__gestures__space_bar_long_press__label" comment="Preference title">Appui long sur la barre d\'espace</string>
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Glisser de la touche de suppression vers la gauche</string>
<string name="pref__gestures__delete_key_long_press__label" comment="Preference title">Touche de suppression pression longue</string>
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">Seuil de vitesse de glissement</string>
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Seuil de la distance de glissement</string>
<string name="settings__advanced__title" comment="Title of Advanced settings">Avancé</string>
@@ -500,8 +503,12 @@
<string name="devtools__show_heap_memory_stats__summary" comment="Summary of Show heap memory stats in Devtools">Afficher en superposé l\'utilisation de mémoire tas et sa taille maximale dans le coin haut droit</string>
<string name="devtools__show_primary_clip__label" comment="Label of Show primary clip in Devtools">Afficher l\'attache principale</string>
<string name="devtools__show_primary_clip__summary" comment="Summary of Show primary clip in Devtools">Superposer l\'attache principale actuelle du presse-papiers</string>
<string name="devtools__show_input_state_overlay__label" comment="Label of Show input cache overlay in Devtools">Afficher la superposition de l\'état de l\'entrée</string>
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Superpose l\'état actuel de l\'entrée pour le débogage</string>
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Afficher la superposition d\'orthographe</string>
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Superpose les résultats d\'orthographe actuels pour le débogage</string>
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Afficher les limites des touches</string>
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Soulignez en rouge les limites des touches clés</string>
<string name="devtools__clear_udm_internal_database__label" comment="Label of Clear internal user dictionary database in Devtools">Effacer la base de données du dictionnaire utilisateur interne</string>
<string name="devtools__clear_udm_internal_database__summary" comment="Summary of Clear internal user dictionary database in Devtools">Efface tous les mots de la table de la base de données du dictionnaire</string>
<string name="devtools__reset_flag__label" comment="Label of Reset flag preferences in Devtools">Réinitialisation du drapeau \"{flag_name}\"</string>
@@ -649,9 +656,8 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Le caractère initial sélectionné après une pression longue est toujours l\'accent primaire ou le symbole de l\'indice si aucun accent primaire n\'est disponible.</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Priorité intelligente</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Le caractère initial sélectionné après une pression longue est décidé de manière dynamique pour être soit l\'accent principal, soit le symbole de l\'indice, en fonction de la langue et de la disposition actuelles.</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Verr Maj</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Verr Maj</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Non décalé</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Verrouillage des majuscules</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Ne jamais afficher</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Toujours afficher</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Affichage dynamique</string>
@@ -683,7 +689,9 @@
<string name="enum__swipe_action__no_action" comment="Enum value label">Aucune action</string>
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Passer au mode de clavier précédent</string>
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Passer au mode de clavier suivant</string>
<string name="enum__swipe_action__delete_character" comment="Enum value label">Effacer le caractère précédant le curseur</string>
<string name="enum__swipe_action__delete_characters_precisely" comment="Enum value label">Effacer les caractères avec précision</string>
<string name="enum__swipe_action__delete_word" comment="Enum value label">Effacer le mot précédant le curseur</string>
<string name="enum__swipe_action__delete_words_precisely" comment="Enum value label">Supprimer les mots avec précision</string>
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">Masquer le clavier</string>
<string name="enum__swipe_action__insert_space" comment="Enum value label">Insérer une espace</string>
@@ -705,6 +713,7 @@
<string name="enum__swipe_action__switch_to_prev_keyboard" comment="Enum value label">Passer au clavier précédent</string>
<string name="enum__swipe_action__switch_to_prev_subtype" comment="Enum value label">Passer à la disposition précédente</string>
<string name="enum__swipe_action__switch_to_next_subtype" comment="Enum value label">Passer à la disposition suivante</string>
<string name="enum__swipe_action__toggle_smartbar_visibility" comment="Enum value label">Modifier la visibilité de la barre intelligente</string>
<string name="enum__theme_mode__always_day" comment="Enum value label">Toujours clair</string>
<string name="enum__theme_mode__always_night" comment="Enum value label">Toujours sombre</string>
<string name="enum__theme_mode__follow_system" comment="Enum value label">Suivre le système</string>

View File

@@ -96,7 +96,6 @@
<string name="settings__theme_editor__edit_rule">Szabály szerkesztése</string>
<string name="settings__theme_editor__rule_element">Cél elem</string>
<string name="settings__theme_editor__rule_groups">Csoportok</string>
<string name="settings__theme_editor__rule_modes">Módok</string>
<string name="settings__theme_editor__rule_selectors">Választók</string>
<string name="settings__theme_editor__add_code">Billentyűkód hozzáadása</string>
<string name="settings__theme_editor__edit_code">Billentyűkód szerkesztése</string>
@@ -542,9 +541,6 @@
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} Göndör haj</string>
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} Ősz haj</string>
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} Kopasz</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normál</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift zárolása</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Soha ne jelenjen meg</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Mindig jelenjen meg</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dinamikus megjelenítés</string>

View File

@@ -45,7 +45,7 @@
<string name="settings__help" comment="General label for help buttons in Settings">Bantuan</string>
<string name="settings__default" comment="General string which is used when a preference has the default value set">Default</string>
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">Standar sistem</string>
<string name="settings__home__title" comment="Title of the Home screen">Selamat datang ke {app_name}</string>
<string name="settings__home__title" comment="Title of the Home screen">Selamat datang di {app_name}</string>
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBoard tidak diaktifkan di sistem dan tidak akan tersedia sebagai metode input di pilihan input.
Klik di sini untuk menyelesaikan masalah ini.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">FlorisBoard tidak dipilih sebagai metode input default. Klik di sini untuk menyelesaikan masalah ini.</string>
@@ -106,7 +106,7 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
<string name="settings__theme_editor__rule_element">Elemen target</string>
<string name="settings__theme_editor__rule_codes">Kode target tombol</string>
<string name="settings__theme_editor__rule_groups">Grup</string>
<string name="settings__theme_editor__rule_modes">Mode</string>
<string name="settings__theme_editor__rule_shift_states">Status shift</string>
<string name="settings__theme_editor__rule_selectors">Pemilih</string>
<string name="settings__theme_editor__add_code">Tambahkan kode tombol</string>
<string name="settings__theme_editor__edit_code">Edit kode tombol</string>
@@ -222,7 +222,7 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
<string name="pref__input_feedback__haptic_feat_key_press__label" comment="Preference title">Getaran tekanan tombol</string>
<string name="pref__input_feedback__haptic_feat_key_long_press__label" comment="Preference title">Getaran tekanan lama tombol</string>
<string name="pref__input_feedback__haptic_feat_key_repeated_action__label" comment="Preference title">Getaran aksi berulang tombol</string>
<string name="pref__input_feedback__haptic_feat_gesture_swipe__label" comment="Preference title">Getarn gestur geser</string>
<string name="pref__input_feedback__haptic_feat_gesture_swipe__label" comment="Preference title">Getaran gestur geser</string>
<string name="pref__input_feedback__haptic_feat_gesture_moving_swipe__label" comment="Preference title">Getaran gestur geser bergerak</string>
<string name="pref__input_feedback__any_feat_key_press__summary" comment="Preference summary">mis. tombol, tab emoji</string>
<string name="pref__input_feedback__any_feat_key_long_press__summary" comment="Preference summary">mis. menu popup</string>
@@ -659,9 +659,10 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Karakter awal yang dipilih setelah ditekan lama selalu merupakan simbol petunjuk, atau aksen utama jika tidak ada simbol petunjuk yang tersedia</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Prioritas pintar</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Karakter awal yang dipilih setelah ditekan lama secara dinamis akan memutuskan untuk menjadi aksen utama atau simbol petunjuk, berdasarkan bahasa dan tata letak saat ini</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Tidak di-shift</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Di-shift (manual)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Di-shift (otomatis)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Kunci huruf kapital</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Jangan tampilkan</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Selalu tampilkan</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Tampilkan secara dinamis</string>

View File

@@ -103,11 +103,23 @@
<string name="settings__theme_editor__no_rules_defined">Questo file di stile non ha regole definite. Aggiungi una regola per iniziare a personalizzare questo file di stile.</string>
<string name="settings__theme_editor__rule_already_exists">Questa regola del file di stile è già definita.</string>
<string name="settings__theme_editor__rule_element">Elemento di destinazione</string>
<string name="settings__theme_editor__rule_codes">Codici tasto di destinazione</string>
<string name="settings__theme_editor__rule_groups">Gruppi</string>
<string name="settings__theme_editor__rule_modes">Modalità</string>
<string name="settings__theme_editor__rule_shift_states">Stati Maiusc</string>
<string name="settings__theme_editor__rule_selectors">Selettori</string>
<string name="settings__theme_editor__add_code">Aggiungi codice tasto</string>
<string name="settings__theme_editor__edit_code">Modifica codice tasto</string>
<string name="settings__theme_editor__no_codes_defined">Applica la regola a tutti gli elementi di destinazione.</string>
<string name="settings__theme_editor__codes_defined">Applica la regola solo agli elementi di destinazione con i seguenti codici tasto:</string>
<string name="settings__theme_editor__code_already_exists">Questo codice di tasto è già definito.</string>
<string name="settings__theme_editor__code_invalid">Questo codice di tasto non è valido. Assicurarsi che il codice di tasto sia compreso nell\'intervallo da {c_min} a {c_max} per i caratteri o da {i_min} a {i_max} per tasti speciali interni.</string>
<string name="settings__theme_editor__code_help_text">In alternativa, i seguenti link ti aiuteranno a trovare il codice di tasto corrispondente:</string>
<string name="settings__theme_editor__code_placeholder">Codice</string>
<string name="settings__theme_editor__code_recording_help_text">Per trovare il codice di un tasto, usare il pulsante accanto al campo di immissione del codice. Una volta attivato, registrerà la successiva pressione del tasto e inserirà il codice nel campo di input.</string>
<string name="settings__theme_editor__code_recording_started">Registrazione del codice tasto iniziata</string>
<string name="settings__theme_editor__code_recording_stopped">Registrazione del codice tasto terminata</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} deve essere la tastiera predefinita per registrare un codice tasto</string>
<string name="settings__theme_editor__code_recording_placeholder">Registrazione in corso…</string>
<string name="settings__theme_editor__add_property">Aggiungi proprietà</string>
<string name="settings__theme_editor__edit_property">Modifica proprietà</string>
<string name="settings__theme_editor__property_already_exists">Una proprietà con questo nome esiste già all\'interno della regola corrente.</string>
@@ -307,6 +319,8 @@
<string name="pref__glide_trail_fade_duration">Tempo di dissolvenza del percorso di scorrimento</string>
<string name="pref__glide_preview_refresh_delay">Ritardo di aggiornamento dell\'anteprima</string>
<string name="pref__glide__show_preview">Mostra l\'anteprima durante la digitazione a scorrimento</string>
<string name="pref__glide__immediate_backspace_deletes_word__label">Elimina sempre la parola</string>
<string name="pref__glide__immediate_backspace_deletes_word__summary">Premendo Elimina subito dopo uno scorrimento si elimina l\'intera parola</string>
<string name="pref__gestures__general_title" comment="Preference group title">Gesti generali</string>
<string name="pref__gestures__space_bar_title" comment="Preference group title">Gesti della barra di spazio</string>
<string name="pref__gestures__other_title" comment="Preference group title">Altri gesti / Sogli dei gesti</string>
@@ -318,7 +332,8 @@
<string name="pref__gestures__space_bar_swipe_left__label" comment="Preference title">Swipe a sinistra sulla barra spaziatrice</string>
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">Swipe a destra sulla barra spaziatrice</string>
<string name="pref__gestures__space_bar_long_press__label" comment="Preference title">Pressione prolungata sulla barra di spazio</string>
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Swipe a sinistra sul tasto di cancellazione all\'indietro</string>
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Swipe a sinistra sul tasto Elimina</string>
<string name="pref__gestures__delete_key_long_press__label" comment="Preference title">Pressione prolungata del tasto Elimina</string>
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">Soglia di velocità dello swipe</string>
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Soglia di distanza dello swipe</string>
<string name="settings__advanced__title" comment="Title of Advanced settings">Avanzate</string>
@@ -490,8 +505,12 @@
<string name="devtools__show_heap_memory_stats__summary" comment="Summary of Show heap memory stats in Devtools">Mostra l\'utilizzo e la dimensione massima della memoria heap nell\'angolo in alto a destra</string>
<string name="devtools__show_primary_clip__label" comment="Label of Show primary clip in Devtools">Mostra clip principale</string>
<string name="devtools__show_primary_clip__summary" comment="Summary of Show primary clip in Devtools">Sovrappone la clip principale corrente degli appunti</string>
<string name="devtools__show_input_state_overlay__label" comment="Label of Show input cache overlay in Devtools">Mostra overlay dello stato di input</string>
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Sovrappone lo stato di input corrente per il debug</string>
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Mostra overlay ortografico</string>
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Sovrappone gli attuali risultati di ortografia per il debug</string>
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Mostra i limiti del tocco tasto</string>
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Delinea i limiti della pressione del tasto in rosso</string>
<string name="devtools__clear_udm_internal_database__label" comment="Label of Clear internal user dictionary database in Devtools">Cancella il dizionario utente interno</string>
<string name="devtools__clear_udm_internal_database__summary" comment="Summary of Clear internal user dictionary database in Devtools">Cancella tutte le parole dal dizionario</string>
<string name="devtools__reset_flag__label" comment="Label of Reset flag preferences in Devtools">Reset impostazione \"{flag_name}\"</string>
@@ -639,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Il carattere iniziale selezionato dopo la pressione prolungata è l\'accento principale, o il suggerimento se non c\'è un accento principale</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Prioritizzazione intelligente</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Il carattere iniziale selezionato dopo una pressione prolungata viene deciso dinamicamente tra l\'accento principale o il suggerimento, in base alla lingua e al layout selezionati</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normale</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift Lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Bloc maiusc</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Maiusc disattivato</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Maiusc attivato (manuale)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Maiusc attivato (automatico)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Bloc maiusc</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Non visualizzare mai</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Mostra sempre</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Mostra dinamicamente</string>
@@ -673,7 +693,9 @@
<string name="enum__swipe_action__no_action" comment="Enum value label">Nessuna azione</string>
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Passa alla modalità precedente</string>
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Passa alla modalità successiva</string>
<string name="enum__swipe_action__delete_character" comment="Enum value label">Elimina il carattere prima del cursore</string>
<string name="enum__swipe_action__delete_characters_precisely" comment="Enum value label">Cancella lettere con precisione</string>
<string name="enum__swipe_action__delete_word" comment="Enum value label">Cancella la parola prima del cursore</string>
<string name="enum__swipe_action__delete_words_precisely" comment="Enum value label">Cancella parole con precisione</string>
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">Nascondi tastiera</string>
<string name="enum__swipe_action__insert_space" comment="Enum value label">Inserisci spazio</string>
@@ -695,6 +717,7 @@
<string name="enum__swipe_action__switch_to_prev_keyboard" comment="Enum value label">Passa alla tastiera precedente</string>
<string name="enum__swipe_action__switch_to_prev_subtype" comment="Enum value label">Vai allo stile di input precedente</string>
<string name="enum__swipe_action__switch_to_next_subtype" comment="Enum value label">Vai allo stile di input successivo</string>
<string name="enum__swipe_action__toggle_smartbar_visibility" comment="Enum value label">Attiva/Disattiva visibilità della Smartbar</string>
<string name="enum__theme_mode__always_day" comment="Enum value label">Sempre chiaro</string>
<string name="enum__theme_mode__always_night" comment="Enum value label">Sempre scuro</string>
<string name="enum__theme_mode__follow_system" comment="Enum value label">Come da Sistema</string>

View File

@@ -174,7 +174,6 @@
<!-- State strings -->
<string name="state__disabled">כבוי</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">רגיל</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">לעולם אל תציג</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">הצג תמיד</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">להציג באופן דינמי</string>

View File

@@ -104,7 +104,6 @@
<string name="settings__theme_editor__rule_already_exists">このスタイルシートルールはすでに定義されています。</string>
<string name="settings__theme_editor__rule_element">ターゲット要素</string>
<string name="settings__theme_editor__rule_groups">グループ</string>
<string name="settings__theme_editor__rule_modes">モード</string>
<string name="settings__theme_editor__rule_selectors">セレクター</string>
<string name="settings__theme_editor__code_already_exists">このキーコードはすでに定義されています。</string>
<string name="settings__theme_editor__code_invalid">このキーコードは無効です。 キーコードが、文字の場合は {c_min} から {c_max} の範囲内、内部特殊キーの場合は {i_min} から {i_max} の範囲内にあることを確認してください。</string>

View File

@@ -101,7 +101,6 @@
<string name="settings__theme_editor__add_rule">Rêbazekê tevlî bike</string>
<string name="settings__theme_editor__edit_rule">Rêbazê serrast bike</string>
<string name="settings__theme_editor__rule_groups">Kom</string>
<string name="settings__theme_editor__rule_modes">Moda</string>
<string name="settings__theme_editor__rule_selectors">Hilbijêr</string>
<string name="settings__theme_editor__code_already_exists">Ev koda mifteyan jixwe hatîye pênasekirin.</string>
<string name="snygg__rule_element__key">Kilîd</string>
@@ -395,9 +394,6 @@
<string name="enum__display_colors_as__rgba" comment="Enum value label">Alfaya Kesk a Sor</string>
<string name="enum__display_kbd_after_dialogs__always" comment="Enum value label">Herdem nîşan bide</string>
<string name="enum__display_kbd_after_dialogs__never" comment="Enum value label">Qet nîşan nede</string>
<string name="enum__input_mode__normal" comment="Enum value label">Asayî</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Tu carî nîşan nede</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Herdem nîşan bide</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dînamîk nîşan bide</string>

View File

@@ -103,11 +103,22 @@
<string name="settings__theme_editor__no_rules_defined">Šī stila lapa nesatur nosacījumus. Lai to pielāgotu, jāpievieno kāds nosacījums.</string>
<string name="settings__theme_editor__rule_already_exists">Šis stila lapas nosacījums jau ir noteikts.</string>
<string name="settings__theme_editor__rule_element">Mērķa elements</string>
<string name="settings__theme_editor__rule_codes">Mērķa taustiņu kodi</string>
<string name="settings__theme_editor__rule_groups">Kopas</string>
<string name="settings__theme_editor__rule_modes">Veidi</string>
<string name="settings__theme_editor__rule_shift_states">Pārslēgšanās taustiņa stāvokļi</string>
<string name="settings__theme_editor__rule_selectors">Atlasītāji</string>
<string name="settings__theme_editor__add_code">Pievienot taustiņa kodu</string>
<string name="settings__theme_editor__edit_code">Labot taustiņa kodu</string>
<string name="settings__theme_editor__no_codes_defined">Pielietot nosacījumu visām mērķa daļām.</string>
<string name="settings__theme_editor__codes_defined">Pielietot nosacījumu tikai mērķa daļām ar šiem taustiņu kodiem:</string>
<string name="settings__theme_editor__code_already_exists">Šis taustiņa kods jau ir noteikts.</string>
<string name="settings__theme_editor__code_invalid">Šis taustiņa kods ir nederīgs. Jānodrošina, ka taustiņa kods ir starp {c_min} un {c_max} rakstzīmēm vai starp {i_min} un {i_max} iekšējiem īpašajiem taustiņiem.</string>
<string name="settings__theme_editor__code_help_text">Vai arī šīs saites palīdzēs atrast atbilstošo taustiņa kodu:</string>
<string name="settings__theme_editor__code_placeholder">Kods</string>
<string name="settings__theme_editor__code_recording_help_text">Lai atrastu taustiņa kodu, jāizmanto poga blakus koda ievades laukam. Tiklīdz tā ir piespiesta, tā tiks ierakstīts nākamais taustiņa piesitiens, un kods tiks ievietots ievades laukā.</string>
<string name="settings__theme_editor__code_recording_started">Taustiņa koda ierakstīšana ir sākusies</string>
<string name="settings__theme_editor__code_recording_stopped">Taustiņa koda ierakstīšana ir apturēta</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} ir jābūt noklusējuma tastatūrai, lai varētu ierakstīt taustiņa kodu</string>
<string name="settings__theme_editor__code_recording_placeholder">Ieraksta…</string>
<string name="settings__theme_editor__add_property">Pievienot īpašību</string>
<string name="settings__theme_editor__edit_property">Labot īpašību</string>
@@ -498,6 +509,8 @@
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Pārklāj pašreizējo ievades stāvokli atkļūdošanai</string>
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Rādīt pareizrakstības pārklājumu</string>
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Pārklāj pašreizējo pareizrakstības iznākumu atkļūdošanai</string>
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Rādīt taustiņa pieskārienu robežas</string>
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Izcelt taustiņa pieskārienu robežas sarkanā krāsā</string>
<string name="devtools__clear_udm_internal_database__label" comment="Label of Clear internal user dictionary database in Devtools">Iztīrīt iekšējās lietotāja vārdnīcas datubāzi</string>
<string name="devtools__clear_udm_internal_database__summary" comment="Summary of Clear internal user dictionary database in Devtools">Tiek izņemti visi vārdi no vārdnīcas datubāzes tabulas</string>
<string name="devtools__reset_flag__label" comment="Label of Reset flag preferences in Devtools">Atiestatīt iezīmi \"{flag_name}\"</string>
@@ -645,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Sākotnējā atlasītā rakstzīme pēc ilga piespiediena vienmēr ir norādes zīme vai galvenā uzsvara zīme, ja norādes zīme nav pieejama</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Viedā priekšrokas noteikšana</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Sākotnējā atlasītā rakstzīme (galvenā uzsvara vai norādes zīme) pēc ilga piespiediena tiek noteikta atbilstoši pašreizējai valodai un izkārtojumam</string>
<string name="enum__input_mode__normal" comment="Enum value label">Vispārpieņemts</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Pārslēgs</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Burtslēgs</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Nepārslēgts</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Pārslēgts (pašrocīgi)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Pārslēgts (automātiski)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Burtslēgs</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nerādīt nekad</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Rādīt vienmēr</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Rādīt pielāgojoties</string>
@@ -703,6 +717,7 @@
<string name="enum__swipe_action__switch_to_prev_keyboard" comment="Enum value label">Pārslēgties uz iepriekšējo tastatūru</string>
<string name="enum__swipe_action__switch_to_prev_subtype" comment="Enum value label">Pārslēgties uz iepriekšējo apakšveidu</string>
<string name="enum__swipe_action__switch_to_next_subtype" comment="Enum value label">Pārslēgties uz nākamo apakšveidu</string>
<string name="enum__swipe_action__toggle_smartbar_visibility" comment="Enum value label">Pārslēgt viedjoslas redzamību</string>
<string name="enum__theme_mode__always_day" comment="Enum value label">Vienmēr diena</string>
<string name="enum__theme_mode__always_night" comment="Enum value label">Vienmēr nakts</string>
<string name="enum__theme_mode__follow_system" comment="Enum value label">Pielāgot sistēmai</string>

View File

@@ -105,7 +105,6 @@
<string name="settings__theme_editor__rule_element">Doelwit element</string>
<string name="settings__theme_editor__rule_codes">Doelwit sleutelcode</string>
<string name="settings__theme_editor__rule_groups">Groepen</string>
<string name="settings__theme_editor__rule_modes">Modussen</string>
<string name="settings__theme_editor__rule_selectors">Selectors</string>
<string name="settings__theme_editor__add_code">Sleutelcode toevoegen</string>
<string name="settings__theme_editor__edit_code">Sleutelcode bewerken</string>
@@ -360,7 +359,6 @@
<string name="enum__key_hint_mode__hint_priority" comment="Enum value label">Hint krijgt prioriteit</string>
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Het eerste geselecteerde teken na een lange indruk is altijd het hint symbool, of het primaire accent als er geen hint symbool beschikbaar is</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Slimme prioriteiten</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normaal</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nooit tonen</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Altijd tonen</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dynamisch tonen</string>

View File

@@ -350,7 +350,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Det første tegnet som velges etter langt trykk er alltid tipssymbolet eller den primære aksenten hvis det ikke finnes noe tipssymbol</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Smart prioritisering</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Det første tegnet valgt etter et langt trykk bestemmes dynamisk for å være enten den primære aksenten eller tipssymbolet, basert på gjeldende språk og oppsett</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Vis aldri</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Vis alltid</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dynamisk visning</string>

View File

@@ -104,7 +104,6 @@
<string name="settings__theme_editor__rule_already_exists">Ta reguła arkusza stylów jest już zdefiniowana.</string>
<string name="settings__theme_editor__rule_element">Element docelowy</string>
<string name="settings__theme_editor__rule_groups">Grupy</string>
<string name="settings__theme_editor__rule_modes">Tryby</string>
<string name="settings__theme_editor__rule_selectors">Selektory</string>
<string name="settings__theme_editor__code_already_exists">Ten kod klucza jest już zdefiniowany.</string>
<string name="settings__theme_editor__code_invalid">Ten kod jest nieprawidłowy. Upewnij się, że kod mieści się w zakresie od {c_min} do {c_max} dla znaków lub od {i_min} do {i_max} dla wewnętrznych kluczy specjalnych.</string>
@@ -630,9 +629,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Początkowy znak wybrany po długim naciśnięciu jest zawsze symbolem podpowiedzi lub głównym akcentem, jeśli nie jest dostępny symbol podpowiedzi</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Inteligentna priorytetyzacja</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Początkowy znak wybrany po długim naciśnięciu jest dynamicznie określany jako główny akcent lub symbol podpowiedzi, w oparciu o bieżący język i układ</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normalny</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Trzymanie shift</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nigdy nie pokazuj</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Zawsze pokazuj</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Pokazuj dynamicznie</string>

View File

@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">Elemento de destino</string>
<string name="settings__theme_editor__rule_codes">Códigos de teclas de destino</string>
<string name="settings__theme_editor__rule_groups">Grupos</string>
<string name="settings__theme_editor__rule_modes">Modos</string>
<string name="settings__theme_editor__rule_shift_states">Alterar estados</string>
<string name="settings__theme_editor__rule_selectors">Seletores</string>
<string name="settings__theme_editor__add_code">Adicionar código de tecla</string>
<string name="settings__theme_editor__edit_code">Editar código de tecla</string>
@@ -658,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">O caractere inicial selecionado após um toque longo é sempre a sugestão de símbolo ou o acento principal se nenhuma sugestão de símbolo estiver disponível</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Priorização inteligente</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">O caractere inicial selecionado após um toque longo é decidido dinamicamente como o acento principal ou a sugestão de símbolo, com base no idioma e layout atuais</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Inalterado</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Alterado (manual)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Alterado (automático)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Nunca mostrar</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Sempre mostrar</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Mostrar dinamicamente</string>

View File

@@ -103,7 +103,6 @@
<string name="settings__theme_editor__no_rules_defined">Esta folha de estilo não tem regras definidas. Adicione uma regra para começar a editar.</string>
<string name="settings__theme_editor__rule_element">Elemento destino</string>
<string name="settings__theme_editor__rule_groups">Grupos</string>
<string name="settings__theme_editor__rule_modes">Modos</string>
<string name="settings__theme_editor__rule_selectors">Seletores</string>
<string name="settings__theme_editor__code_already_exists">Este código já está definido.</string>
<string name="settings__theme_editor__code_placeholder">Código</string>
@@ -564,9 +563,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">O carácter inicialmente selecionado após um toque longo será sempre o símbolo ou o acento principal caso não exista um símbolo</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Prioridade inteligente</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">O carácter inicialmente selecionado após um toque longo é decidido por ser o acento principal ou o símbolo, tendo por base o idioma e a disposição atual</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift Lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps Lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Não mostrar</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Mostrar sempre</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Mostrar dinamicamente</string>

View File

@@ -35,7 +35,7 @@
<string name="smartbar__quick_action__one_handed_mode" comment="Content-description for the one-handed quick action in Smartbar">Переключить режим одной руки.</string>
<string name="smartbar__quick_action__open_settings" comment="Content-description for the settings quick action in Smartbar">Открыть настройки.</string>
<string name="smartbar__quick_action__switch_to_editing_context" comment="Content-description for the editing quick action in Smartbar">Перейти на панель редактирования текста.</string>
<string name="smartbar__quick_action__switch_to_media_context" comment="Content-description for the media quick action in Smartbar">Переключиться на режим смайлов.</string>
<string name="smartbar__quick_action__switch_to_media_context" comment="Content-description for the media quick action in Smartbar">Переключение в режим просмотра ввода информации.</string>
<string name="smartbar__quick_action__undo" comment="Content-description for the undo quick action in Smartbar">Кнопка \"Отменить\" для отмены последнего действия</string>
<string name="smartbar__quick_action__redo" comment="Content-description for the redo quick action in Smartbar">Кнопка \"Повтор\" для отмены последнего \"Отменить\"</string>
<string name="smartbar__quick_action__private_mode" comment="Content-description for the private mode button in Smartbar">Если видна, значит активен режим Инкогнито. При нажатии доступна информация об этом режиме.</string>
@@ -44,7 +44,7 @@
<string name="settings__preview_keyboard" comment="Hint for try your setup box">Протестируйте свои настройки</string>
<string name="settings__help" comment="General label for help buttons in Settings">Помощь</string>
<string name="settings__default" comment="General string which is used when a preference has the default value set">По умолчанию</string>
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">По умолчанию</string>
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">Как в системе</string>
<string name="settings__home__title" comment="Title of the Home screen">Приветствуем в {app_name}</string>
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBoard не включена в системе и поэтому не будет доступна в качестве метода ввода. Нажмите здесь, чтобы исправить эту проблему.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">FlorisBoard не выбрана в качестве метода ввода по умолчанию. Нажмите здесь, чтобы исправить эту проблему.</string>
@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">Целевой элемент</string>
<string name="settings__theme_editor__rule_codes">Целевые коды клавиш</string>
<string name="settings__theme_editor__rule_groups">Группы</string>
<string name="settings__theme_editor__rule_modes">Режимы</string>
<string name="settings__theme_editor__rule_shift_states">Сдвиг</string>
<string name="settings__theme_editor__rule_selectors">Переключатели</string>
<string name="settings__theme_editor__add_code">Добавить код клавиши</string>
<string name="settings__theme_editor__edit_code">Изменить код клавиши</string>
@@ -338,7 +338,7 @@
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Порог срабатывания жеста</string>
<string name="settings__advanced__title" comment="Title of Advanced settings">Дополнительные</string>
<string name="pref__advanced__settings_theme__label" comment="Label of Settings theme preference in Advanced">Настройки темы</string>
<string name="pref__advanced__settings_theme__auto_amoled" comment="Possible value of Settings theme preference in Advanced">По умолчанию (AMOLED)</string>
<string name="pref__advanced__settings_theme__auto_amoled" comment="Possible value of Settings theme preference in Advanced">Как в системе (AMOLED)</string>
<string name="pref__advanced__settings_theme__light" comment="Possible value of Settings theme preference in Advanced">Светлая</string>
<string name="pref__advanced__settings_theme__dark" comment="Possible value of Settings theme preference in Advanced">Тёмная</string>
<string name="pref__advanced__settings_theme__amoled_dark" comment="Possible value of Settings theme preference in Advanced">Тёмная AMOLED</string>
@@ -658,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Начальный символ, выбираемый долгим нажатием, всегда является символом подсказки или основным акцентом, если символ подсказки недоступен</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Умный приоритет</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Начальный символ, выбираемый долгим нажатием, динамически определяется как основной акцент или символ подсказки, основываясь на текущем языке и раскладке</string>
<string name="enum__input_mode__normal" comment="Enum value label">Нормальный</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Без сдвига</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Сдвиг (вручную)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Сдвиг (авто)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Caps Lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Не показывать</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Всегда показывать</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Автоматически</string>

View File

@@ -107,7 +107,6 @@
<!-- State strings -->
<string name="state__disabled">Inaktiverad</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__one_handed_mode__off" comment="Enum value label">Av</string>
<string name="enum__one_handed_mode__start" comment="Enum value label">Vänsterhandsläge</string>
<string name="enum__one_handed_mode__end" comment="Enum value label">Högerhandsläge</string>

View File

@@ -48,6 +48,7 @@
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBoard sisteminizde etkinleştirilmediği için giriş seçeneklerinde görüntülenemez. Bu durumu düzeltmek için tıklayın.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">FlorisBoard varsayılan giriş seçeneği değil. Düzeltmek için tıklayın.</string>
<string name="settings__localization__title" comment="Title of languages and Layout screen">Diller &amp; Düzenler</string>
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Dil isimlerini şu şekilde göster</string>
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">Alt Türler</string>
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">Dil/dizilim ekle</string>
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">Dil/dizilim düzenle</string>
@@ -92,10 +93,10 @@
<string name="settings__theme_manager__title_day" comment="Title of the theme manager screen for day theme selection">ık temayı seç</string>
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">Koyu temayı seç</string>
<string name="settings__theme_manager__title_manage" comment="Title of the theme manager screen for managing installed and custom themes">İndirilen temaları düzenle</string>
<string name="settings__theme_editor__fine_tune__display_colors_as">Renkleri şu şekilde göster</string>
<string name="settings__theme_editor__add_rule">Kural ekle</string>
<string name="settings__theme_editor__edit_rule">Kuralı düzenle</string>
<string name="settings__theme_editor__rule_groups">Gruplar</string>
<string name="settings__theme_editor__rule_modes">Modlar</string>
<string name="settings__theme_editor__code_placeholder">Kod</string>
<string name="settings__theme_editor__code_recording_placeholder">Kaydediliyor…</string>
<string name="settings__theme_editor__add_property">Özellik Ekle</string>
@@ -272,7 +273,7 @@
<string name="about__repository__title" comment="Preference title">Veri havuzu (GitHub)</string>
<string name="about__repository__summary" comment="Preference summary">Kaynak kodu, tartışmalar, sorunlar ve bilgi</string>
<string name="about__privacy_policy__title" comment="Preference title">Gizlilik politikası</string>
<string name="about__privacy_policy__summary" comment="Preference summary">Bu projein gizlilik politikası</string>
<string name="about__privacy_policy__summary" comment="Preference summary">Bu projenin gizlilik politikası</string>
<string name="about__project_license__title" comment="Preference title">Proje lisansı</string>
<string name="about__project_license__summary" comment="Preference summary">FlorisBoard {license_name} altında lisanslıdır</string>
<string name="about__project_license__error_license_text_failed" comment="Error text for license text loading failure">Hata. Lisans bilgisi yüklenemedi. \n-&gt; Sebep: {error_message}</string>
@@ -285,13 +286,14 @@
<string name="setup__footer__privacy_policy" comment="Privacy policy label for URL">Gizlilik politikası</string>
<string name="setup__footer__repository" comment="Repository label for URL">Depo</string>
<string name="setup__enable_ime__title">Etkinleştir {app_name}</string>
<string name="setup__enable_ime__description">Android, kullanabilmeniz için her özel klavyenin ayrı ayrı etkinleştirilmesini gerektirir. Sistem <i>Language &amp; Input</i> Ayarları, orada \"{app_name}\" etkinleştirin.</string>
<string name="setup__enable_ime__open_settings_btn">Sistem ayarlarını açın</string>
<string name="setup__select_ime__title">Seç {app_name}</string>
<string name="setup__select_ime__description">{app_name} sistemde etkin. Aktif olarak kullanmak için, giriş seçici iletişim kutusunda seçerek {app_name} uygulamasına geçin!</string>
<string name="setup__enable_ime__description">Android, her özel klavyenin siz kullanmadan önce ayrı ayrı etkinleştirilmesini gerektirir. Sistem <i>Diller &amp; Giriş</i> ayarınıın, oradan \"{app_name}\"\'u etkinleştirin.</string>
<string name="setup__enable_ime__open_settings_btn">Sistem Ayarlarını Açın</string>
<string name="setup__select_ime__title">{app_name}\'u Seç</string>
<string name="setup__select_ime__description">{app_name} sisteminizde etkinleştirildi. Aktif olarak kullanmak için, giriş seçme penceresinde seçerek {app_name}\'a geçin!</string>
<string name="setup__select_ime__switch_keyboard_btn">Klavyeyi Değiştir</string>
<string name="setup__finish_up__title">Tamamen Bitir</string>
<string name="setup__finish_up__title">Bitir</string>
<string name="setup__finish_up__description_p1">{app_name} sistemde etkin ve özelleştirilmeye hazır.</string>
<string name="setup__finish_up__description_p2">Herhangi bir sorunla, hatayla, çökmeyle karşılaşırsanız veya sadece bir öneride bulunmak isterseniz, hakkında ekranından proje deposuna göz atın!</string>
<string name="setup__finish_up__finish_btn">Özelleştirmeye Başla</string>
<!-- Back up & Restore -->
<!-- Crash Dialog strings -->
@@ -394,9 +396,6 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Uzun basıştan sonra seçilen ilk karakter her zaman ipucu sembolüdür veya ipucu sembolü yoksa birincil vurgudur</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Akıllı önceliklendirme</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Uzun basıştan sonra seçilen ilk karakterin, geçerli dil ve düzene bağlı olarak ya birincil vurgu ya da ipucu sembolü olduğuna dinamik olarak karar verilir</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Üst karakter kilidi</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Büyük harf kilidi</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Asla gösterme</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Her zaman göster</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Dinamik olarak göster</string>

View File

@@ -180,7 +180,6 @@
<!-- State strings -->
<string name="state__disabled">Вимкнено</string>
<!-- Enum label and description strings -->
<string name="enum__input_mode__normal" comment="Enum value label">Нормальний</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Ніколи не відображати</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Завжди відображати</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">Динамічно показувати</string>

View File

@@ -105,7 +105,7 @@
<string name="settings__theme_editor__rule_element">目标元件</string>
<string name="settings__theme_editor__rule_codes">目标键码</string>
<string name="settings__theme_editor__rule_groups">规则组</string>
<string name="settings__theme_editor__rule_modes">模式</string>
<string name="settings__theme_editor__rule_shift_states">Shift 键状态</string>
<string name="settings__theme_editor__rule_selectors">选择器</string>
<string name="settings__theme_editor__add_code">添加键码</string>
<string name="settings__theme_editor__edit_code">编辑键码</string>
@@ -658,9 +658,10 @@
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">长按后自动选择的字符始终是提示符号,如果没有提示符号可用,则为主重音</string>
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">智能优先</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">长按后自动选择的字符根据当前语言和布局,动态决定是主重音还是提示符号</string>
<string name="enum__input_mode__normal" comment="Enum value label">正常</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift 锁定</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">大写锁定</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">未 Shift</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Shift(手动)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">已 Shift自动</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">大写锁定</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">从不显示</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">始终显示</string>
<string name="enum__landscape_input_ui_mode__dynamically_show" comment="Enum value label">动态显示</string>

View File

@@ -115,7 +115,7 @@
<string name="settings__theme_editor__rule_element">Target element</string>
<string name="settings__theme_editor__rule_codes">Target key codes</string>
<string name="settings__theme_editor__rule_groups">Groups</string>
<string name="settings__theme_editor__rule_modes">Modes</string>
<string name="settings__theme_editor__rule_shift_states">Shift states</string>
<string name="settings__theme_editor__rule_selectors">Selectors</string>
<string name="settings__theme_editor__add_code">Add key code</string>
<string name="settings__theme_editor__edit_code">Edit key code</string>
@@ -718,9 +718,10 @@
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Smart prioritization</string>
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">The initial character selected after long press is dynamically decided to be either the primary accent or the hint symbol, based on the current language and layout</string>
<string name="enum__input_mode__normal" comment="Enum value label">Normal</string>
<string name="enum__input_mode__shift_lock" comment="Enum value label">Shift lock</string>
<string name="enum__input_mode__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__input_shift_state__unshifted" comment="Enum value label">Unshifted</string>
<string name="enum__input_shift_state__shifted_manual" comment="Enum value label">Shifted (manual)</string>
<string name="enum__input_shift_state__shifted_automatic" comment="Enum value label">Shifted (automatic)</string>
<string name="enum__input_shift_state__caps_lock" comment="Enum value label">Caps lock</string>
<string name="enum__landscape_input_ui_mode__never_show" comment="Enum value label">Never show</string>
<string name="enum__landscape_input_ui_mode__always_show" comment="Enum value label">Always show</string>

View File

@@ -98,12 +98,12 @@ class SnyggRuleTest : FunSpec({
SnyggRule(element = "test", groups = listOf(11)),
),
row(
SnyggRule(element = "test", modes = listOf(0)),
SnyggRule(element = "test", modes = listOf(1)),
SnyggRule(element = "test", shiftStates = listOf(0)),
SnyggRule(element = "test", shiftStates = listOf(1)),
),
row(
SnyggRule(element = "test", codes = listOf(32), modes = listOf(0)),
SnyggRule(element = "test", codes = listOf(32), modes = listOf(1)),
SnyggRule(element = "test", codes = listOf(32), shiftStates = listOf(0)),
SnyggRule(element = "test", codes = listOf(32), shiftStates = listOf(1)),
),
row(
SnyggRule(element = "test", pressedSelector = false),
@@ -196,34 +196,34 @@ class SnyggRuleTest : FunSpec({
false,
),
row(
SnyggRule(element = "test", modes = listOf(0, 1, 2)),
SnyggRule(element = "test", modes = listOf(0, 1, 2)),
SnyggRule(element = "test", shiftStates = listOf(0, 1, 2)),
SnyggRule(element = "test", shiftStates = listOf(0, 1, 2)),
true,
),
row(
SnyggRule(element = "test", modes = listOf()),
SnyggRule(element = "test", modes = listOf(0)),
SnyggRule(element = "test", shiftStates = listOf()),
SnyggRule(element = "test", shiftStates = listOf(0)),
false,
),
row(
SnyggRule(element = "test", modes = listOf(0, 1, 2)),
SnyggRule(element = "test", modes = listOf(0, 1, 2)),
SnyggRule(element = "test", shiftStates = listOf(0, 1, 2)),
SnyggRule(element = "test", shiftStates = listOf(0, 1, 2)),
true,
),
row(
SnyggRule(element = "test", modes = listOf()),
SnyggRule(element = "test", modes = listOf(2)),
SnyggRule(element = "test", shiftStates = listOf()),
SnyggRule(element = "test", shiftStates = listOf(2)),
false,
),
row(
SnyggRule(element = "test", modes = listOf(1)),
SnyggRule(element = "test", modes = listOf(2)),
SnyggRule(element = "test", shiftStates = listOf(1)),
SnyggRule(element = "test", shiftStates = listOf(2)),
false,
),
row(
SnyggRule(element = "system-nav-bar", modes = listOf(2)),
SnyggRule(element = "system-nav-bar", modes = listOf(1, 2)),
SnyggRule(element = "system-nav-bar", shiftStates = listOf(2)),
SnyggRule(element = "system-nav-bar", shiftStates = listOf(1, 2)),
false,
),
row(
@@ -234,7 +234,7 @@ class SnyggRuleTest : FunSpec({
row(
SnyggRule(element = "test", codes = listOf(10)),
SnyggRule(element = "test", modes = listOf(2)),
SnyggRule(element = "test", shiftStates = listOf(2)),
false,
),
)

View File

@@ -4,9 +4,10 @@ plugins {
buildscript {
dependencies {
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.android.tools.build:gradle:7.2.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
classpath("org.jetbrains.kotlin:kotlin-serialization:1.6.10")
classpath("com.google.devtools.ksp:symbol-processing-gradle-plugin:1.6.10-1.0.4")
classpath("com.google.android.gms:oss-licenses-plugin:0.10.5")
classpath("de.mannodermaus.gradle.plugins:android-junit5:1.8.2.0")
}

Some files were not shown because too many files have changed in this diff Show More